summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapi/system-current.txt6
-rw-r--r--core/java/android/content/pm/PackageManager.java16
-rw-r--r--core/java/android/permission/IPermissionController.aidl1
-rw-r--r--core/java/android/permission/IPermissionManager.aidl6
-rw-r--r--core/java/android/permission/PermissionControllerManager.java14
-rw-r--r--core/java/android/permission/PermissionControllerService.java21
-rw-r--r--core/java/android/permission/PermissionManager.java66
-rw-r--r--services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java281
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java51
9 files changed, 459 insertions, 3 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 2cb77b99c027..bd16463c5f94 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2088,6 +2088,7 @@ package android.content.pm {
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
+ field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800
field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
@@ -2162,7 +2163,7 @@ package android.content.pm {
method public void onPermissionsChanged(int);
}
- @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -7016,6 +7017,7 @@ package android.permission {
method @BinderThread public abstract void onGetPermissionUsages(boolean, long, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionUsageInfo>>);
method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
+ method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
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);
@@ -7032,6 +7034,8 @@ package android.permission {
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+ method @RequiresPermission(allOf={android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void startOneTimePermissionSession(@NonNull String, long, int, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void stopOneTimePermissionSession(@NonNull String);
}
public static final class PermissionManager.SplitPermissionInfo {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 21c9739dd1ee..ee27f4f8345f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3243,6 +3243,15 @@ public abstract class PackageManager {
public static final int FLAG_PERMISSION_REVOKED_COMPAT = FLAG_PERMISSION_REVOKE_ON_UPGRADE;
/**
+ * Permission flag: The permission is one-time and should be revoked automatically on app
+ * inactivity
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
+
+ /**
* Permission flags: Bitwise or of all permission flags allowing an
* exemption for a restricted permission.
* @hide
@@ -3283,7 +3292,8 @@ public abstract class PackageManager {
| FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
| FLAG_PERMISSION_APPLY_RESTRICTION
| FLAG_PERMISSION_GRANTED_BY_ROLE
- | FLAG_PERMISSION_REVOKED_COMPAT;
+ | FLAG_PERMISSION_REVOKED_COMPAT
+ | FLAG_PERMISSION_ONE_TIME;
/**
* Injected activity in app that forwards user to setting activity of that app.
@@ -4063,7 +4073,8 @@ public abstract class PackageManager {
FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT,
FLAG_PERMISSION_APPLY_RESTRICTION,
FLAG_PERMISSION_GRANTED_BY_ROLE,
- FLAG_PERMISSION_REVOKED_COMPAT
+ FLAG_PERMISSION_REVOKED_COMPAT,
+ FLAG_PERMISSION_ONE_TIME
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionFlags {}
@@ -7164,6 +7175,7 @@ public abstract class PackageManager {
case FLAG_PERMISSION_APPLY_RESTRICTION: return "APPLY_RESTRICTION";
case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
+ case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
default: return Integer.toString(flag);
}
}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 26c1ec170834..0483514e6297 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -43,4 +43,5 @@ oneway interface IPermissionController {
String permission, int grantState, in AndroidFuture callback);
void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
void updateUserSensitive(in AndroidFuture callback);
+ void notifyOneTimePermissionSessionTimeout(String packageName);
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 60c88116f804..2615c98a6d5e 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -20,6 +20,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.os.UserHandle;
import android.permission.IOnPermissionsChangeListener;
/**
@@ -100,4 +101,9 @@ interface IPermissionManager {
boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId);
List<SplitPermissionInfoParcelable> getSplitPermissions();
+
+ void startOneTimePermissionSession(String packageName, int userId, long timeout,
+ int importanceToResetTimer, int importanceToKeepSessionAlive);
+
+ void stopOneTimePermissionSession(String packageName, int userId);
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 421e29ed0542..2a1857fd0027 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -636,4 +636,18 @@ public final class PermissionControllerManager {
return future;
});
}
+
+ /**
+ * Called when a package that has permissions registered as "one-time" is considered
+ * inactive.
+ *
+ * @param packageName The package which became inactive
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+ public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName) {
+ mRemoteService.run(
+ service -> service.notifyOneTimePermissionSessionTimeout(packageName));
+ }
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index f9146637c8cb..5d4561c21e55 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -240,6 +240,18 @@ public abstract class PermissionControllerService extends Service {
@NonNull String permission, @PermissionGrantState int grantState,
@NonNull Consumer<Boolean> callback);
+ /**
+ * Called when a package is considered inactive based on the criteria given by
+ * {@link PermissionManager#startOneTimePermissionSession(String, long, int, int)}.
+ * This method is called at the end of a one-time permission session
+ *
+ * @param packageName The package that has been inactive
+ */
+ @BinderThread
+ public void onOneTimePermissionSessionTimeout(@NonNull String packageName) {
+ throw new AbstractMethodError("Must be overridden in implementing class");
+ }
+
@Override
public final @NonNull IBinder onBind(Intent intent) {
return new IPermissionController.Stub() {
@@ -452,6 +464,15 @@ public abstract class PermissionControllerService extends Service {
onUpdateUserSensitivePermissionFlags();
callback.complete(null);
}
+
+ @Override
+ public void notifyOneTimePermissionSessionTimeout(String packageName) {
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+ packageName = Preconditions.checkNotNull(packageName,
+ "packageName cannot be null");
+ onOneTimePermissionSessionTimeout(packageName);
+ }
};
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 09286feee6b9..a3215a47fd62 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -25,6 +25,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.app.ActivityManager;
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.IPackageManager;
@@ -284,4 +285,69 @@ public final class PermissionManager {
mSplitPermissionInfoParcelable = parcelable;
}
}
+
+ /**
+ * 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
+ * {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} 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 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
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ Manifest.permission.PACKAGE_USAGE_STATS})
+ public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis,
+ @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
+ @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
+ try {
+ mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
+ timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stops the one-time permission session for the package. The callback to the end of session is
+ * not invoked. If there is no one-time session for the package then nothing happens.
+ *
+ * @param packageName Package to stop the one-time permission session for
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ Manifest.permission.PACKAGE_USAGE_STATS})
+ public void stopOneTimePermissionSession(@NonNull String packageName) {
+ try {
+ mPermissionManager.stopOneTimePermissionSession(packageName,
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
new file mode 100644
index 000000000000..b809951947d9
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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.content.Context;
+import android.content.pm.PackageManager;
+import android.permission.PermissionControllerManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Class that handles one-time permissions for a user
+ */
+public class OneTimePermissionUserManager {
+
+ private static final String LOG_TAG = OneTimePermissionUserManager.class.getSimpleName();
+
+ private final @NonNull Context mContext;
+ private final @NonNull ActivityManager mActivityManager;
+ private final @NonNull AlarmManager mAlarmManager;
+ private final @NonNull PermissionControllerManager mPermissionControllerManager;
+
+ private final Object mLock = new Object();
+
+ /** Maps the uid to the PackageInactivityListener */
+ @GuardedBy("mLock")
+ private final SparseArray<PackageInactivityListener> mListeners = new SparseArray<>();
+
+ OneTimePermissionUserManager(@NonNull Context context) {
+ mContext = context;
+ mActivityManager = context.getSystemService(ActivityManager.class);
+ mAlarmManager = context.getSystemService(AlarmManager.class);
+ mPermissionControllerManager = context.getSystemService(PermissionControllerManager.class);
+ }
+
+ /**
+ * 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 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,
+ int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ int uid;
+ try {
+ uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Unknown package name " + packageName, e);
+ return;
+ }
+
+ synchronized (mLock) {
+ PackageInactivityListener listener = mListeners.get(uid);
+ if (listener == null) {
+ listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
+ importanceToResetTimer, importanceToKeepSessionAlive);
+ mListeners.put(uid, listener);
+ }
+ }
+ }
+
+ /**
+ * Stops the one-time permission session for the package. The callback to the end of session is
+ * not invoked. If there is no one-time session for the package then nothing happens.
+ *
+ * @param packageName Package to stop the one-time permission session for
+ */
+ void stopPackageOneTimeSession(@NonNull String packageName) {
+ int uid;
+ try {
+ uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Unknown package name " + packageName, e);
+ return;
+ }
+
+ synchronized (mLock) {
+ PackageInactivityListener listener = mListeners.get(uid);
+ if (listener != null) {
+ mListeners.remove(uid);
+ listener.cancel();
+ }
+ }
+ }
+
+ /**
+ * A class which watches a package for inactivity and notifies the permission controller when
+ * the package becomes inactive
+ */
+ private class PackageInactivityListener implements AlarmManager.OnAlarmListener {
+
+ private static final long TIMER_INACTIVE = -1;
+
+ private final int mUid;
+ private final @NonNull String mPackageName;
+ private final long mTimeout;
+ private final int mImportanceToResetTimer;
+ private final 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 PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
+ int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ mUid = uid;
+ mPackageName = packageName;
+ mTimeout = timeout;
+ 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);
+
+ onImportanceChanged(mUid, mActivityManager.getPackageImportance(packageName));
+ }
+
+ private void onImportanceChanged(int uid, int importance) {
+ if (uid != mUid) {
+ return;
+ }
+ synchronized (mInnerLock) {
+ if (importance > IMPORTANCE_CACHED) {
+ onPackageInactiveLocked();
+ return;
+ }
+ if (importance > mImportanceToResetTimer) {
+ if (mTimerStart == TIMER_INACTIVE) {
+ mTimerStart = System.currentTimeMillis();
+ }
+ } else {
+ mTimerStart = TIMER_INACTIVE;
+ }
+ if (importance > mImportanceToKeepSessionAlive) {
+ setAlarmLocked();
+ } else {
+ cancelAlarmLocked();
+ }
+ }
+ }
+
+ /**
+ * Stop watching the package for inactivity
+ */
+ private void cancel() {
+ synchronized (mInnerLock) {
+ mIsFinished = true;
+ cancelAlarmLocked();
+ mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
+ mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
+ mActivityManager.removeOnUidImportanceListener(mGoneListener);
+ }
+ }
+
+ /**
+ * Set the alarm which will callback when the package is inactive
+ */
+ @GuardedBy("mInnerLock")
+ private void setAlarmLocked() {
+ if (mIsAlarmSet) {
+ return;
+ }
+
+ long revokeTime = mTimerStart + mTimeout;
+ if (revokeTime > System.currentTimeMillis()) {
+ mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, revokeTime, LOG_TAG, this,
+ mContext.getMainThreadHandler());
+ mIsAlarmSet = true;
+ } else {
+ mIsAlarmSet = true;
+ onAlarm();
+ }
+ }
+
+ /**
+ * Cancel the alarm
+ */
+ @GuardedBy("mInnerLock")
+ private void cancelAlarmLocked() {
+ if (mIsAlarmSet) {
+ mAlarmManager.cancel(this);
+ mIsAlarmSet = false;
+ }
+ }
+
+ /**
+ * Called when the package is considered inactive. This is the end of the session
+ */
+ @GuardedBy("mInnerLock")
+ private void onPackageInactiveLocked() {
+ if (mIsFinished) {
+ return;
+ }
+ mIsFinished = true;
+ cancelAlarmLocked();
+ mContext.getMainThreadHandler().post(
+ () -> mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
+ mPackageName));
+ mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
+ mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
+ mActivityManager.removeOnUidImportanceListener(mGoneListener);
+ synchronized (mLock) {
+ mListeners.remove(mUid);
+ }
+ }
+
+ @Override
+ public void onAlarm() {
+ synchronized (mInnerLock) {
+ if (!mIsAlarmSet) {
+ return;
+ }
+ mIsAlarmSet = false;
+ onPackageInactiveLocked();
+ }
+ }
+ }
+}
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 8ce1a52afd0c..7580a3001901 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -204,6 +204,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
/** Permission controller: User space permission management */
private PermissionControllerManager mPermissionControllerManager;
+ /** Map of OneTimePermissionUserManagers keyed by userId */
+ private final SparseArray<OneTimePermissionUserManager> mOneTimePermissionUserManagers =
+ new SparseArray<>();
+
/** Default permission policy to provide proper behaviour out-of-the-box */
private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
@@ -3009,6 +3013,53 @@ public class PermissionManagerService extends IPermissionManager.Stub {
SystemConfig.getInstance().getSplitPermissions());
}
+ private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) {
+ synchronized (mLock) {
+ OneTimePermissionUserManager oneTimePermissionUserManager =
+ mOneTimePermissionUserManagers.get(userId);
+ if (oneTimePermissionUserManager == null) {
+ oneTimePermissionUserManager = new OneTimePermissionUserManager(
+ mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0));
+ mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager);
+ }
+ return oneTimePermissionUserManager;
+ }
+ }
+
+ @Override
+ public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
+ long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ mContext.enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ "Must be able to revoke runtime permissions to register permissions as one time.");
+ mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
+ "Must be able to access usage stats to register permissions as one time.");
+ packageName = Preconditions.checkNotNull(packageName);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
+ timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void stopOneTimePermissionSession(String packageName, @UserIdInt int userId) {
+ mContext.enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ "Must be able to revoke runtime permissions to remove permissions as one time.");
+ mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
+ "Must be able to access usage stats to remove permissions as one time.");
+ Preconditions.checkNotNull(packageName);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ getOneTimePermissionUserManager(userId).stopPackageOneTimeSession(packageName);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
boolean allowed = false;
final int NP = PackageParser.NEW_PERMISSIONS.length;