Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ca9409d authored by Hai Zhang's avatar Hai Zhang
Browse files

Move CheckPermissionDelegate API into PermissionManagerServiceInternal.

Expose only start/stopShellPermissionIdentityDelegation() as API,
because there won't be a use case for UIDs other than the shell UID.

The caller checking is left in ActivityManagerService because only it
knows info about the ongoing intrumentations. So this new system
server API only delegates the permission identity of Shell to someone
else, but checking who can start the delegation to whom is the
responsibility of other parts of the system.

For now the API only delegates permissions checks since app ops won't
be updatable in this release, and it is a platform implementation
detail that ActivityManagerService also delegates app op checks via
the in platform AppOpsService interface at the same time. Once we
complete moving AppOpsService, this API will start delegating for app
ops (or whatever it will become) as well, and then the platform code
can just drop the app op related code and call this API only. Platform
code will have to drop those app op related code by then anyway since
the internal app op interface will no longer be available after the
move.

Bug: 158736025
Test: presubmit
Change-Id: I42839dacdf06e4d94682a46a0e692119de0bfdc0
parent 9c0907c3
Loading
Loading
Loading
Loading
+0 −31
Original line number Diff line number Diff line
@@ -21,10 +21,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.UserHandle;

import com.android.internal.util.function.TriFunction;

import java.util.function.BiFunction;

/**
 * Internal interfaces to be used by other components within the system server.
 *
@@ -50,33 +46,6 @@ public abstract class PermissionManagerInternal {
                @UserIdInt int userId);
    }

    /** Interface to override permission checks via composition */
    public interface CheckPermissionDelegate {
        /**
         * Checks whether the given package has been granted the specified permission.
         *
         * @return If the package has the permission, PERMISSION_GRANTED is
         * returned.  If it does not have the permission, PERMISSION_DENIED
         * is returned.
         *
         * @see android.content.pm.PackageManager#checkPermission(String, String)
         */
        int checkPermission(String permName, String pkgName, int userId,
                TriFunction<String, String, Integer, Integer> superImpl);

        /**
        /**
         * Checks whether the given uid has been granted the specified permission.
         *
         * @return If the package has the permission, PERMISSION_GRANTED is
         * returned.  If it does not have the permission, PERMISSION_DENIED
         * is returned.
         *
         */
        int checkUidPermission(String permName, int uid,
                BiFunction<String, Integer, Integer> superImpl);
    }

    /**
     * Get the state of the runtime permissions as xml file.
     *
+11 −46
Original line number Diff line number Diff line
@@ -263,7 +263,6 @@ import android.os.UserManager;
import android.os.WorkSource;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.permission.PermissionManagerInternal.CheckPermissionDelegate;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.server.ServerProtoEnums;
@@ -325,7 +324,6 @@ import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleInternal;
@@ -392,7 +390,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -14465,7 +14462,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
                        app.info.packageName, AppOpsManager.MODE_ERRORED);
                mAppOpsService.setAppOpsServiceDelegate(null);
                getPermissionManagerInternalLocked().setCheckPermissionDelegate(null);
                getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation();
                mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
                        instr.mUiAutomationConnection).sendToTarget();
            }
@@ -17185,12 +17182,6 @@ public class ActivityManagerService extends IActivityManager.Stub
        // We allow delegation only to one instrumentation started from the shell
        synchronized (ActivityManagerService.this) {
            // If there is a delegate it should be the same instance for app ops and permissions.
            if (mAppOpsService.getAppOpsServiceDelegate()
                    != getPermissionManagerInternalLocked().getCheckPermissionDelegate()) {
                throw new IllegalStateException("Bad shell delegate state");
            }
            // If the delegate is already set up for the target UID, nothing to do.
            if (mAppOpsService.getAppOpsServiceDelegate() != null) {
                if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) {
@@ -17219,10 +17210,14 @@ public class ActivityManagerService extends IActivityManager.Stub
                }
                // Hook them up...
                final ShellDelegate shellDelegate = new ShellDelegate(
                        instr.mTargetInfo.packageName, delegateUid, permissions);
                final ShellDelegate shellDelegate = new ShellDelegate(delegateUid,
                        permissions);
                mAppOpsService.setAppOpsServiceDelegate(shellDelegate);
                getPermissionManagerInternalLocked().setCheckPermissionDelegate(shellDelegate);
                final String packageName = instr.mTargetInfo.packageName;
                final List<String> permissionNames = permissions != null ?
                        Arrays.asList(permissions) : null;
                getPermissionManagerInternalLocked().startShellPermissionIdentityDelegation(
                        delegateUid, packageName, permissionNames);
                return;
            }
        }
@@ -17236,17 +17231,15 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
        synchronized (ActivityManagerService.this) {
            mAppOpsService.setAppOpsServiceDelegate(null);
            getPermissionManagerInternalLocked().setCheckPermissionDelegate(null);
            getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation();
        }
    }
    private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate {
        private final String mTargetPackageName;
    private class ShellDelegate implements CheckOpsDelegate {
        private final int mTargetUid;
        private @Nullable String[] mPermissions;
        ShellDelegate(String targetPackageName, int targetUid, @Nullable String[] permissions) {
            mTargetPackageName = targetPackageName;
        ShellDelegate(int targetUid, @Nullable String[] permissions) {
            mTargetUid = targetUid;
            mPermissions = permissions;
        }
@@ -17309,34 +17302,6 @@ public class ActivityManagerService extends IActivityManager.Stub
                    message, shouldCollectMessage);
        }
        @Override
        public int checkPermission(String permName, String pkgName, int userId,
                TriFunction<String, String, Integer, Integer> superImpl) {
            if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) {
                final long identity = Binder.clearCallingIdentity();
                try {
                    return superImpl.apply(permName, "com.android.shell", userId);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            }
            return superImpl.apply(permName, pkgName, userId);
        }
        @Override
        public int checkUidPermission(String permName, int uid,
                BiFunction<String, Integer, Integer> superImpl) {
            if (uid == mTargetUid  && isTargetPermission(permName)) {
                final long identity = Binder.clearCallingIdentity();
                try {
                    return superImpl.apply(permName, Process.SHELL_UID);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            }
            return superImpl.apply(permName, uid);
        }
        private boolean isTargetOp(int code) {
            // null permissions means all ops are targeted
            if (mPermissions == null) {
+130 −12
Original line number Diff line number Diff line
@@ -114,7 +114,6 @@ import android.permission.IPermissionManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
import android.permission.PermissionManagerInternal.CheckPermissionDelegate;
import android.permission.PermissionManagerInternal.OnRuntimePermissionStateChangedListener;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -138,6 +137,7 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -173,6 +173,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction;

/**
 * Manages all permissions and handles permissions related tasks.
@@ -2287,6 +2288,32 @@ public class PermissionManagerService extends IPermissionManager.Stub {
        }
    }

    private void startShellPermissionIdentityDelegationInternal(int uid,
            @NonNull String packageName, @Nullable List<String> permissionNames) {
        synchronized (mLock) {
            final CheckPermissionDelegate oldDelegate = mCheckPermissionDelegate;
            if (oldDelegate != null && oldDelegate.getDelegatedUid() != uid) {
                throw new SecurityException(
                        "Shell can delegate permissions only to one UID at a time");
            }
            final ShellDelegate delegate = new ShellDelegate(uid, packageName, permissionNames);
            setCheckPermissionDelegateLocked(delegate);
        }
    }

    private void stopShellPermissionIdentityDelegationInternal() {
        synchronized (mLock) {
            setCheckPermissionDelegateLocked(null);
        }
    }

    private void setCheckPermissionDelegateLocked(@Nullable CheckPermissionDelegate delegate) {
        if (delegate != null || mCheckPermissionDelegate != null) {
            PackageManager.invalidatePackageInfoCache();
        }
        mCheckPermissionDelegate = delegate;
    }

    /**
     * We might auto-grant permissions if any permission of the group is already granted. Hence if
     * the group of a granted permission changes we need to revoke it to avoid having permissions of
@@ -5117,20 +5144,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
        }

        @Override
        public CheckPermissionDelegate getCheckPermissionDelegate() {
            synchronized (mLock) {
                return mCheckPermissionDelegate;
            }
        public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName,
                @Nullable List<String> permissionNames) {
            Objects.requireNonNull(packageName, "packageName");
            startShellPermissionIdentityDelegationInternal(uid, packageName, permissionNames);
        }

        @Override
        public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
            synchronized (mLock) {
                if (delegate != null || mCheckPermissionDelegate != null) {
                    PackageManager.invalidatePackageInfoCache();
                }
                mCheckPermissionDelegate = delegate;
            }
        public void stopShellPermissionIdentityDelegation() {
            stopShellPermissionIdentityDelegationInternal();
        }

        @Override
@@ -5366,6 +5388,102 @@ public class PermissionManagerService extends IPermissionManager.Stub {
        }
    }

    /**
     * Interface to intercept permission checks and optionally pass through to the original
     * implementation.
     */
    private interface CheckPermissionDelegate {
        /**
         * Get the UID whose permission checks is being delegated.
         *
         * @return the UID
         */
        int getDelegatedUid();

        /**
         * Check whether the given package has been granted the specified permission.
         *
         * @param permissionName the name of the permission to be checked
         * @param packageName the name of the package to be checked
         * @param userId the user ID
         * @param superImpl the original implementation that can be delegated to
         * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
         * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
         *
         * @see android.content.pm.PackageManager#checkPermission(String, String)
         */
        int checkPermission(@NonNull String permissionName, @NonNull String packageName,
                @UserIdInt int userId,
                @NonNull TriFunction<String, String, Integer, Integer> superImpl);

        /**
         * Check whether the given UID has been granted the specified permission.
         *
         * @param permissionName the name of the permission to be checked
         * @param uid the UID to be checked
         * @param superImpl the original implementation that can be delegated to
         * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
         * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
         */
        int checkUidPermission(@NonNull String permissionName, int uid,
                BiFunction<String, Integer, Integer> superImpl);
    }

    private class ShellDelegate implements CheckPermissionDelegate {
        private final int mDelegatedUid;
        @NonNull
        private final String mDelegatedPackageName;
        @Nullable
        private final List<String> mDelegatedPermissionNames;

        public ShellDelegate(int delegatedUid, @NonNull String delegatedPackageName,
                @Nullable List<String> delegatedPermissionNames) {
            mDelegatedUid = delegatedUid;
            mDelegatedPackageName = delegatedPackageName;
            mDelegatedPermissionNames = delegatedPermissionNames;
        }

        @Override
        public int getDelegatedUid() {
            return mDelegatedUid;
        }

        @Override
        public int checkPermission(@NonNull String permissionName, @NonNull String packageName,
                int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
            if (mDelegatedPackageName.equals(packageName)
                    && isDelegatedPermission(permissionName)) {
                final long identity = Binder.clearCallingIdentity();
                try {
                    return superImpl.apply(permissionName, "com.android.shell", userId);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            }
            return superImpl.apply(permissionName, packageName, userId);
        }

        @Override
        public int checkUidPermission(@NonNull String permissionName, int uid,
                @NonNull BiFunction<String, Integer, Integer> superImpl) {
            if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
                final long identity = Binder.clearCallingIdentity();
                try {
                    return superImpl.apply(permissionName, Process.SHELL_UID);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            }
            return superImpl.apply(permissionName, uid);
        }

        private boolean isDelegatedPermission(@NonNull String permissionName) {
            // null permissions means all permissions are targeted
            return mDelegatedPermissionNames == null
                    || mDelegatedPermissionNames.contains(permissionName);
        }
    }

    /**
     * Allows injection of services and method responses to facilitate testing.
     *
+12 −6
Original line number Diff line number Diff line
@@ -180,18 +180,24 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
            @PermissionInfo.ProtectionFlags int protectionFlags);

    /**
     * Returns the delegate used to influence permission checking.
     * Start delegate the permission identity of the shell UID to the given UID.
     *
     * @return The delegate instance.
     * @param uid the UID to delegate shell permission identity to
     * @param packageName the name of the package to delegate shell permission identity to
     * @param permissionNames the names of the permissions to delegate shell permission identity
     *                       for, or {@code null} for all permissions
     */
    public abstract @Nullable CheckPermissionDelegate getCheckPermissionDelegate();
    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
    public abstract void startShellPermissionIdentityDelegation(int uid,
            @NonNull String packageName, @Nullable List<String> permissionNames);

    /**
     * Sets the delegate used to influence permission checking.
     * Stop delegating the permission identity of the shell UID.
     *
     * @param delegate A delegate instance or {@code null} to clear.
     * @see #startShellPermissionIdentityDelegation(int, String, List)
     */
    public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate);
    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
    public abstract void stopShellPermissionIdentityDelegation();

    /**
     * Sets the dialer application packages provider.