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:
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user