diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 90a3667016dea..a2634da67a554 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -355,11 +355,13 @@ package android.app { public final class UiAutomation { method public void destroy(); method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String); + method @NonNull public java.util.Set getAdoptedShellPermissions(); method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle); method public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean); method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle); method public void syncInputTransactions(); method public void syncInputTransactions(boolean); + field @NonNull public static final java.util.Set ALL_PERMISSIONS; } public class UiModeManager { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 81e5e1d962944..1b8eb8add7918 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -571,6 +571,12 @@ interface IActivityManager { */ void stopDelegateShellPermissionIdentity(); + /** + * Method for the shell UID to get currently adopted permissions for an active instrumentation. + * An active instrumentation is one running and started from the shell. + */ + List getDelegatedShellPermissions(); + /** Returns a file descriptor that'll be closed when the system server process dies. */ ParcelFileDescriptor getLifeMonitor(); diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index ec7d7832dc820..c30bc248344c7 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -24,6 +24,8 @@ import android.view.WindowContentFrameStats; import android.view.WindowAnimationFrameStats; import android.os.ParcelFileDescriptor; +import java.util.List; + /** * This interface contains privileged operations a shell program can perform * on behalf of an instrumentation that it runs. These operations require @@ -54,4 +56,5 @@ interface IUiAutomationConnection { oneway void shutdown(); void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink, in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink); + List getAdoptedShellPermissions(); } diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 05872bad30f07..7d3db5e2cc491 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -43,6 +43,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -66,7 +67,9 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeoutException; /** @@ -160,6 +163,16 @@ public final class UiAutomation { */ public static final int FLAG_DONT_USE_ACCESSIBILITY = 0x00000002; + /** + * Returned by {@link #getAdoptedShellPermissions} to indicate that all permissions have been + * adopted using {@link #adoptShellPermissionIdentity}. + * + * @hide + */ + @TestApi + @NonNull + public static final Set ALL_PERMISSIONS = Set.of("_ALL_PERMISSIONS_"); + private final Object mLock = new Object(); private final ArrayList mEventQueue = new ArrayList(); @@ -498,6 +511,25 @@ public final class UiAutomation { } } + /** + * Returns a list of adopted shell permissions using {@link #adoptShellPermissionIdentity}, + * returns and empty set if no permissions are adopted and {@link #ALL_PERMISSIONS} if all + * permissions are adopted. + * + * @hide + */ + @TestApi + @NonNull + public Set getAdoptedShellPermissions() { + try { + final List permissions = mUiAutomationConnection.getAdoptedShellPermissions(); + return permissions == null ? ALL_PERMISSIONS : new ArraySet<>(permissions); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error getting adopted shell permissions", re); + return Collections.emptySet(); + } + } + /** * Performs a global action. Such an action can be performed at any moment * regardless of the current application or user location in that application. diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 7036b6e7dbc94..90210a9a27569 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -49,6 +49,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; /** * This is a remote object that is passed from the shell to an instrumentation @@ -340,6 +341,22 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } } + @Override + @Nullable + public List getAdoptedShellPermissions() throws RemoteException { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + final long identity = Binder.clearCallingIdentity(); + try { + return mActivityManager.getDelegatedShellPermissions(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + public class Repeater implements Runnable { // Continuously read readFrom and write back to writeTo until EOF is encountered private final InputStream readFrom; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8e63efa562c53..ad978c86c55ae 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16548,6 +16548,17 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public List getDelegatedShellPermissions() { + if (UserHandle.getCallingAppId() != Process.SHELL_UID + && UserHandle.getCallingAppId() != Process.ROOT_UID) { + throw new SecurityException("Only the shell can get delegated permissions"); + } + synchronized (mProcLock) { + return getPermissionManagerInternal().getDelegatedShellPermissions(); + } + } + private class ShellDelegate implements CheckOpsDelegate { private final int mTargetUid; @Nullable diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index fab0c00cb5ad3..310ef237edccd 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2085,6 +2085,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + @Nullable + private List getDelegatedShellPermissionsInternal() { + synchronized (mLock) { + if (mCheckPermissionDelegate == null) { + return Collections.EMPTY_LIST; + } + return mCheckPermissionDelegate.getDelegatedPermissionNames(); + } + } + private void setCheckPermissionDelegateLocked(@Nullable CheckPermissionDelegate delegate) { if (delegate != null || mCheckPermissionDelegate != null) { PackageManager.invalidatePackageInfoCache(); @@ -5041,6 +5051,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { stopShellPermissionIdentityDelegationInternal(); } + @Override + @NonNull + public List getDelegatedShellPermissions() { + return getDelegatedShellPermissionsInternal(); + } + @Override public void onUserCreated(@UserIdInt int userId) { Preconditions.checkArgumentNonNegative(userId, "userId"); @@ -5234,6 +5250,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { */ int checkUidPermission(int uid, @NonNull String permissionName, BiFunction superImpl); + + /** + * @return list of delegated permissions + */ + List getDelegatedPermissionNames(); } private class ShellDelegate implements CheckPermissionDelegate { @@ -5284,6 +5305,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { return superImpl.apply(uid, permissionName); } + @Override + public List getDelegatedPermissionNames() { + return mDelegatedPermissionNames == null + ? null + : new ArrayList<>(mDelegatedPermissionNames); + } + private boolean isDelegatedPermission(@NonNull String permissionName) { // null permissions means all permissions are targeted return mDelegatedPermissionNames == null diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 3939d0efb4a1c..895cabc33bab1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -183,6 +183,11 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) void stopShellPermissionIdentityDelegation(); + /** + * Get all delegated shell permissions. + */ + @NonNull List getDelegatedShellPermissions(); + /** * Read legacy permissions from legacy permission settings. *