Create system server component for one-time permissions

In this change we introduce new system api to manage tracking apps for
inactivity when they hold one-time permissions. The api includes adding
a package, removing a package, and a callback to notify the app has gone
inactive and which permissions are considered one-time.

Also introduce a new permission flag so that it is possible to determine
if a currently granted permission is one-time.

Test: Manual
Bug: 136219229
Change-Id: Iac3cb776a0204c64953f0a03abe76c8e320c9e56
This commit is contained in:
Evan Severson
2019-11-20 08:41:33 -08:00
parent 2808564d7c
commit b252d8bdbf
9 changed files with 459 additions and 3 deletions

View File

@@ -2052,6 +2052,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
@@ -2126,7 +2127,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 {
@@ -6939,6 +6940,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);
@@ -6955,6 +6957,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 {

View File

@@ -3252,6 +3252,15 @@ public abstract class PackageManager {
@TestApi
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.
@@ -3293,7 +3302,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.
@@ -4073,7 +4083,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 {}
@@ -7174,6 +7185,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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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);
}
};
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;