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

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

Merge "Make one-time permission sessions continue over service restarts" into rvc-dev

parents 65a3920a 3535e815
Loading
Loading
Loading
Loading
+84 −19
Original line number Diff line number Diff line
@@ -21,9 +21,14 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHE
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AlarmManager;
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.permission.PermissionControllerManager;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;

@@ -36,7 +41,10 @@ public class OneTimePermissionUserManager {

    private static final String LOG_TAG = OneTimePermissionUserManager.class.getSimpleName();

    private static final boolean DEBUG = true;
    private static final boolean DEBUG = false;
    private static final long DEFAULT_KILLED_DELAY_MILLIS = 5000;
    public static final String PROPERTY_KILLED_DELAY_CONFIG_KEY =
            "one_time_permissions_killed_delay_millis";

    private final @NonNull Context mContext;
    private final @NonNull ActivityManager mActivityManager;
@@ -45,15 +53,37 @@ public class OneTimePermissionUserManager {

    private final Object mLock = new Object();

    private final BroadcastReceiver mUninstallListener = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_UID_REMOVED.equals(intent.getAction())) {
                int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                PackageInactivityListener listener = mListeners.get(uid);
                if (listener != null) {
                    if (DEBUG) {
                        Log.d(LOG_TAG, "Removing  the inactivity listener for " + uid);
                    }
                    listener.cancel();
                    mListeners.remove(uid);
                }
            }
        }
    };

    /** Maps the uid to the PackageInactivityListener */
    @GuardedBy("mLock")
    private final SparseArray<PackageInactivityListener> mListeners = new SparseArray<>();
    private final Handler mHandler;

    OneTimePermissionUserManager(@NonNull Context context) {
        mContext = context;
        mActivityManager = context.getSystemService(ActivityManager.class);
        mAlarmManager = context.getSystemService(AlarmManager.class);
        mPermissionControllerManager = context.getSystemService(PermissionControllerManager.class);
        mHandler = context.getMainThreadHandler();

        // Listen for tracked uid being uninstalled
        context.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
    }

    /**
@@ -131,6 +161,15 @@ public class OneTimePermissionUserManager {
        }
    }

    /**
     * The delay to wait before revoking on the event an app is terminated. Recommended to be long
     * enough so that apps don't lose permission on an immediate restart
     */
    private static long getKilledDelayMillis() {
        return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
                PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS);
    }

    /**
     * A class which watches a package for inactivity and notifies the permission controller when
     * the package becomes inactive
@@ -155,16 +194,15 @@ public class OneTimePermissionUserManager {
        private final ActivityManager.OnUidImportanceListener mGoneListener;

        private final Object mInnerLock = new Object();
        private final Object mToken = new Object();

        private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
                int importanceToResetTimer, int importanceToKeepSessionAlive) {

            if (DEBUG) {
                Log.d(LOG_TAG,
            Log.i(LOG_TAG,
                    "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
                            + " importanceToResetTimer=" + importanceToResetTimer
                            + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
            }

            mUid = uid;
            mPackageName = packageName;
@@ -193,18 +231,34 @@ public class OneTimePermissionUserManager {
                return;
            }


            if (DEBUG) {
                Log.d(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
            Log.v(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
                    + " importance=" + importance);
            }
            synchronized (mInnerLock) {
                // Remove any pending inactivity callback
                mHandler.removeCallbacksAndMessages(mToken);

                if (importance > IMPORTANCE_CACHED) {
                    // Delay revocation in case app is restarting
                    mHandler.postDelayed(() -> {
                        int imp = mActivityManager.getUidImportance(mUid);
                        if (imp > IMPORTANCE_CACHED) {
                            onPackageInactiveLocked();
                        } else {
                            if (DEBUG) {
                                Log.d(LOG_TAG, "No longer gone after delayed revocation. "
                                        + "Rechecking for " + mPackageName + " (" + mUid + ").");
                            }
                            onImportanceChanged(mUid, imp);
                        }
                    }, mToken, getKilledDelayMillis());
                    return;
                }
                if (importance > mImportanceToResetTimer) {
                    if (mTimerStart == TIMER_INACTIVE) {
                        if (DEBUG) {
                            Log.d(LOG_TAG, "Start the timer for "
                                    + mPackageName + " (" + mUid + ").");
                        }
                        mTimerStart = System.currentTimeMillis();
                    }
                } else {
@@ -240,10 +294,13 @@ public class OneTimePermissionUserManager {
                return;
            }

            if (DEBUG) {
                Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ").");
            }
            long revokeTime = mTimerStart + mTimeout;
            if (revokeTime > System.currentTimeMillis()) {
                mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, revokeTime, LOG_TAG, this,
                        mContext.getMainThreadHandler());
                        mHandler);
                mIsAlarmSet = true;
            } else {
                mIsAlarmSet = true;
@@ -257,6 +314,9 @@ public class OneTimePermissionUserManager {
        @GuardedBy("mInnerLock")
        private void cancelAlarmLocked() {
            if (mIsAlarmSet) {
                if (DEBUG) {
                    Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ").");
                }
                mAlarmManager.cancel(this);
                mIsAlarmSet = false;
            }
@@ -270,14 +330,16 @@ public class OneTimePermissionUserManager {
            if (mIsFinished) {
                return;
            }
            if (DEBUG) {
                Log.d(LOG_TAG, "onPackageInactiveLocked stack trace for "
                        + mPackageName + " (" + mUid + ").", new RuntimeException());
            }
            mIsFinished = true;
            cancelAlarmLocked();
            mContext.getMainThreadHandler().post(
            mHandler.post(
                    () -> {
                        if (DEBUG) {
                            Log.d(LOG_TAG, "One time session expired for "
                        Log.i(LOG_TAG, "One time session expired for "
                                + mPackageName + " (" + mUid + ").");
                        }

                        mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
                                mPackageName);
@@ -292,6 +354,9 @@ public class OneTimePermissionUserManager {

        @Override
        public void onAlarm() {
            if (DEBUG) {
                Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ").");
            }
            synchronized (mInnerLock) {
                if (!mIsAlarmSet) {
                    return;