device aware self revoke and one time session timeout
Bug: 283978092
Test: atest RevokeSelfPermissionTest
Test: atest OneTimePermissionTest
Test: atest DevicePermissionsTest
Change-Id: I0adef572133a288c17e43b3e1b8adcf53ff68114
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7c78f09..69ed5ae 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10635,12 +10635,14 @@
method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
- method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
+ method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
- method @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
+ method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
+ method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index e3f02e7..1cb7930 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -43,7 +43,7 @@
void setRuntimePermissionGrantStateByDeviceAdminFromParams(String callerPackageName,
in AdminPermissionControlParams params, in AndroidFuture callback);
void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
- void notifyOneTimePermissionSessionTimeout(String packageName);
+ void notifyOneTimePermissionSessionTimeout(String packageName, int deviceId);
void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
void getPrivilegesDescriptionStringForProfile(
in String deviceProfileName,
@@ -60,5 +60,5 @@
in String packageName,
in AndroidFuture callback);
void revokeSelfPermissionsOnKill(in String packageName, in List<String> permissions,
- in AndroidFuture callback);
+ int deviceId, in AndroidFuture callback);
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 18ede44d..7cecfdc 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -78,7 +78,7 @@
List<SplitPermissionInfoParcelable> getSplitPermissions();
@EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
- void startOneTimePermissionSession(String packageName, int userId, long timeout,
+ void startOneTimePermissionSession(String packageName, int deviceId, int userId, long timeout,
long revokeAfterKilledDelay);
@EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 84a197a..ba75918 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -764,13 +764,14 @@
* inactive.
*
* @param packageName The package which became inactive
- *
+ * @param deviceId The device ID refers either the primary device i.e. the phone or
+ * a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
* @hide
*/
@RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
- public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName) {
- mRemoteService.run(
- service -> service.notifyOneTimePermissionSessionTimeout(packageName));
+ public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName, int deviceId) {
+ mRemoteService.run(service -> service.notifyOneTimePermissionSessionTimeout(
+ packageName, deviceId));
}
/**
@@ -930,12 +931,14 @@
@NonNull List<String> permissions) {
mRemoteService.postAsync(service -> {
AndroidFuture<Void> callback = new AndroidFuture<>();
- service.revokeSelfPermissionsOnKill(packageName, permissions, callback);
+ service.revokeSelfPermissionsOnKill(packageName, permissions, mContext.getDeviceId(),
+ callback);
return callback;
}).whenComplete((result, err) -> {
if (err != null) {
Log.e(TAG, "Failed to self revoke " + String.join(",", permissions)
- + " for package " + packageName, err);
+ + " for package " + packageName + ", and device " + mContext.getDeviceId(),
+ err);
}
});
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 11005a6..9fe2599 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -30,6 +30,7 @@
import android.Manifest;
import android.annotation.BinderThread;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -37,6 +38,7 @@
import android.app.admin.DevicePolicyManager.PermissionGrantState;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -46,6 +48,7 @@
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.permission.PermissionControllerManager.CountPermissionAppsFlag;
+import android.permission.flags.Flags;
import android.util.ArrayMap;
import android.util.Log;
@@ -296,13 +299,32 @@
* This method is called at the end of a one-time permission session
*
* @param packageName The package that has been inactive
+ *
+ * @deprecated Implement {@link #onOneTimePermissionSessionTimeout(String, int)} instead.
*/
+ @Deprecated
@BinderThread
public void onOneTimePermissionSessionTimeout(@NonNull String packageName) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
/**
+ * Called when a package is considered inactive based on the criteria given by
+ * {@link PermissionManager#startOneTimePermissionSession(String, long, long, int, int)}.
+ * This method is called at the end of a one-time permission session
+ *
+ * @param packageName The package that has been inactive
+ * @param deviceId The device ID refers either the primary device i.e. the phone or
+ * a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
+ */
+ @BinderThread
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ public void onOneTimePermissionSessionTimeout(@NonNull String packageName,
+ int deviceId) {
+ onOneTimePermissionSessionTimeout(packageName);
+ }
+
+ /**
* Get the platform permissions which belong to a particular permission group
*
* @param permissionGroupName The permission group whose permissions are desired
@@ -341,13 +363,42 @@
* @param callback Callback waiting for operation to be complete.
*
* @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
+ *
+ * @deprecated Implement {@link #onRevokeSelfPermissionsOnKill(String, List, int, Runnable)}
+ * instead.
*/
+ @Deprecated
@BinderThread
public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions, @NonNull Runnable callback) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
+ /**
+ * Triggers the revocation of one or more permissions for a package and device.
+ * This should only be called at the request of {@code packageName}.
+ * <p>
+ * Background permissions which have no corresponding foreground permission still granted once
+ * the revocation is effective will also be revoked.
+ * <p>
+ * This revocation happens asynchronously and kills all processes running in the same UID as
+ * {@code packageName}. It will be triggered once it is safe to do so.
+ *
+ * @param packageName The name of the package for which the permissions will be revoked.
+ * @param permissions List of permissions to be revoked.
+ * @param deviceId The device ID refers either the primary device i.e. the phone or
+ * a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
+ * @param callback Callback waiting for operation to be complete.
+ *
+ * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
+ */
+ @BinderThread
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
+ @NonNull List<String> permissions, int deviceId, @NonNull Runnable callback) {
+ onRevokeSelfPermissionsOnKill(packageName, permissions, callback);
+ }
+
// TODO(b/272129940): Remove this API and device profile role description when we drop T
// support.
/**
@@ -613,12 +664,12 @@
}
@Override
- public void notifyOneTimePermissionSessionTimeout(String packageName) {
+ public void notifyOneTimePermissionSessionTimeout(String packageName, int deviceId) {
enforceSomePermissionsGrantedToCaller(
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
packageName = Preconditions.checkNotNull(packageName,
"packageName cannot be null");
- onOneTimePermissionSessionTimeout(packageName);
+ onOneTimePermissionSessionTimeout(packageName, deviceId);
}
@Override
@@ -710,7 +761,8 @@
@Override
public void revokeSelfPermissionsOnKill(@NonNull String packageName,
- @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
+ @NonNull List<String> permissions, int deviceId,
+ @NonNull AndroidFuture callback) {
try {
Objects.requireNonNull(callback);
@@ -721,7 +773,7 @@
enforceSomePermissionsGrantedToCaller(
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
}
- onRevokeSelfPermissionsOnKill(packageName, permissions,
+ onRevokeSelfPermissionsOnKill(packageName, permissions, deviceId,
() -> callback.complete(null));
} catch (Throwable t) {
callback.completeExceptionally(t);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7d39210..e10ea10 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -214,6 +214,12 @@
public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
/**
+ * Additional debug log for virtual device permissions.
+ * @hide
+ */
+ public static final boolean DEBUG_DEVICE_PERMISSIONS = false;
+
+ /**
* Intent extra: List of PermissionGroupUsages
* <p>
* Type: {@code List<PermissionGroupUsage>}
@@ -1392,8 +1398,8 @@
@ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
try {
- mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
- timeoutMillis, revokeAfterKilledDelayMillis);
+ mPermissionManager.startOneTimePermissionSession(packageName, mContext.getDeviceId(),
+ mContext.getUserId(), timeoutMillis, revokeAfterKilledDelayMillis);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 15620b7..11ae9c3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -206,6 +206,7 @@
<uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
+ <uses-permission android:name="android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
<!-- Permission required for processes that don't own the focused window to switch
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 3296c1f..150ca9b 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -91,13 +91,14 @@
mHandler = context.getMainThreadHandler();
}
- void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
+ void startPackageOneTimeSession(@NonNull String packageName, int deviceId, long timeoutMillis,
long revokeAfterKilledDelayMillis) {
int uid;
try {
uid = mContext.getPackageManager().getPackageUid(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Unknown package name " + packageName, e);
+ Log.e(LOG_TAG,
+ "Unknown package name " + packageName + ", device ID " + deviceId, e);
return;
}
@@ -107,7 +108,7 @@
listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis);
return;
}
- listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
+ listener = new PackageInactivityListener(uid, packageName, deviceId, timeoutMillis,
revokeAfterKilledDelayMillis);
mListeners.put(uid, listener);
}
@@ -159,6 +160,7 @@
private final int mUid;
private final @NonNull String mPackageName;
+ private final int mDeviceId;
private long mTimeout;
private long mRevokeAfterKilledDelay;
@@ -191,14 +193,15 @@
}
};
- private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
- long revokeAfterkilledDelay) {
+ private PackageInactivityListener(int uid, @NonNull String packageName, int deviceId,
+ long timeout, long revokeAfterkilledDelay) {
Log.i(LOG_TAG,
"Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
+ " killedDelay=" + revokeAfterkilledDelay);
mUid = uid;
mPackageName = packageName;
+ mDeviceId = deviceId;
mTimeout = timeout;
mRevokeAfterKilledDelay = revokeAfterkilledDelay == -1
? DeviceConfig.getLong(
@@ -232,7 +235,8 @@
PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
: revokeAfterKilledDelayMillis);
Log.v(LOG_TAG,
- "Updated params for " + mPackageName + ". timeout=" + mTimeout
+ "Updated params for " + mPackageName + ", device ID " + mDeviceId
+ + ". timeout=" + mTimeout
+ " killedDelay=" + mRevokeAfterKilledDelay);
updateUidState();
}
@@ -260,7 +264,7 @@
private void updateUidState(int state) {
Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")."
- + " state=" + state);
+ + " device ID=" + mDeviceId + ", state=" + state);
synchronized (mInnerLock) {
// Remove any pending inactivity callback
mHandler.removeCallbacksAndMessages(mToken);
@@ -283,7 +287,7 @@
if (DEBUG) {
Log.d(LOG_TAG, "No longer gone after delayed revocation. "
+ "Rechecking for " + mPackageName + " (" + mUid
- + ").");
+ + "). device ID " + mDeviceId);
}
updateUidState(currentState);
}, mToken, mRevokeAfterKilledDelay);
@@ -292,7 +296,7 @@
if (mTimerStart == TIMER_INACTIVE) {
if (DEBUG) {
Log.d(LOG_TAG, "Start the timer for "
- + mPackageName + " (" + mUid + ").");
+ + mPackageName + " (" + mUid + "). device ID " + mDeviceId);
}
mTimerStart = System.currentTimeMillis();
setAlarmLocked();
@@ -329,7 +333,8 @@
}
if (DEBUG) {
- Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ").");
+ Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ")."
+ + " device ID " + mDeviceId);
}
long revokeTime = mTimerStart + mTimeout;
if (revokeTime > System.currentTimeMillis()) {
@@ -349,7 +354,8 @@
private void cancelAlarmLocked() {
if (mIsAlarmSet) {
if (DEBUG) {
- Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ").");
+ Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ")."
+ + " device ID " + mDeviceId);
}
mAlarmManager.cancel(this);
mIsAlarmSet = false;
@@ -366,17 +372,17 @@
}
if (DEBUG) {
Log.d(LOG_TAG, "onPackageInactiveLocked stack trace for "
- + mPackageName + " (" + mUid + ").", new RuntimeException());
+ + mPackageName + " (" + mUid + "). device ID " + mDeviceId,
+ new RuntimeException());
}
mIsFinished = true;
cancelAlarmLocked();
mHandler.post(
() -> {
Log.i(LOG_TAG, "One time session expired for "
- + mPackageName + " (" + mUid + ").");
-
+ + mPackageName + " (" + mUid + "). deviceID " + mDeviceId);
mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
- mPackageName);
+ mPackageName, mDeviceId);
});
try {
mIActivityManager.unregisterUidObserver(mObserver);
@@ -391,7 +397,8 @@
@Override
public void onAlarm() {
if (DEBUG) {
- Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ").");
+ Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ")."
+ + " device ID " + mDeviceId);
}
synchronized (mInnerLock) {
if (!mIsAlarmSet) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 44eb28f..9610d05 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -404,15 +404,15 @@
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
@Override
- public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
- long timeoutMillis, long revokeAfterKilledDelayMillis) {
+ public void startOneTimePermissionSession(String packageName, int deviceId,
+ @UserIdInt int userId, long timeoutMillis, long revokeAfterKilledDelayMillis) {
startOneTimePermissionSession_enforcePermission();
Objects.requireNonNull(packageName);
final long token = Binder.clearCallingIdentity();
try {
getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
- timeoutMillis, revokeAfterKilledDelayMillis);
+ deviceId, timeoutMillis, revokeAfterKilledDelayMillis);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index b2733d4..240585c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,6 +16,7 @@
package com.android.server.permission.access.permission
+import android.permission.PermissionManager
import android.util.Slog
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
@@ -164,9 +165,18 @@
deviceId: String,
userId: Int,
permissionName: String
- ): Int =
- state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
- ?.getWithDefault(permissionName, 0) ?: 0
+ ): Int {
+ val flags = state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
+ ?.getWithDefault(permissionName, 0) ?: 0
+ if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
+ Slog.i(
+ LOG_TAG, "getPermissionFlags: appId=$appId, userId=$userId," +
+ " deviceId=$deviceId, permissionName=$permissionName," +
+ " flags=${PermissionFlags.toString(flags)}"
+ )
+ }
+ return flags
+ }
fun MutateStateScope.setPermissionFlags(
appId: Int,
@@ -202,6 +212,13 @@
val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
MutableIndexedReferenceMap()
}
+ if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
+ Slog.i(
+ LOG_TAG, "setPermissionFlags(): appId=$appId, userId=$userId," +
+ " deviceId=$deviceId, permissionName=$permissionName," +
+ " newFlags=${PermissionFlags.toString(newFlags)}"
+ )
+ }
val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
permissionFlags.putWithDefault(permissionName, newFlags, 0)
if (permissionFlags.isEmpty()) {