diff options
| author | 2019-12-19 22:04:40 +0000 | |
|---|---|---|
| committer | 2019-12-19 22:04:40 +0000 | |
| commit | 7f1ce8f9035ddb09a820f286b3ca6776587d8db7 (patch) | |
| tree | fb43feaa634c5de3996e4de26462a3b2d6b229c7 | |
| parent | 34824d8167c00adfc30d63e7a4a1dcbb5238ace4 (diff) | |
| parent | b252d8bdbf378065ff209cb2135b3324136d226f (diff) | |
Merge "Create system server component for one-time permissions"
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; |