URL : https://thenewcircle.com/s/post/1340/Deep_Dive_Into_Binder_Presentation.htm#slide-15
.....
Sharing Memory via Binder
- Binder transactional data is copied among parties communicating - not ideal if we have a lot of data to send
+ In fact, binder imposes limits on how much data we can send via transactions
- If the data we want to share comes from a file, then we should just send the file descriptor instead
+ This is how we ask the media player to play an audio/video file for us - we just send it the FD
- If the data we want to send is located in memory, rather than trying to send all of it at once, we could send multiple but smaller chunks instead
+ Complicates our design
- Alternatively, we could take advantage of Android’s ashmem (Anonymous Shared Memory) facilities
+ Its Java wrapper android.os.MemoryFile is not meant for memory sharing from 3rd party apps
+ Drop to native (via JNI) and use ashmem directly?
- Native memory sharing implemented via frameworks/base/libs/binder/Parcel.cpp's:
+ void Parcel::writeBlob(size_t len, WritableBlob* outBlob)
+ status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob)
- This is roughly implemented as follows:
Client
size_t len = 4096; int fd = ashmem_create_region("Parcel Blob", len); ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); void* ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ashmem_set_prot_region(fd, PROT_READ); writeFileDescriptor(fd, true); // write into ptr for len as desired … munmap(ptr, len); close(fd);
Service
|
int fd = readFileDescriptor(); void* ptr = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); // read from ptr up to len as desired … munmap(ptr, len);
# Removed error handling for brevity. Also, writeFileDescriptor(…) and readFileDescriptor(…) are provided by libbinder.
… static int open_driver() { int fd = open("/dev/binder", O_RDWR); if (fd >= 0) { … size_t maxThreads = 15; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); … } else { … } return fd; } …
Binder - Security
- Binder does directly deal with "security" concerns, but it enables a "trusted" execution environment and DAC
- The binder driver allows only a single CONTEXT_MGR (i.e. servicemanager) to register:
drivers/staging/android/binder.c:
… static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { … switch (cmd) { … case BINDER_SET_CONTEXT_MGR: if (binder_context_mgr_node != NULL) { printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n"); ret = -EBUSY; goto err; } … binder_context_mgr_node = binder_new_node(proc, NULL, NULL); … } … …
- The servicemanager in turn only allows registrations from trusted UIDs (like system, radio, media, etc.):
frameworks/base/cmds/servicemanager/service_manager.c:
… static struct { unsigned uid; const char *name; } allowed[] = { #ifdef LVMX { AID_MEDIA, "com.lifevibes.mx.ipc" }, #endif { AID_MEDIA, "media.audio_flinger" }, { AID_MEDIA, "media.player" }, { AID_MEDIA, "media.camera" }, { AID_MEDIA, "media.audio_policy" }, { AID_DRM, "drm.drmManager" }, { AID_NFC, "nfc" }, { AID_RADIO, "radio.phone" }, { AID_RADIO, "radio.sms" }, { AID_RADIO, "radio.phonesubinfo" }, { AID_RADIO, "radio.simphonebook" }, /* TODO: remove after phone services are updated: */ { AID_RADIO, "phone" }, { AID_RADIO, "sip" }, { AID_RADIO, "isms" }, { AID_RADIO, "iphonesubinfo" }, { AID_RADIO, "simphonebook" }, }; … int svc_can_register(unsigned uid, uint16_t *name) { unsigned n; if ((uid == 0) || (uid == AID_SYSTEM)) return 1; for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++) if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name)) return 1; return 0; } … int do_add_service(struct binder_state *bs, uint16_t *s, unsigned len, void *ptr, unsigned uid) { … if (!svc_can_register(uid, s)) { LOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n", str8(s), ptr, uid); return -1; } … } …
- Each binder transaction caries in it the UID and PID of the sender, which we can easily access:
+ android.os.Binder.getCallingPid()
+ android.os.Binder.getCallingUid()
- Once we have the knowledge of the calling UID, we can easily resolve it the calling app via PackageManager.getPackagesForUid(int uid)
- Once we have the knowledge of the calling app, we can easily check whether it holds a permission we want to enforce via PackageManager.getPackageInfo(String packageName, int flags) (with the PackageManager.GET_PERMISSIONS flag)
- But, much easier to do permission enforcement via:
+ Context.checkCallingOrSelfPermission(String permission), which returns PackageManager.PERMISSION_GRANTED if the calling process has been granted the permission or PackageManager.PERMISSION_DENIED otherwise
+ Context.enforceCallingPermission(String permission, String message) - to automatically throw SecurityException if the caller does not have the requested permission
- This is how many of the application framework services enforce their permissions
- For example:
frameworks/base/services/java/com/android/server/VibratorService.java:
package com.android.server; … public class VibratorService extends IVibratorService.Stub { … public void vibrate(long milliseconds, IBinder token) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); } … } … }
frameworks/base/services/java/com/android/server/LocationManagerService.java:
package com.android.server; … public class LocationManagerService extends ILocationManager.Stub implements Runnable { … private static final String ACCESS_FINE_LOCATION = android.Manifest.permission.ACCESS_FINE_LOCATION; private static final String ACCESS_COARSE_LOCATION = android.Manifest.permission.ACCESS_COARSE_LOCATION; … private void checkPermissionsSafe(String provider) { if ((LocationManager.GPS_PROVIDER.equals(provider) || LocationManager.PASSIVE_PROVIDER.equals(provider)) && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Provider " + provider + " requires ACCESS_FINE_LOCATION permission"); } if (LocationManager.NETWORK_PROVIDER.equals(provider) && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Provider " + provider + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); } } … private Location _getLastKnownLocationLocked(String provider) { checkPermissionsSafe(provider); … } … public Location getLastKnownLocation(String provider) { … _getLastKnownLocationLocked(provider); … } }
Permissions by Example
- In this example, we are given two applications, FibonacciClient and FibonacciService
+ These two apps communicate via Binder/IPC
+ Common files for these applications reside in a library project called FibonacciCommon
- The finished code for these applications (as Eclipse projects) is available:
+ As a ZIP archive: https://github.com/marakana/FibonacciBinderDemo/zipball/secured
+ By Git: git clone https://github.com/marakana/FibonacciBinderDemo.git -b secured
Static Permission Enforcement
- Here, we want to restrict access to the com.marakana.android.fibonacciservice.FibonacciService to applications (i.e. clients) that hold USE_FIBONACCI_SERVICE custom permission
1. We start by by creating a custom permission group (making sure that we name-space it):
FibonacciService/res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> … <string name="fibonacci_permissions_group_label">Fibonacci Permissions</string> … </resources>
FibonacciService/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest …> … <permission-group android:name="com.marakana.android.fibonacciservice.FIBONACCI_PERMISSIONS" android:label="@string/fibonacci_permissions_group_label" /> … </manifest>
# This permission group is optional - as we could instead use one of the already provided groups
2. Next, we create a custom permission (again, making sure that we name-space it), while taking advantage of our newly-created permission group:
FibonacciService/res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> … <string name="use_fibonacci_service_permission_label">use fibonacci service</string> <string name="use_fibonacci_service_permission_description"> applications with this permissions get fibonacci results for free </string> … </resources>
FibonacciService/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest …> … <permission-group …/> <permission android:name="com.marakana.android.fibonacciservice.USE_FIBONACCI_SERVICE" android:description="@string/use_fibonacci_service_permission_description" android:label="@string/use_fibonacci_service_permission_label" android:permissionGroup="com.marakana.android.fibonacciservice.FIBONACCI_PERMISSIONS" android:protectionLevel="dangerous" /> … </manifest>
3. Now we can statically require the permission on our FibonacciService service:
FibonacciService/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest …> … <permission-group …/> <permission …/> <application …> <service android:name=".FibonacciService" android:permission="com.marakana.android.fibonacciservice.USE_FIBONACCI_SERVICE" > … </service> </application> … </manifest>
4. If we now re-run the FibonacciService and re-run the FibonacciClient, we will notice that the client will fail to launch and adb logcat will show something like:
… W/ActivityManager( 85): Permission Denial: Accessing service ComponentInfo{com.marakana.android.fibonacciservice/com.marakana.android.fibonacciservice.FibonacciService} from pid=540, uid=10043 requires com.marakana.android.fibonacciservice.USE_FIBONACCI_SERVICE D/AndroidRuntime( 540): Shutting down VM W/dalvikvm( 540): threadid=1: thread exiting with uncaught exception (group=0x409c01f8) E/AndroidRuntime( 540): FATAL EXCEPTION: main E/AndroidRuntime( 540): java.lang.RuntimeException: Unable to resume activity {com.marakana.android.fibonacciclient/com.marakana.android.fibonacciclient.FibonacciActivity}: java.lang.SecurityException: Not allowed to bind to service Intent { act=com.marakana.android.fibonaccicommon.IFibonacciService } E/AndroidRuntime( 540): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2444) … E/AndroidRuntime( 540): at dalvik.system.NativeStart.main(Native Method) E/AndroidRuntime( 540): Caused by: java.lang.SecurityException: Not allowed to bind to service Intent { act=com.marakana.android.fibonaccicommon.IFibonacciService } E/AndroidRuntime( 540): at android.app.ContextImpl.bindService(ContextImpl.java:1135) E/AndroidRuntime( 540): at android.content.ContextWrapper.bindService(ContextWrapper.java:370) E/AndroidRuntime( 540): at com.marakana.android.fibonacciclient.FibonacciActivity.onResume(FibonacciActivity.java:65) … W/ActivityManager( 85): Force finishing activity com.marakana.android.fibonacciclient/.FibonacciActivity … |
5. Finally, we can give FibonacciClient a fighting chance by allowing it to use the USE_FIBONACCI_SERVICE permission:
FibonacciClient/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest …> … <uses-permission android:name="com.marakana.android.fibonacciservice.USE_FIBONACCI_SERVICE"/> … </manifest>
6. We can now observe that our client is again able to use the service
7. In the Emulator, if we go to Home → Menu → Manage apps → Fibonacci Client → PERMISSIONS, we should see the Fibonacci Permissions group and under it, use fibonacci service permission
Dynamic Permission Enforcement
- Here, we want to restrict access to the com.marakana.android.fibonacciservice.IFibonacciServiceImpl's recursive operations (fibJR(long n) and fibNR(long n)) for n > 10 to applications (i.e. clients) that hold USE_SLOW_FIBONACCI_SERVICE custom permission
1. Like before, we start off by creating a custom permission:
FibonacciService/res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> … <string name="use_slow_fibonacci_service_permission_label"> use slow fibonacci service operations </string> <string name="use_slow_fibonacci_service_permission_description"> applications with this permissions can melt the CPU and drain the battery by using slow fibonacci operations </string> … </resources>
FibonacciService/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest …> … <permission-group …/> <permission …/> <permission android:name="com.marakana.android.fibonacciservice.USE_SLOW_FIBONACCI_SERVICE" android:description="@string/use_slow_fibonacci_service_permission_description" android:label="@string/use_slow_fibonacci_service_permission_label" android:permissionGroup="com.marakana.android.fibonacciservice.FIBONACCI_PERMISSIONS" android:protectionLevel="dangerous" /> … </manifest>
2. Next, we update our IFibonacciServiceImpl to enforce this permission dynamically - via a android.content.Context that get expect to get through the constructor:
FibonacciService/src/com/marakana/android/fibonacciservice/IFibonacciServiceImpl.java:
package com.marakana.android.fibonacciservice; import android.content.Context; … public class IFibonacciServiceImpl extends IFibonacciService.Stub { … private final Context context; public IFibonacciServiceImpl(Context context) { this.context = context; } private long checkN(long n) { if (n > 10) { this.context.enforceCallingOrSelfPermission( Manifest.permission.USE_SLOW_FIBONACCI_SERVICE, "Go away!"); } return n; } … public long fibJR(long n) { … return FibLib.fibJR(this.checkN(n)); } … public long fibNR(long n) { … return FibLib.fibNR(this.checkN(n)); } … }
3. We have to update FibonacciService to invoke the new IFibonacciServiceImpl's constructor:
FibonacciService/src/com/marakana/android/fibonacciservice/FibonacciService.java:
… public class FibonacciService extends Service { … @Override public void onCreate() { … this.service = new IFibonacciServiceImpl(super.getApplicationContext()); … } … }
4. If we now re-run the FibonacciService and re-run the FibonacciClient for a recursive operation with n > 10, we will notice that the client will fail and adb logcat will show something like:
… D/IFibonacciServiceImpl( 617): fib(15, RECURSIVE_NATIVE) D/IFibonacciServiceImpl( 617): fibNR(15) W/dalvikvm( 604): threadid=11: thread exiting with uncaught exception (group=0x409c01f8) E/AndroidRuntime( 604): FATAL EXCEPTION: AsyncTask #1 E/AndroidRuntime( 604): java.lang.RuntimeException: An error occured while executing doInBackground() … E/AndroidRuntime( 604): at java.lang.Thread.run(Thread.java:856) E/AndroidRuntime( 604): Caused by: java.lang.SecurityException: Go away!: Neither user 10043 nor current process has com.marakana.android.fibonacciservice.USE_SLOW_FIBONACCI_SERVICE. … |
5. Finally, we can allow FibonacciClient to melt our CPU and drain our battery by allowing it to use the USE_SLOW_FIBONACCI_SERVICE permission:
FibonacciClient/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest …> … <uses-permission android:name="com.marakana.android.fibonacciservice.USE_SLOW_FIBONACCI_SERVICE"/> … </manifest>
6. We can now observe that our client is again able to use recursive fibonacci operations even for n > 10
7. In the Emulator, if we go to Home → Menu → Manage apps → Fibonacci Client → PERMISSIONS → Fibonacci Permissions, we should see both use fibonacci service and use slow fibonacci service operations permissions
Other Binder Features
- Given a reference to an IBinder object, we can:
+ Ask whether the remote object is alive, via isBinderAlive() and pingBinder()
+ Ask to be notified of its death, via linkToDeath(IBinder.DeathRecipient recipient, int flags):
frameworks/base/services/java/com/android/server/LocationManagerService.java:
public class LocationManagerService extends ILocationManager.Stub implements Runnable { … private Receiver getReceiver(ILocationListener listener) { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver == null) { receiver = new Receiver(listener); … receiver.getListener().asBinder().linkToDeath(receiver, 0); … } return receiver; } private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished { final ILocationListener mListener; … Receiver(ILocationListener listener) { mListener = listener; … } … public void binderDied() { … removeUpdatesLocked(this); } … } … }
- Binder driver reports various stats on active/failed transactions via /proc/binder/
+ /proc/binder/failed_transaction_log
+ /proc/binder/state
+ /proc/binder/stats
+ /proc/binder/transaction_log
+ /proc/binder/transactions
+ /proc/binder/proc/<pid>
......
Additional Resources
- Android Binder by Thorsten Schreiber from Ruhr-Universität Bochum
- Android Binder IPC Mechanism - 0xLab by Jim Huang (黃敬群) from 0xlab
- Android’s Binder by Ken from Ken’s Space
- Dianne Hackborn on Binder in Android on Linux Kernel Mailing List archive (LKML.ORG)
- Android Binder on elinux.org
- Share memory using ashmem and binder in the android framework
- Introduction to OpenBinder and Interview with Dianne Hackborn
- Open Binder Documentation
'Programming > Android Kernel,Native' 카테고리의 다른 글
3.1.1 안드로이드 서비스 클라이언트와 서비스 서버 (0) | 2014.10.22 |
---|---|
3.1 안드로이드 바인더 IPC 개요 (0) | 2014.10.22 |
Deep Dive into Android IPC/Binder Framework(1/2) (0) | 2014.10.21 |
Android IPC Mechanism (0) | 2014.10.21 |
안드로이드 부팅 시 시작되는 프로세스 (혹은 activity) - Talking about Android process (0) | 2014.10.20 |