diff options
| -rwxr-xr-x | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.aidl | 2 | ||||
| -rw-r--r-- | core/java/android/app/IUiAutomationConnection.aidl | 2 | ||||
| -rw-r--r-- | core/java/android/app/UiAutomation.java | 43 | ||||
| -rw-r--r-- | core/java/android/app/UiAutomationConnection.java | 7 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 47 |
6 files changed, 81 insertions, 21 deletions
diff --git a/api/current.txt b/api/current.txt index cc38630f0d76..63bb349db838 100755 --- a/api/current.txt +++ b/api/current.txt @@ -6180,6 +6180,7 @@ package android.app { public final class UiAutomation { method public void adoptShellPermissionIdentity(); + method public void adoptShellPermissionIdentity(java.lang.String...); method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); method public void dropShellPermissionIdentity(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 519a2749e5bb..80f512c0110f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -486,7 +486,7 @@ interface IActivityManager { * instrumentation at a time. An active instrumentation is one running and * started from the shell. */ - void startDelegateShellPermissionIdentity(int uid); + void startDelegateShellPermissionIdentity(int uid, in String[] permissions); /** * Method for the shell UID to stop deletating its permission identity to an diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index ac4bf7d9c2c5..96da72a1b517 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -47,7 +47,7 @@ interface IUiAutomationConnection { in ParcelFileDescriptor source); void grantRuntimePermission(String packageName, String permission, int userId); void revokeRuntimePermission(String packageName, String permission, int userId); - void adoptShellPermissionIdentity(int uid); + void adoptShellPermissionIdentity(int uid, in String[] permissions); void dropShellPermissionIdentity(); // Called from the system process. oneway void shutdown(); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 5a25f5aed161..3f9627ed807c 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -354,12 +354,17 @@ public final class UiAutomation { } /** - * Adopt the permission identity of the shell UID. This allows you to call APIs protected - * permissions which normal apps cannot hold but are granted to the shell UID. If you - * already adopted the shell permission identity this method would be a no-op. - * Note that your permission state becomes that of the shell UID and it is not a - * combination of your and the shell UID permissions. + * Adopt the permission identity of the shell UID for all permissions. This allows + * you to call APIs protected permissions which normal apps cannot hold but are + * granted to the shell UID. If you already adopted all shell permissions by calling + * this method or {@link #adoptShellPermissionIdentity(String...)} a subsequent call + * would be a no-op. Note that your permission state becomes that of the shell UID + * and it is not a combination of your and the shell UID permissions. + * <p> + * <strong>Note:<strong/> Calling this method adopts all shell permissions and overrides + * any subset of adopted permissions via {@link #adoptShellPermissionIdentity(String...)}. * + * @see #adoptShellPermissionIdentity(String...) * @see #dropShellPermissionIdentity() */ public void adoptShellPermissionIdentity() { @@ -368,7 +373,33 @@ public final class UiAutomation { } try { // Calling out without a lock held. - mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid()); + mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), null); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re); + } + } + + /** + * Adopt the permission identity of the shell UID only for the provided permissions. + * This allows you to call APIs protected permissions which normal apps cannot hold + * but are granted to the shell UID. If you already adopted the specified shell + * permissions by calling this method or {@link #adoptShellPermissionIdentity()} a + * subsequent call would be a no-op. Note that your permission state becomes that of the + * shell UID and it is not a combination of your and the shell UID permissions. + * <p> + * <strong>Note:<strong/> Calling this method adopts only the specified shell permissions + * and overrides all adopted permissions via {@link #adoptShellPermissionIdentity()}. + * + * @see #adoptShellPermissionIdentity() + * @see #dropShellPermissionIdentity() + */ + public void adoptShellPermissionIdentity(String... permissions) { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + try { + // Calling out without a lock held. + mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), permissions); } catch (RemoteException re) { Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re); } diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index b406d9e30a53..dc2f9838785c 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -18,7 +18,7 @@ package android.app; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; -import android.annotation.UnsupportedAppUsage; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.IPackageManager; import android.graphics.Bitmap; @@ -279,7 +279,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override - public void adoptShellPermissionIdentity(int uid) throws RemoteException { + public void adoptShellPermissionIdentity(int uid, @Nullable String[] permissions) + throws RemoteException { synchronized (mLock) { throwIfCalledByNotTrustedUidLocked(); throwIfShutdownLocked(); @@ -287,7 +288,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } final long identity = Binder.clearCallingIdentity(); try { - mActivityManager.startDelegateShellPermissionIdentity(uid); + mActivityManager.startDelegateShellPermissionIdentity(uid, permissions); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e8c6365b38cb..5316f4ec3c34 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19386,7 +19386,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void startDelegateShellPermissionIdentity(int delegateUid) { + public void startDelegateShellPermissionIdentity(int delegateUid, + @Nullable String[] permissions) { if (UserHandle.getCallingAppId() != Process.SHELL_UID && UserHandle.getCallingAppId() != Process.ROOT_UID) { throw new SecurityException("Only the shell can delegate its permissions"); @@ -19405,11 +19406,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) { throw new IllegalStateException("Bad shell delegate state"); } - if (((ShellDelegate) mAppOpsService.getAppOpsServiceDelegate()) - .getDelegateUid() != delegateUid) { + final ShellDelegate delegate = (ShellDelegate) mAppOpsService + .getAppOpsServiceDelegate(); + if (delegate.getDelegateUid() != delegateUid) { throw new SecurityException("Shell can delegate permissions only " + "to one instrumentation at a time"); } + delegate.setPermissions(permissions); return; } @@ -19427,7 +19430,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Hook them up... final ShellDelegate shellDelegate = new ShellDelegate( - instr.mTargetInfo.packageName, delegateUid); + instr.mTargetInfo.packageName, delegateUid, permissions); mAppOpsService.setAppOpsServiceDelegate(shellDelegate); getPackageManagerInternalLocked().setCheckPermissionDelegate(shellDelegate); return; @@ -19450,20 +19453,26 @@ public class ActivityManagerService extends IActivityManager.Stub private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate { private final String mTargetPackageName; private final int mTargetUid; + private @Nullable String[] mPermissions; - ShellDelegate(String targetPacakgeName, int targetUid) { + ShellDelegate(String targetPacakgeName, int targetUid, @Nullable String[] permissions) { mTargetPackageName = targetPacakgeName; mTargetUid = targetUid; + mPermissions = permissions; } int getDelegateUid() { return mTargetUid; } + void setPermissions(@Nullable String[] permissions) { + mPermissions = permissions; + } + @Override public int checkOperation(int code, int uid, String packageName, TriFunction<Integer, Integer, String, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(code, Process.SHELL_UID, @@ -19478,7 +19487,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkAudioOperation(int code, int usage, int uid, String packageName, QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(code, usage, Process.SHELL_UID, @@ -19493,7 +19502,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int noteOperation(int code, int uid, String packageName, TriFunction<Integer, Integer, String, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID, @@ -19508,7 +19517,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkPermission(String permName, String pkgName, int userId, TriFunction<String, String, Integer, Integer> superImpl) { - if (mTargetPackageName.equals(pkgName)) { + if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) { return superImpl.apply(permName, "com.android.shell", userId); } return superImpl.apply(permName, pkgName, userId); @@ -19517,11 +19526,29 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkUidPermission(String permName, int uid, BiFunction<String, Integer, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetPermission(permName)) { return superImpl.apply(permName, Process.SHELL_UID); } return superImpl.apply(permName, uid); } + + private boolean isTargetOp(int code) { + // null permissions means all ops are targeted + if (mPermissions == null) { + return true; + } + // no permission for the op means the op is targeted + final String permission = AppOpsManager.opToPermission(code); + if (permission == null) { + return true; + } + return isTargetPermission(permission); + } + + private boolean isTargetPermission(@NonNull String permission) { + // null permissions means all permissions are targeted + return (mPermissions == null || ArrayUtils.contains(mPermissions, permission)); + } } /** |