|
|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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, int importanceToResetTimer,
|
|
|
|
|
int importanceToKeepSessionAlive) {
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mActivityManager.addOnUidImportanceListener(mStartTimerListener,
|
|
|
|
|
importanceToResetTimer);
|
|
|
|
|
mActivityManager.addOnUidImportanceListener(mSessionKillableListener,
|
|
|
|
|
importanceToKeepSessionAlive);
|
|
|
|
|
mActivityManager.addOnUidImportanceListener(mGoneListener, IMPORTANCE_CACHED);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Log.v(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
|
|
|
|
|
+ " importance=" + importance);
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
onPackageInactiveLocked();
|
|
|
|
|
} else {
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
Log.d(LOG_TAG, "No longer gone after delayed revocation. "
|
|
|
|
|
+ "Rechecking for " + mPackageName + " (" + mUid + ").");
|
|
|
|
|
int currentState;
|
|
|
|
|
synchronized (mInnerLock) {
|
|
|
|
|
currentState = getCurrentState();
|
|
|
|
|
if (currentState == STATE_GONE) {
|
|
|
|
|
onPackageInactiveLocked();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
onImportanceChanged(mUid, imp);
|
|
|
|
|
}
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
Log.d(LOG_TAG, "No longer gone after delayed revocation. "
|
|
|
|
|
+ "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);
|
|
|
|
|
}
|
|
|
|
|
|