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

Commit a541e9a0 authored by Evan Severson's avatar Evan Severson Committed by Android (Google) Code Review
Browse files

Merge "Watch uid proc state instead of importance for 1-time permissions"

parents 0d0b0bce 61864145
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -77,8 +77,7 @@ interface IPermissionManager {
    List<SplitPermissionInfoParcelable> getSplitPermissions();

    void startOneTimePermissionSession(String packageName, int userId, long timeout,
            long revokeAfterKilledDelay, int importanceToResetTimer,
            int importanceToKeepSessionAlive);
            long revokeAfterKilledDelay);

    @EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
    void stopOneTimePermissionSession(String packageName, int userId);
+1 −2
Original line number Diff line number Diff line
@@ -1371,8 +1371,7 @@ public final class PermissionManager {
            @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
        try {
            mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
                    timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
                    importanceToKeepSessionAlive);
                    timeoutMillis, revokeAfterKilledDelayMillis);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
+112 −119
Original line number Diff line number Diff line
@@ -16,17 +16,18 @@

package com.android.server.pm.permission;

import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
import android.permission.PermissionControllerManager;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -48,7 +49,7 @@ public class OneTimePermissionUserManager {
            "one_time_permissions_killed_delay_millis";

    private final @NonNull Context mContext;
    private final @NonNull ActivityManager mActivityManager;
    private final @NonNull IActivityManager mIActivityManager;
    private final @NonNull AlarmManager mAlarmManager;
    private final @NonNull PermissionControllerManager mPermissionControllerManager;

@@ -78,50 +79,15 @@ public class OneTimePermissionUserManager {

    OneTimePermissionUserManager(@NonNull Context context) {
        mContext = context;
        mActivityManager = context.getSystemService(ActivityManager.class);
        mIActivityManager = ActivityManager.getService();
        mAlarmManager = context.getSystemService(AlarmManager.class);
        mPermissionControllerManager = new PermissionControllerManager(
                mContext, PermissionThread.getHandler());
        mHandler = context.getMainThreadHandler();
    }

    /**
     * Starts a one-time permission session for a given package. A one-time permission session is
     * ended if app becomes inactive. Inactivity is defined as the package's uid importance level
     * staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid
     * importance level goes <= importanceToResetTimer then the timer is reset and doesn't start
     * until going > importanceToResetTimer.
     * <p>
     * When this timeoutMillis is reached if the importance level is <= importanceToKeepSessionAlive
     * then the session is extended until either the importance goes above
     * importanceToKeepSessionAlive which will end the session or <= importanceToResetTimer which
     * will continue the session and reset the timer.
     * </p>
     * <p>
     * Importance levels are defined in {@link android.app.ActivityManager.RunningAppProcessInfo}.
     * </p>
     * <p>
     * Once the session ends PermissionControllerService#onNotifyOneTimePermissionSessionTimeout
     * is invoked.
     * </p>
     * <p>
     * Note that if there is currently an active session for a package a new one isn't created and
     * the existing one isn't changed.
     * </p>
     * @param packageName The package to start a one-time permission session for
     * @param timeoutMillis Number of milliseconds for an app to be in an inactive state
     * @param revokeAfterKilledDelayMillis Number of milliseconds to wait after the process dies
     *                                     before ending the session. Set to -1 to use default value
     *                                     for the device.
     * @param importanceToResetTimer The least important level to uid must be to reset the timer
     * @param importanceToKeepSessionAlive The least important level the uid must be to keep the
     *                                     session alive
     *
     * @hide
     */
    void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
            long revokeAfterKilledDelayMillis, int importanceToResetTimer,
            int importanceToKeepSessionAlive) {
            long revokeAfterKilledDelayMillis) {
        int uid;
        try {
            uid = mContext.getPackageManager().getPackageUid(packageName, 0);
@@ -133,13 +99,11 @@ public class OneTimePermissionUserManager {
        synchronized (mLock) {
            PackageInactivityListener listener = mListeners.get(uid);
            if (listener != null) {
                listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis,
                        importanceToResetTimer, importanceToKeepSessionAlive);
                listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis);
                return;
            }
            listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
                    revokeAfterKilledDelayMillis, importanceToResetTimer,
                    importanceToKeepSessionAlive);
                    revokeAfterKilledDelayMillis);
            mListeners.put(uid, listener);
        }
    }
@@ -184,34 +148,58 @@ public class OneTimePermissionUserManager {

        private static final long TIMER_INACTIVE = -1;

        private static final int STATE_GONE = 0;
        private static final int STATE_TIMER = 1;
        private static final int STATE_ACTIVE = 2;

        private final int mUid;
        private final @NonNull String mPackageName;
        private long mTimeout;
        private long mRevokeAfterKilledDelay;
        private int mImportanceToResetTimer;
        private int mImportanceToKeepSessionAlive;

        private boolean mIsAlarmSet;
        private boolean mIsFinished;

        private long mTimerStart = TIMER_INACTIVE;

        private final ActivityManager.OnUidImportanceListener mStartTimerListener;
        private final ActivityManager.OnUidImportanceListener mSessionKillableListener;
        private final ActivityManager.OnUidImportanceListener mGoneListener;

        private final Object mInnerLock = new Object();
        private final Object mToken = new Object();
        private final IUidObserver.Stub mObserver = new IUidObserver.Stub() {
            @Override
            public void onUidGone(int uid, boolean disabled) {
                if (uid == mUid) {
                    PackageInactivityListener.this.updateUidState(STATE_GONE);
                }
            }

        private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
                long revokeAfterkilledDelay, int importanceToResetTimer,
                int importanceToKeepSessionAlive) {
            @Override
            public void onUidStateChanged(int uid, int procState, long procStateSeq,
                    int capability) {
                if (uid == mUid) {
                    if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
                            && procState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
                        PackageInactivityListener.this.updateUidState(STATE_TIMER);
                    } else {
                        PackageInactivityListener.this.updateUidState(STATE_ACTIVE);
                    }
                }
            }

            public void onUidActive(int uid) {
            }
            public void onUidIdle(int uid, boolean disabled) {
            }
            public void onUidProcAdjChanged(int uid) {
            }
            public void onUidCachedChanged(int uid, boolean cached) {
            }
        };

        private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
                long revokeAfterkilledDelay) {
            Log.i(LOG_TAG,
                    "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
                            + " killedDelay=" + revokeAfterkilledDelay
                            + " importanceToResetTimer=" + importanceToResetTimer
                            + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
                            + " killedDelay=" + revokeAfterkilledDelay);

            mUid = uid;
            mPackageName = packageName;
@@ -221,27 +209,24 @@ public class OneTimePermissionUserManager {
                            DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_KILLED_DELAY_CONFIG_KEY,
                            DEFAULT_KILLED_DELAY_MILLIS)
                    : revokeAfterkilledDelay;
            mImportanceToResetTimer = importanceToResetTimer;
            mImportanceToKeepSessionAlive = importanceToKeepSessionAlive;

            mStartTimerListener =
                    (changingUid, importance) -> onImportanceChanged(changingUid, importance);
            mSessionKillableListener =
                    (changingUid, importance) -> onImportanceChanged(changingUid, importance);
            mGoneListener =
                    (changingUid, importance) -> onImportanceChanged(changingUid, importance);

            mActivityManager.addOnUidImportanceListener(mStartTimerListener,
                    importanceToResetTimer);
            mActivityManager.addOnUidImportanceListener(mSessionKillableListener,
                    importanceToKeepSessionAlive);
            mActivityManager.addOnUidImportanceListener(mGoneListener, IMPORTANCE_CACHED);
            try {
                mIActivityManager.registerUidObserver(mObserver,
                        ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE,
                        ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
                        null);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Couldn't check uid proc state", e);
                // Can't register uid observer, just revoke immediately
                synchronized (mInnerLock) {
                    onPackageInactiveLocked();
                }
            }

            onImportanceChanged(mUid, mActivityManager.getPackageImportance(packageName));
            updateUidState();
        }

        public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis,
                int importanceToResetTimer, int importanceToKeepSessionAlive) {
        public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis) {
            synchronized (mInnerLock) {
                mTimeout = Math.min(mTimeout, timeoutMillis);
                mRevokeAfterKilledDelay = Math.min(mRevokeAfterKilledDelay,
@@ -250,63 +235,79 @@ public class OneTimePermissionUserManager {
                                DeviceConfig.NAMESPACE_PERMISSIONS,
                                PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
                                : revokeAfterKilledDelayMillis);
                mImportanceToResetTimer = Math.min(importanceToResetTimer, mImportanceToResetTimer);
                mImportanceToKeepSessionAlive = Math.min(importanceToKeepSessionAlive,
                        mImportanceToKeepSessionAlive);
                Log.v(LOG_TAG,
                        "Updated params for " + mPackageName + ". timeout=" + mTimeout
                                + " killedDelay=" + mRevokeAfterKilledDelay
                                + " importanceToResetTimer=" + mImportanceToResetTimer
                                + " importanceToKeepSessionAlive=" + mImportanceToKeepSessionAlive);
                onImportanceChanged(mUid, mActivityManager.getPackageImportance(mPackageName));
                                + " killedDelay=" + mRevokeAfterKilledDelay);
                updateUidState();
            }
        }

        private void onImportanceChanged(int uid, int importance) {
            if (uid != mUid) {
                return;
        private int getCurrentState() {
            try {
                return getStateFromProcState(mIActivityManager.getUidProcessState(mUid, null));
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Couldn't check uid proc state", e);
            }
            return STATE_GONE;
        }

        private int getStateFromProcState(int procState) {
            if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                return STATE_GONE;
            } else {
                if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
                    return STATE_TIMER;
                } else {
                    return STATE_ACTIVE;
                }
            }
        }

        private void updateUidState() {
            updateUidState(getCurrentState());
        }

            Log.v(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
                    + " importance=" + importance);
        private void updateUidState(int state) {
            Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")."
                    + " state=" + state);
            synchronized (mInnerLock) {
                // Remove any pending inactivity callback
                mHandler.removeCallbacksAndMessages(mToken);

                if (importance > IMPORTANCE_CACHED) {
                if (state == STATE_GONE) {
                    if (mRevokeAfterKilledDelay == 0) {
                        onPackageInactiveLocked();
                        return;
                    }
                    // Delay revocation in case app is restarting
                    mHandler.postDelayed(() -> {
                        int imp = mActivityManager.getUidImportance(mUid);
                        if (imp > IMPORTANCE_CACHED) {
                        int currentState;
                        synchronized (mInnerLock) {
                            currentState = getCurrentState();
                            if (currentState == STATE_GONE) {
                                onPackageInactiveLocked();
                        } else {
                                return;
                            }
                        }
                        if (DEBUG) {
                            Log.d(LOG_TAG, "No longer gone after delayed revocation. "
                                        + "Rechecking for " + mPackageName + " (" + mUid + ").");
                            }
                            onImportanceChanged(mUid, imp);
                                    + "Rechecking for " + mPackageName + " (" + mUid
                                    + ").");
                        }
                        updateUidState(currentState);
                    }, mToken, mRevokeAfterKilledDelay);
                    return;
                }
                if (importance > mImportanceToResetTimer) {
                } else if (state == STATE_TIMER) {
                    if (mTimerStart == TIMER_INACTIVE) {
                        if (DEBUG) {
                            Log.d(LOG_TAG, "Start the timer for "
                                    + mPackageName + " (" + mUid + ").");
                        }
                        mTimerStart = System.currentTimeMillis();
                        setAlarmLocked();
                    }
                } else {
                } else if (state == STATE_ACTIVE) {
                    mTimerStart = TIMER_INACTIVE;
                }
                if (importance > mImportanceToKeepSessionAlive) {
                    setAlarmLocked();
                } else {
                    cancelAlarmLocked();
                }
            }
@@ -320,19 +321,9 @@ public class OneTimePermissionUserManager {
                mIsFinished = true;
                cancelAlarmLocked();
                try {
                    mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
                } catch (IllegalArgumentException e) {
                    Log.e(LOG_TAG, "Could not remove start timer listener", e);
                }
                try {
                    mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
                } catch (IllegalArgumentException e) {
                    Log.e(LOG_TAG, "Could not remove session killable listener", e);
                }
                try {
                    mActivityManager.removeOnUidImportanceListener(mGoneListener);
                } catch (IllegalArgumentException e) {
                    Log.e(LOG_TAG, "Could not remove gone listener", e);
                    mIActivityManager.unregisterUidObserver(mObserver);
                } catch (RemoteException e) {
                    Log.e(LOG_TAG, "Unable to unregister uid observer.", e);
                }
            }
        }
@@ -396,9 +387,11 @@ public class OneTimePermissionUserManager {
                        mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
                                mPackageName);
                    });
            mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
            mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
            mActivityManager.removeOnUidImportanceListener(mGoneListener);
            try {
                mIActivityManager.unregisterUidObserver(mObserver);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Unable to unregister uid observer.", e);
            }
            synchronized (mLock) {
                mListeners.remove(mUid);
            }
+2 −4
Original line number Diff line number Diff line
@@ -385,8 +385,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {

    @Override
    public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
            long timeoutMillis, long revokeAfterKilledDelayMillis, int importanceToResetTimer,
            int importanceToKeepSessionAlive) {
            long timeoutMillis, long revokeAfterKilledDelayMillis) {
        mContext.enforceCallingOrSelfPermission(
                Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
                "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
@@ -396,8 +395,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
        final long token = Binder.clearCallingIdentity();
        try {
            getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
                    timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
                    importanceToKeepSessionAlive);
                    timeoutMillis, revokeAfterKilledDelayMillis);
        } finally {
            Binder.restoreCallingIdentity(token);
        }