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

Commit 053dbad9 authored by Philip P. Moltmann's avatar Philip P. Moltmann Committed by Android (Google) Code Review
Browse files

Merge changes from topic "AppOpsFgBackgroundMode"

* changes:
  Package manager is not ready when reading app-ops state
  Use setUidMode when reading state
  Expose syntax for app-ops settings as test-api
  Callback startWatchingMode with the right op
  Force update uid state when pending uid state is applied
  Notify all packages is uid-mode is changed
parents 8301f05c fe2fc3a9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -183,6 +183,9 @@ package android.app {
    field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
    field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
    field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2
    field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
    field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
    field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
    field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
    field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
    field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
+46 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.usage.UsageStatsManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -100,6 +102,18 @@ import java.util.function.Supplier;
 */
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
    /**
     * This is a subtle behavior change to {@link #startWatchingMode}.
     *
     * Before this change the system called back for the switched op. After the change the system
     * will call back for the actually requested op or all switched ops if no op is specified.
     *
     * @hide
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L;

    /**
     * <p>App ops allows callers to:</p>
     *
@@ -147,6 +161,38 @@ public class AppOpsManager {

    static IBinder sClientId;

    /**
     * How many seconds we want for a drop in uid state from top to settle before applying it.
     *
     * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
     *
     * @hide
     */
    @TestApi
    public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";

    /**
     * How many second we want for a drop in uid state from foreground to settle before applying it.
     *
     * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
     *
     * @hide
     */
    @TestApi
    public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME =
            "fg_service_state_settle_time";

    /**
     * How many seconds we want for a drop in uid state from background to settle before applying
     * it.
     *
     * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
     *
     * @hide
     */
    @TestApi
    public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "HISTORICAL_MODE_" }, value = {
+2 −2
Original line number Diff line number Diff line
@@ -2617,7 +2617,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mProcessCpuThread.start();
        mBatteryStatsService.publish();
        mAppOpsService.publish(mContext);
        mAppOpsService.publish();
        Slog.d("AppOps", "AppOpsService published");
        LocalServices.addService(ActivityManagerInternal.class, mInternal);
        mActivityTaskManager.onActivityManagerInternalAdded();
@@ -19510,7 +19510,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
        public AppOpsService getAppOpsService(File file, Handler handler) {
            return new AppOpsService(file, handler);
            return new AppOpsService(file, handler, getContext());
        }
        public Handler getUiHandler(ActivityManagerService service) {
+161 −79
Original line number Diff line number Diff line
@@ -19,11 +19,15 @@ package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.AppOpsManager.CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE;
import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.NoteOpEvent;
import static android.app.AppOpsManager.OP_CAMERA;
@@ -41,6 +45,7 @@ import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
import static android.app.AppOpsManager.extractUidStateFromKey;
@@ -53,6 +58,10 @@ import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.os.Process.STATSD_UID;

import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;

import static java.lang.Long.max;

import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -70,6 +79,7 @@ import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.AsyncNotedAppOp;
import android.compat.Compatibility;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -129,7 +139,6 @@ import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -216,7 +225,7 @@ public class AppOpsService extends IAppOpsService.Stub {
    //TODO: remove this when development is done.
    private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;

    Context mContext;
    final Context mContext;
    final AtomicFile mFile;
    final Handler mHandler;

@@ -291,8 +300,11 @@ public class AppOpsService extends IAppOpsService.Stub {
    @GuardedBy("this")
    private CheckOpsDelegate mCheckOpsDelegate;

    @GuardedBy("this")
    private SparseArray<List<Integer>> mSwitchOpToOps;
    /**
      * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never
      * changed
      */
    private final SparseArray<int[]> mSwitchedOps = new SparseArray<>();

    private ActivityManagerInternal mActivityManagerInternal;

@@ -343,30 +355,25 @@ public class AppOpsService extends IAppOpsService.Stub {
     */
    @VisibleForTesting
    final class Constants extends ContentObserver {
        // Key names stored in the settings value.
        private static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
        private static final String KEY_FG_SERVICE_STATE_SETTLE_TIME
                = "fg_service_state_settle_time";
        private static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";

        /**
         * How long we want for a drop in uid state from top to settle before applying it.
         * @see Settings.Global#APP_OPS_CONSTANTS
         * @see #KEY_TOP_STATE_SETTLE_TIME
         * @see AppOpsManager#KEY_TOP_STATE_SETTLE_TIME
         */
        public long TOP_STATE_SETTLE_TIME;

        /**
         * How long we want for a drop in uid state from foreground to settle before applying it.
         * @see Settings.Global#APP_OPS_CONSTANTS
         * @see #KEY_FG_SERVICE_STATE_SETTLE_TIME
         * @see AppOpsManager#KEY_FG_SERVICE_STATE_SETTLE_TIME
         */
        public long FG_SERVICE_STATE_SETTLE_TIME;

        /**
         * How long we want for a drop in uid state from background to settle before applying it.
         * @see Settings.Global#APP_OPS_CONSTANTS
         * @see #KEY_BG_STATE_SETTLE_TIME
         * @see AppOpsManager#KEY_BG_STATE_SETTLE_TIME
         */
        public long BG_STATE_SETTLE_TIME;

@@ -1191,17 +1198,22 @@ public class AppOpsService extends IAppOpsService.Stub {
    final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager();

    final class ModeCallback implements DeathRecipient {
        /** If mWatchedOpCode==ALL_OPS notify for ops affected by the switch-op */
        public static final int ALL_OPS = -2;

        final IAppOpsCallback mCallback;
        final int mWatchingUid;
        final int mFlags;
        final int mWatchedOpCode;
        final int mCallingUid;
        final int mCallingPid;

        ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int callingUid,
                int callingPid) {
        ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOp,
                int callingUid, int callingPid) {
            mCallback = callback;
            mWatchingUid = watchingUid;
            mFlags = flags;
            mWatchedOpCode = watchedOp;
            mCallingUid = callingUid;
            mCallingPid = callingPid;
            try {
@@ -1224,6 +1236,10 @@ public class AppOpsService extends IAppOpsService.Stub {
            UserHandle.formatUid(sb, mWatchingUid);
            sb.append(" flags=0x");
            sb.append(Integer.toHexString(mFlags));
            if (mWatchedOpCode != OP_NONE) {
                sb.append(" op=");
                sb.append(opToName(mWatchedOpCode));
            }
            sb.append(" from uid=");
            UserHandle.formatUid(sb, mCallingUid);
            sb.append(" pid=");
@@ -1337,16 +1353,23 @@ public class AppOpsService extends IAppOpsService.Stub {
        featureOp.onClientDeath(clientId);
    }

    public AppOpsService(File storagePath, Handler handler) {
    public AppOpsService(File storagePath, Handler handler, Context context) {
        mContext = context;

        LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
        mFile = new AtomicFile(storagePath, "appops");
        mHandler = handler;
        mConstants = new Constants(mHandler);
        readState();

        for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) {
            int switchCode = AppOpsManager.opToSwitch(switchedCode);
            mSwitchedOps.put(switchCode,
                    ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode));
        }
    }

    public void publish(Context context) {
        mContext = context;
    public void publish() {
        ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
        LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
    }
@@ -1616,6 +1639,19 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    /**
     * Update the pending state for the uid
     *
     * @param currentTime The current elapsed real time
     * @param uid The uid that has a pending state
     */
    private void updatePendingState(long currentTime, int uid) {
        synchronized (this) {
            mLastRealtime = max(currentTime, mLastRealtime);
            updatePendingStateIfNeededLocked(mUidStates.get(uid));
        }
    }

    public void updateUidProcState(int uid, int procState,
            @ActivityManager.ProcessCapability int capability) {
        synchronized (this) {
@@ -1647,7 +1683,12 @@ public class AppOpsService extends IAppOpsService.Stub {
                    } else {
                        settleTime = mConstants.BG_STATE_SETTLE_TIME;
                    }
                    uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
                    final long commitTime = SystemClock.elapsedRealtime() + settleTime;
                    uidState.pendingStateCommitTime = commitTime;

                    mHandler.sendMessageDelayed(
                            PooledLambda.obtainMessage(AppOpsService::updatePendingState, this,
                                    commitTime + 1, uid), settleTime + 1);
                }

                if (uidState.pkgOps != null) {
@@ -2014,6 +2055,19 @@ public class AppOpsService extends IAppOpsService.Stub {
            uidState.evalForegroundOps(mOpModeWatchers);
        }

        notifyOpChangedForAllPkgsInUid(code, uid, false, callbackToIgnore);
        notifyOpChangedSync(code, uid, null, mode);
    }

    /**
     * Notify that an op changed for all packages in an uid.
     *
     * @param code The op that changed
     * @param uid The uid the op was changed for
     * @param onlyForeground Only notify watchers that watch for foreground changes
     */
    private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground,
            @Nullable IAppOpsCallback callbackToIgnore) {
        String[] uidPackageNames = getPackagesForUid(uid);
        ArrayMap<ModeCallback, ArraySet<String>> callbackSpecs = null;

@@ -2023,6 +2077,10 @@ public class AppOpsService extends IAppOpsService.Stub {
                final int callbackCount = callbacks.size();
                for (int i = 0; i < callbackCount; i++) {
                    ModeCallback callback = callbacks.valueAt(i);
                    if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) {
                        continue;
                    }

                    ArraySet<String> changedPackages = new ArraySet<>();
                    Collections.addAll(changedPackages, uidPackageNames);
                    if (callbackSpecs == null) {
@@ -2041,6 +2099,10 @@ public class AppOpsService extends IAppOpsService.Stub {
                    final int callbackCount = callbacks.size();
                    for (int i = 0; i < callbackCount; i++) {
                        ModeCallback callback = callbacks.valueAt(i);
                        if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) {
                            continue;
                        }

                        ArraySet<String> changedPackages = callbackSpecs.get(callback);
                        if (changedPackages == null) {
                            changedPackages = new ArraySet<>();
@@ -2057,7 +2119,6 @@ public class AppOpsService extends IAppOpsService.Stub {
        }

        if (callbackSpecs == null) {
            notifyOpChangedSync(code, uid, null, mode);
            return;
        }

@@ -2079,23 +2140,24 @@ public class AppOpsService extends IAppOpsService.Stub {
                }
            }
        }

        notifyOpChangedSync(code, uid, null, mode);
    }

    private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
        PackageManager packageManager = mContext.getPackageManager();
        if (packageManager == null) {
            // This can only happen during early boot. At this time the permission state and appop
            // state are in sync
            return;
        }

        String[] packageNames = packageManager.getPackagesForUid(uid);
        if (ArrayUtils.isEmpty(packageNames)) {
            return;
        }
        String packageName = packageNames[0];

        List<Integer> ops = getSwitchOpToOps().get(switchCode);
        int opsSize = CollectionUtils.size(ops);
        for (int i = 0; i < opsSize; i++) {
            int code = ops.get(i);

        int[] ops = mSwitchedOps.get(switchCode);
        for (int code : ops) {
            String permissionName = AppOpsManager.opToPermission(code);
            if (permissionName == null) {
                continue;
@@ -2173,25 +2235,6 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    @NonNull
    private SparseArray<List<Integer>> getSwitchOpToOps() {
        synchronized (this) {
            if (mSwitchOpToOps == null) {
                mSwitchOpToOps = new SparseArray<>();
                for (int op = 0; op < _NUM_OP; op++) {
                    int switchOp = AppOpsManager.opToSwitch(op);
                    List<Integer> ops = mSwitchOpToOps.get(switchOp);
                    if (ops == null) {
                        ops = new ArrayList<>();
                        mSwitchOpToOps.put(switchOp, ops);
                    }
                    ops.add(op);
                }
            }
            return mSwitchOpToOps;
        }
    }

    private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) {
        final StorageManagerInternal storageManagerInternal =
                LocalServices.getService(StorageManagerInternal.class);
@@ -2292,18 +2335,31 @@ public class AppOpsService extends IAppOpsService.Stub {
        if (uid != UID_ANY && callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
            return;
        }

        // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
        int[] switchedCodes;
        if (callback.mWatchedOpCode == ALL_OPS) {
            switchedCodes = mSwitchedOps.get(code);
        } else if (callback.mWatchedOpCode == OP_NONE) {
            switchedCodes = new int[]{code};
        } else {
            switchedCodes = new int[]{callback.mWatchedOpCode};
        }

        for (int switchedCode : switchedCodes) {
            // There are features watching for mode changes such as window manager
            // and location manager which are in our process. The callbacks in these
            // features may require permissions our remote caller does not have.
            final long identity = Binder.clearCallingIdentity();
            try {
            callback.mCallback.opChanged(code, uid, packageName);
                callback.mCallback.opChanged(switchedCode, uid, packageName);
            } catch (RemoteException e) {
                /* ignore */
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }
    }

    private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks(
            HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks,
@@ -2496,17 +2552,32 @@ public class AppOpsService extends IAppOpsService.Stub {
            return;
        }
        synchronized (this) {
            op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
            int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;

            // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
            int notifiedOps;
            if (Compatibility.isChangeEnabled(
                    CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
                if (op == OP_NONE) {
                    notifiedOps = ALL_OPS;
                } else {
                    notifiedOps = op;
                }
            } else {
                notifiedOps = switchOp;
            }

            ModeCallback cb = mModeWatchers.get(callback.asBinder());
            if (cb == null) {
                cb = new ModeCallback(callback, watchedUid, flags, callingUid, callingPid);
                cb = new ModeCallback(callback, watchedUid, flags, notifiedOps, callingUid,
                        callingPid);
                mModeWatchers.put(callback.asBinder(), cb);
            }
            if (op != AppOpsManager.OP_NONE) {
                ArraySet<ModeCallback> cbs = mOpModeWatchers.get(op);
            if (switchOp != AppOpsManager.OP_NONE) {
                ArraySet<ModeCallback> cbs = mOpModeWatchers.get(switchOp);
                if (cbs == null) {
                    cbs = new ArraySet<>();
                    mOpModeWatchers.put(op, cbs);
                    mOpModeWatchers.put(switchOp, cbs);
                }
                cbs.add(cb);
            }
@@ -3325,6 +3396,18 @@ public class AppOpsService extends IAppOpsService.Stub {
            uidState = new UidState(uid);
            mUidStates.put(uid, uidState);
        } else {
            updatePendingStateIfNeededLocked(uidState);
        }
        return uidState;
    }

    /**
     * Check if the pending state should be updated and do so if needed
     *
     * @param uidState The uidState that might have a pending state
     */
    private void updatePendingStateIfNeededLocked(@NonNull UidState uidState) {
        if (uidState != null) {
            if (uidState.pendingStateCommitTime != 0) {
                if (uidState.pendingStateCommitTime < mLastRealtime) {
                    commitUidPendingStateLocked(uidState);
@@ -3336,7 +3419,6 @@ public class AppOpsService extends IAppOpsService.Stub {
                }
            }
        }
        return uidState;
    }

    private void commitUidPendingStateLocked(UidState uidState) {
@@ -3356,6 +3438,14 @@ public class AppOpsService extends IAppOpsService.Stub {
                        && uidState.appWidgetVisible == uidState.pendingAppWidgetVisible) {
                    continue;
                }

                if (uidState.opModes != null
                        && uidState.opModes.indexOfKey(code) >= 0
                        && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) {
                    mHandler.sendMessage(PooledLambda.obtainMessage(
                            AppOpsService::notifyOpChangedForAllPkgsInUid,
                            this, code, uidState.uid, true, null));
                } else {
                    final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
                    if (callbacks != null) {
                        for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) {
@@ -3364,16 +3454,12 @@ public class AppOpsService extends IAppOpsService.Stub {
                                    || !callback.isWatchingUid(uidState.uid)) {
                                continue;
                            }
                        boolean doAllPackages = uidState.opModes != null
                                && uidState.opModes.indexOfKey(code) >= 0
                                && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND;
                        if (uidState.pkgOps != null) {
                            for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
                                final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
                                if (op == null) {
                                    continue;
                                }
                                if (doAllPackages || op.mode == AppOpsManager.MODE_FOREGROUND) {
                                if (op.mode == AppOpsManager.MODE_FOREGROUND) {
                                    mHandler.sendMessage(PooledLambda.obtainMessage(
                                            AppOpsService::notifyOpChanged,
                                            this, callback, code, uidState.uid,
@@ -3798,11 +3884,7 @@ public class AppOpsService extends IAppOpsService.Stub {
            if (tagName.equals("op")) {
                final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
                final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
                UidState uidState = getUidStateLocked(uid, true);
                if (uidState.opModes == null) {
                    uidState.opModes = new SparseIntArray();
                }
                uidState.opModes.put(code, mode);
                setUidMode(code, uid, mode);
            } else {
                Slog.w(TAG, "Unknown element under <uid-ops>: "
                        + parser.getName());
Loading