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

Change-Id: I9f89877c4ad47088d7f396dd693c7da1c99c2f97
This commit is contained in:
Evan Severson
2020-04-13 18:40:57 +00:00
committed by Automerger Merge Worker

View File

@@ -21,9 +21,14 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHE
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Handler;
import android.permission.PermissionControllerManager; import android.permission.PermissionControllerManager;
import android.provider.DeviceConfig;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
@@ -36,7 +41,10 @@ public class OneTimePermissionUserManager {
private static final String LOG_TAG = OneTimePermissionUserManager.class.getSimpleName(); 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 Context mContext;
private final @NonNull ActivityManager mActivityManager; private final @NonNull ActivityManager mActivityManager;
@@ -45,15 +53,37 @@ public class OneTimePermissionUserManager {
private final Object mLock = new Object(); 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 */ /** Maps the uid to the PackageInactivityListener */
@GuardedBy("mLock") @GuardedBy("mLock")
private final SparseArray<PackageInactivityListener> mListeners = new SparseArray<>(); private final SparseArray<PackageInactivityListener> mListeners = new SparseArray<>();
private final Handler mHandler;
OneTimePermissionUserManager(@NonNull Context context) { OneTimePermissionUserManager(@NonNull Context context) {
mContext = context; mContext = context;
mActivityManager = context.getSystemService(ActivityManager.class); mActivityManager = context.getSystemService(ActivityManager.class);
mAlarmManager = context.getSystemService(AlarmManager.class); mAlarmManager = context.getSystemService(AlarmManager.class);
mPermissionControllerManager = context.getSystemService(PermissionControllerManager.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 * A class which watches a package for inactivity and notifies the permission controller when
* the package becomes inactive * the package becomes inactive
@@ -155,16 +194,15 @@ public class OneTimePermissionUserManager {
private final ActivityManager.OnUidImportanceListener mGoneListener; private final ActivityManager.OnUidImportanceListener mGoneListener;
private final Object mInnerLock = new Object(); private final Object mInnerLock = new Object();
private final Object mToken = new Object();
private PackageInactivityListener(int uid, @NonNull String packageName, long timeout, private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
int importanceToResetTimer, int importanceToKeepSessionAlive) { int importanceToResetTimer, int importanceToKeepSessionAlive) {
if (DEBUG) { Log.i(LOG_TAG,
Log.d(LOG_TAG,
"Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
+ " importanceToResetTimer=" + importanceToResetTimer + " importanceToResetTimer=" + importanceToResetTimer
+ " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive); + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
}
mUid = uid; mUid = uid;
mPackageName = packageName; mPackageName = packageName;
@@ -193,18 +231,34 @@ public class OneTimePermissionUserManager {
return; return;
} }
Log.v(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
if (DEBUG) {
Log.d(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
+ " importance=" + importance); + " importance=" + importance);
}
synchronized (mInnerLock) { synchronized (mInnerLock) {
// Remove any pending inactivity callback
mHandler.removeCallbacksAndMessages(mToken);
if (importance > IMPORTANCE_CACHED) { if (importance > IMPORTANCE_CACHED) {
// Delay revocation in case app is restarting
mHandler.postDelayed(() -> {
int imp = mActivityManager.getUidImportance(mUid);
if (imp > IMPORTANCE_CACHED) {
onPackageInactiveLocked(); onPackageInactiveLocked();
} else {
if (DEBUG) {
Log.d(LOG_TAG, "No longer gone after delayed revocation. "
+ "Rechecking for " + mPackageName + " (" + mUid + ").");
}
onImportanceChanged(mUid, imp);
}
}, mToken, getKilledDelayMillis());
return; return;
} }
if (importance > mImportanceToResetTimer) { if (importance > mImportanceToResetTimer) {
if (mTimerStart == TIMER_INACTIVE) { if (mTimerStart == TIMER_INACTIVE) {
if (DEBUG) {
Log.d(LOG_TAG, "Start the timer for "
+ mPackageName + " (" + mUid + ").");
}
mTimerStart = System.currentTimeMillis(); mTimerStart = System.currentTimeMillis();
} }
} else { } else {
@@ -240,10 +294,13 @@ public class OneTimePermissionUserManager {
return; return;
} }
if (DEBUG) {
Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ").");
}
long revokeTime = mTimerStart + mTimeout; long revokeTime = mTimerStart + mTimeout;
if (revokeTime > System.currentTimeMillis()) { if (revokeTime > System.currentTimeMillis()) {
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, revokeTime, LOG_TAG, this, mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, revokeTime, LOG_TAG, this,
mContext.getMainThreadHandler()); mHandler);
mIsAlarmSet = true; mIsAlarmSet = true;
} else { } else {
mIsAlarmSet = true; mIsAlarmSet = true;
@@ -257,6 +314,9 @@ public class OneTimePermissionUserManager {
@GuardedBy("mInnerLock") @GuardedBy("mInnerLock")
private void cancelAlarmLocked() { private void cancelAlarmLocked() {
if (mIsAlarmSet) { if (mIsAlarmSet) {
if (DEBUG) {
Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ").");
}
mAlarmManager.cancel(this); mAlarmManager.cancel(this);
mIsAlarmSet = false; mIsAlarmSet = false;
} }
@@ -270,14 +330,16 @@ public class OneTimePermissionUserManager {
if (mIsFinished) { if (mIsFinished) {
return; return;
} }
if (DEBUG) {
Log.d(LOG_TAG, "onPackageInactiveLocked stack trace for "
+ mPackageName + " (" + mUid + ").", new RuntimeException());
}
mIsFinished = true; mIsFinished = true;
cancelAlarmLocked(); cancelAlarmLocked();
mContext.getMainThreadHandler().post( mHandler.post(
() -> { () -> {
if (DEBUG) { Log.i(LOG_TAG, "One time session expired for "
Log.d(LOG_TAG, "One time session expired for "
+ mPackageName + " (" + mUid + ")."); + mPackageName + " (" + mUid + ").");
}
mPermissionControllerManager.notifyOneTimePermissionSessionTimeout( mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
mPackageName); mPackageName);
@@ -292,6 +354,9 @@ public class OneTimePermissionUserManager {
@Override @Override
public void onAlarm() { public void onAlarm() {
if (DEBUG) {
Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ").");
}
synchronized (mInnerLock) { synchronized (mInnerLock) {
if (!mIsAlarmSet) { if (!mIsAlarmSet) {
return; return;