Delegation API
Island provide an API mechanism similar to the “Delegated Scopes” introduced in Android 8, but backward-compatible back to 5.x.
Authorization
First of all, required delegation(s) must be declared in AndroidManifest.xml
as meta-data: (separated by comma)
<meta-data android:name="com.oasisfeng.island.delegation" android:value="delegation-package-access,-island-delegation-app-ops" />
All standard delegations definded by Android SDK can be declared here, together with non-standard delegations (with vendor prefix, just like CSS Vendor Prefix). Some standard delegations requiring recent version of Android are also supported in back-port manner by Island. (see Invocation section below for the instructions of back-ported delegation)
Before invoking any of the privileged APIs, you can check and request authorization with RestrictionsManager
:
final String TYPE_DELEGATION = "com.oasisfeng.island.delegation";
final String DELEGATION_APP_OPS = "-island-delegation-app-ops";
final RestrictionsManager rm = (RestrictionsManager) context.getSystemService(RESTRICTIONS_SERVICE);
if (rm != null && rm.hasRestrictionsProvider()) { // Otherwise, current user is not managed by Island or the version of Island is too low.
final String[] delegations = rm.getApplicationRestrictions().getStringArray(TYPE_DELEGATION);
if (delegations == null || ! Arrays.asList(delegations).contains(DELEGATION_APP_OPS)) {
final PersistableBundle request = new PersistableBundle();
request.putString(RestrictionsManager.REQUEST_KEY_DATA, DELEGATION_APP_OPS);
rm.requestPermission(TYPE_DELEGATION, "com.example.android.app-ops", request)
}
}
Invocation
For standard delegation on supported Android version, corresponding APIs can be invoked directly, as mentioned in official Android developer documents.
For non-standard delegation or standard delegation on not-yet-supported Android version, you can bind to this service of Island to get the internal binder of delegated system service. The binder returned in onServiceConnected()
needs to be injected into a system service manager (e.g. AppOpsManager.mService
) for convenient invocation.
final String ACTION_BIND_SYSTEM_SERVICE = "com.oasisfeng.island.api.action.BIND_SYSTEM_SERVICE";
final Intent intent = new Intent(ACTION_BIND_SYSTEM_SERVICE, Uri.fromParts("service", Context.APP_OPS_SERVICE, null));
final List<ResolveInfo> candidates = context.getPackageManager().queryIntentServices(intent, 0);
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (candidates != null) for (final ResolveInfo candidate : candidates) {
final String pkg = candidate.serviceInfo.packageName;
if (dpm.isDeviceOwnerApp(pkg) || dpm.isProfileOwnerApp(pkg) ) {
if (! context.bindService(intent.setClassName(pkg, candidate.serviceInfo.name), new ServiceConnection() {
@Override public void onServiceConnected(final ComponentName name, final IBinder binder) {
...
context.unbindService(this);
}
@Override public void onServiceDisconnected(final ComponentName name) {}
}, Context.BIND_AUTO_CREATE)) {
Log.e(TAG, "Failed to bind");
}
}
}