Merge "Exclude only system and device intelligence roles from indicators" into sc-dev
This commit is contained in:
@@ -35,6 +35,7 @@ import android.app.IActivityManager;
|
||||
import android.app.PropertyInvalidatedCache;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledAfter;
|
||||
import android.content.AttributionSource;
|
||||
import android.content.Context;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -42,9 +43,7 @@ import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.PermissionGroupInfo;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.permission.SplitPermissionInfoParcelable;
|
||||
import android.location.LocationManager;
|
||||
import android.media.AudioManager;
|
||||
import android.content.AttributionSource;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -52,8 +51,8 @@ import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DebugUtils;
|
||||
@@ -103,6 +102,20 @@ public final class PermissionManager {
|
||||
@EnabledAfter(targetSdkVersion = S)
|
||||
public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
|
||||
|
||||
/**
|
||||
* The time to wait in between refreshing the exempted indicator role packages
|
||||
*/
|
||||
private static final long EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS = 15000;
|
||||
|
||||
private static long sLastIndicatorUpdateTime = -1;
|
||||
|
||||
private static final int[] EXEMPTED_ROLES = {R.string.config_systemAmbientAudioIntelligence,
|
||||
R.string.config_systemUiIntelligence, R.string.config_systemAudioIntelligence,
|
||||
R.string.config_systemNotificationIntelligence, R.string.config_systemTextIntelligence,
|
||||
R.string.config_systemVisualIntelligence};
|
||||
|
||||
private static final String[] INDICATOR_EXEMPTED_PACKAGES = new String[EXEMPTED_ROLES.length];
|
||||
|
||||
/**
|
||||
* Note: Changing this won't do anything on its own - you should also change the filtering in
|
||||
* {@link #shouldTraceGrant}.
|
||||
@@ -873,21 +886,31 @@ public final class PermissionManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this package/op combination is exempted from indicators
|
||||
* @return
|
||||
* Determine if a package should be shown in indicators. Only a select few roles, and the
|
||||
* system app itself, are hidden. These values are updated at most every 15 seconds.
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isSpecialCaseShownIndicator(@NonNull Context context,
|
||||
public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
|
||||
@NonNull String packageName) {
|
||||
|
||||
if (packageName.equals(SYSTEM_PKG)) {
|
||||
if (SYSTEM_PKG.equals(packageName)) {
|
||||
return false;
|
||||
}
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
if (sLastIndicatorUpdateTime == -1
|
||||
|| (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
|
||||
sLastIndicatorUpdateTime = now;
|
||||
for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
|
||||
INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
|
||||
String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
|
||||
if (exemptedPackage != null && exemptedPackage.equals(packageName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
|
||||
false)
|
||||
|| packageName.equals(context.getString(R.string.config_systemSpeechRecognizer))
|
||||
|| context.getSystemService(LocationManager.class).isProviderPackage(packageName);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Gets the list of packages that have permissions that specified
|
||||
|
||||
@@ -26,8 +26,6 @@ import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA;
|
||||
import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
|
||||
import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
|
||||
import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
|
||||
import static android.app.AppOpsManager.opToPermission;
|
||||
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
|
||||
import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
|
||||
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
|
||||
|
||||
@@ -284,10 +282,6 @@ public class PermissionUsageHelper {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!shouldShowPermissionsHub() && !isUserSensitive(packageName, user, op)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isRunning = attrOpEntry.isRunning()
|
||||
|| lastAccessTime >= runningThreshold;
|
||||
|
||||
@@ -375,7 +369,7 @@ public class PermissionUsageHelper {
|
||||
// for it's uid and package name, save it.
|
||||
int usageId = usage.getPackageIdHash();
|
||||
OpUsage lastMostRecent = mostRecentUsages.get(usageId);
|
||||
if (!usage.packageName.equals(SYSTEM_PKG) && (lastMostRecent == null
|
||||
if (shouldShowPackage(usage.packageName) && (lastMostRecent == null
|
||||
|| usage.lastAccessTime > lastMostRecent.lastAccessTime)) {
|
||||
mostRecentUsages.put(usageId, usage);
|
||||
}
|
||||
@@ -401,8 +395,7 @@ public class PermissionUsageHelper {
|
||||
// We are missing the proxy usage. This may be because it's a one-step trusted
|
||||
// proxy. Check if we should show the proxy label, and show it, if so.
|
||||
OpUsage proxy = currentUsage.proxy;
|
||||
if (PermissionManager.isSpecialCaseShownIndicator(mContext, proxy.packageName)
|
||||
|| isUserSensitive(proxy.packageName, proxy.getUser(), proxy.op)) {
|
||||
if (shouldShowPackage(proxy.packageName)) {
|
||||
currentUsage = proxy;
|
||||
// We've effectively added one usage, so increment the max number of usages
|
||||
maxUsages++;
|
||||
@@ -411,7 +404,6 @@ public class PermissionUsageHelper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentUsage == null || iterNum == maxUsages
|
||||
|| currentUsage.getPackageIdHash() == start.getPackageIdHash()) {
|
||||
// We have an invalid state, or a cycle, so break
|
||||
@@ -421,7 +413,7 @@ public class PermissionUsageHelper {
|
||||
proxyPackages.add(currentUsage.getPackageIdHash());
|
||||
// Don't add an app label for the main app, or the system app
|
||||
if (!currentUsage.packageName.equals(start.packageName)
|
||||
&& !currentUsage.packageName.equals(SYSTEM_PKG)) {
|
||||
&& shouldShowPackage(currentUsage.packageName)) {
|
||||
try {
|
||||
PackageManager userPkgManager =
|
||||
getUserContext(currentUsage.getUser()).getPackageManager();
|
||||
@@ -451,17 +443,8 @@ public class PermissionUsageHelper {
|
||||
return usagesAndLabels;
|
||||
}
|
||||
|
||||
private boolean isUserSensitive(String packageName, UserHandle user, String op) {
|
||||
if (op.equals(OPSTR_PHONE_CALL_CAMERA) || op.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (opToPermission(op) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int permFlags = mPkgManager.getPermissionFlags(opToPermission(op), packageName, user);
|
||||
return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
|
||||
private boolean shouldShowPackage(String packageName) {
|
||||
return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,24 +25,20 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.LocationManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecordingConfiguration;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.permission.PermissionManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
@@ -83,19 +79,12 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
private final Context mContext;
|
||||
private final AppOpsManager mAppOps;
|
||||
private final AudioManager mAudioManager;
|
||||
private final LocationManager mLocationManager;
|
||||
private final IndividualSensorPrivacyController mSensorPrivacyController;
|
||||
private final SystemClock mClock;
|
||||
|
||||
// mLocationProviderPackages are cached and updated only occasionally
|
||||
private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
|
||||
private long mLastLocationProviderPackageUpdate;
|
||||
private List<String> mLocationProviderPackages;
|
||||
|
||||
private H mBGHandler;
|
||||
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
|
||||
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
|
||||
private final PermissionFlagsCache mFlagsCache;
|
||||
private boolean mListening;
|
||||
private boolean mMicMuted;
|
||||
private boolean mCameraDisabled;
|
||||
@@ -124,7 +113,6 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
Context context,
|
||||
@Background Looper bgLooper,
|
||||
DumpManager dumpManager,
|
||||
PermissionFlagsCache cache,
|
||||
AudioManager audioManager,
|
||||
IndividualSensorPrivacyController sensorPrivacyController,
|
||||
BroadcastDispatcher dispatcher,
|
||||
@@ -132,7 +120,6 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
) {
|
||||
mDispatcher = dispatcher;
|
||||
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mFlagsCache = cache;
|
||||
mBGHandler = new H(bgLooper);
|
||||
final int numOps = OPS.length;
|
||||
for (int i = 0; i < numOps; i++) {
|
||||
@@ -143,7 +130,6 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
mMicMuted = audioManager.isMicrophoneMute()
|
||||
|| mSensorPrivacyController.isSensorBlocked(MICROPHONE);
|
||||
mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
|
||||
mLocationManager = context.getSystemService(LocationManager.class);
|
||||
mContext = context;
|
||||
mClock = clock;
|
||||
dumpManager.registerDumpable(TAG, this);
|
||||
@@ -308,102 +294,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
return createdNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the app-op code refer to a user sensitive permission for the specified user id
|
||||
* and package. Only user sensitive permission should be shown to the user by default.
|
||||
*
|
||||
* @param appOpCode The code of the app-op.
|
||||
* @param uid The uid of the user.
|
||||
* @param packageName The name of the package.
|
||||
*
|
||||
* @return {@code true} iff the app-op item is user sensitive
|
||||
*/
|
||||
private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
|
||||
String permission = AppOpsManager.opToPermission(appOpCode);
|
||||
if (permission == null) {
|
||||
return false;
|
||||
}
|
||||
int permFlags = mFlagsCache.getPermissionFlags(permission,
|
||||
packageName, uid);
|
||||
return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the app-op item refer to an operation that should be shown to the user.
|
||||
* Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
|
||||
* permission should be shown to the user by default.
|
||||
*
|
||||
* @param item The item
|
||||
*
|
||||
* @return {@code true} iff the app-op item should be shown to the user
|
||||
*/
|
||||
private boolean isUserVisible(AppOpItem item) {
|
||||
return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a package is the current location provider.
|
||||
*
|
||||
* <p>Data is cached to avoid too many calls into system server
|
||||
*
|
||||
* @param packageName The package that might be the location provider
|
||||
*
|
||||
* @return {@code true} iff the package is the location provider.
|
||||
*/
|
||||
private boolean isLocationProvider(String packageName) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (mLastLocationProviderPackageUpdate + LOCATION_PROVIDER_UPDATE_FREQUENCY_MS < now) {
|
||||
mLastLocationProviderPackageUpdate = now;
|
||||
mLocationProviderPackages = mLocationManager.getProviderPackages(
|
||||
LocationManager.FUSED_PROVIDER);
|
||||
}
|
||||
|
||||
return mLocationProviderPackages.contains(packageName);
|
||||
}
|
||||
|
||||
private boolean isSpeechRecognizerUsage(int opCode, String packageName) {
|
||||
if (AppOpsManager.OP_RECORD_AUDIO != opCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return packageName.equals(
|
||||
mContext.getString(R.string.config_systemSpeechRecognizer));
|
||||
}
|
||||
|
||||
// TODO ntmyren: remove after teamfood is finished
|
||||
private boolean showSystemApps() {
|
||||
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the app-op, uid and package name, refer to an operation that should be shown to the
|
||||
* user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
|
||||
* ops that refer to user sensitive permission should be shown to the user by default.
|
||||
*
|
||||
* @param item The item
|
||||
*
|
||||
* @return {@code true} iff the app-op for should be shown to the user
|
||||
*/
|
||||
private boolean isUserVisible(int appOpCode, int uid, String packageName) {
|
||||
// currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
|
||||
// does not correspond to a platform permission
|
||||
// which may be user sensitive, so for now always show it to the user.
|
||||
if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
|
||||
|| appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION
|
||||
|| appOpCode == AppOpsManager.OP_PHONE_CALL_CAMERA
|
||||
|| appOpCode == AppOpsManager.OP_PHONE_CALL_MICROPHONE) {
|
||||
return true;
|
||||
}
|
||||
// TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
|
||||
if (((showSystemApps() && !packageName.equals("android"))
|
||||
|| appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
|
||||
|| isSpeechRecognizerUsage(appOpCode, packageName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isUserSensitive(appOpCode, uid, packageName);
|
||||
private boolean isUserVisible(String packageName) {
|
||||
return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -438,7 +330,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
AppOpItem item = mActiveItems.get(i);
|
||||
if ((userId == UserHandle.USER_ALL
|
||||
|| UserHandle.getUserId(item.getUid()) == userId)
|
||||
&& isUserVisible(item) && !item.isDisabled()) {
|
||||
&& isUserVisible(item.getPackageName()) && !item.isDisabled()) {
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
@@ -449,7 +341,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
AppOpItem item = mNotedItems.get(i);
|
||||
if ((userId == UserHandle.USER_ALL
|
||||
|| UserHandle.getUserId(item.getUid()) == userId)
|
||||
&& isUserVisible(item)) {
|
||||
&& isUserVisible(item.getPackageName())) {
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
@@ -503,7 +395,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
||||
}
|
||||
|
||||
private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) {
|
||||
if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
|
||||
if (mCallbacksByCode.contains(code) && isUserVisible(packageName)) {
|
||||
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
|
||||
for (Callback cb: mCallbacksByCode.get(code)) {
|
||||
cb.onActiveStateChanged(code, uid, packageName, active);
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.systemui.appops
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.UserHandle
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.util.Assert
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
private data class PermissionFlagKey(
|
||||
val permission: String,
|
||||
val packageName: String,
|
||||
val uid: Int
|
||||
)
|
||||
|
||||
/**
|
||||
* Cache for PackageManager's PermissionFlags.
|
||||
*
|
||||
* After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
|
||||
* and changes to the uid will trigger new requests (in the background).
|
||||
*/
|
||||
@SysUISingleton
|
||||
class PermissionFlagsCache @Inject constructor(
|
||||
private val packageManager: PackageManager,
|
||||
@Background private val executor: Executor
|
||||
) : PackageManager.OnPermissionsChangedListener {
|
||||
|
||||
private val permissionFlagsCache =
|
||||
mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
|
||||
private var listening = false
|
||||
|
||||
override fun onPermissionsChanged(uid: Int) {
|
||||
executor.execute {
|
||||
// Only track those that we've seen before
|
||||
val keys = permissionFlagsCache.get(uid)
|
||||
if (keys != null) {
|
||||
keys.mapValuesTo(keys) {
|
||||
getFlags(it.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve permission flags from cache or PackageManager. There parameters will be passed
|
||||
* directly to [PackageManager].
|
||||
*
|
||||
* Calls to this method should be done from a background thread (though it will only be
|
||||
* enforced if the cache is not hit).
|
||||
*/
|
||||
@WorkerThread
|
||||
fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
|
||||
if (!listening) {
|
||||
listening = true
|
||||
packageManager.addOnPermissionsChangeListener(this)
|
||||
}
|
||||
val key = PermissionFlagKey(permission, packageName, uid)
|
||||
return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
|
||||
getFlags(key).also {
|
||||
Assert.isNotMainThread()
|
||||
permissionFlagsCache.get(uid)?.put(key, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFlags(key: PermissionFlagKey): Int {
|
||||
return packageManager.getPermissionFlags(key.permission, key.packageName,
|
||||
UserHandle.getUserHandleForUid(key.uid))
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -49,6 +50,7 @@ import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
@@ -71,6 +73,7 @@ import java.util.List;
|
||||
public class AppOpsControllerTest extends SysuiTestCase {
|
||||
private static final String TEST_PACKAGE_NAME = "test";
|
||||
private static final String TEST_ATTRIBUTION_NAME = "attribution";
|
||||
private static final String SYSTEM_PKG = "android";
|
||||
private static final int TEST_UID = UserHandle.getUid(0, 0);
|
||||
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
|
||||
private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
|
||||
@@ -84,8 +87,6 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private DumpManager mDumpManager;
|
||||
@Mock
|
||||
private PermissionFlagsCache mFlagsCache;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private IndividualSensorPrivacyController mSensorPrivacyController;
|
||||
@@ -101,22 +102,19 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
||||
private AppOpsControllerImpl mController;
|
||||
private TestableLooper mTestableLooper;
|
||||
|
||||
private String mExemptedRolePkgName;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mTestableLooper = TestableLooper.get(this);
|
||||
mExemptedRolePkgName = getContext().getString(R.string.config_systemUiIntelligence);
|
||||
|
||||
getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
|
||||
|
||||
// All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
|
||||
// TEST_UID_NON_USER_SENSITIVE are user sensitive.
|
||||
getContext().setMockPackageManager(mPackageManager);
|
||||
when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
|
||||
PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
|
||||
when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
|
||||
.thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
|
||||
when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
|
||||
eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
|
||||
|
||||
doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0))
|
||||
.when(mAudioManager).registerAudioRecordingCallback(any(), any());
|
||||
@@ -135,7 +133,6 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
||||
mContext,
|
||||
mTestableLooper.getLooper(),
|
||||
mDumpManager,
|
||||
mFlagsCache,
|
||||
mAudioManager,
|
||||
mSensorPrivacyController,
|
||||
mDispatcher,
|
||||
@@ -242,18 +239,26 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonUserSensitiveOpsAreIgnored() {
|
||||
public void systemAndExemptedRolesAreIgnored() {
|
||||
assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
|
||||
|
||||
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
|
||||
TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
|
||||
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
|
||||
assertEquals(0, mController.getActiveAppOpsForUser(
|
||||
UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
|
||||
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
|
||||
TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true);
|
||||
assertEquals(0, mController.getActiveAppOpsForUser(
|
||||
UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonUserSensitiveOpsNotNotified() {
|
||||
public void exemptedRoleNotNotified() {
|
||||
assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
|
||||
|
||||
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
|
||||
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
|
||||
TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
|
||||
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
|
||||
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.systemui.appops
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.UserHandle
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class PermissionFlagsCacheTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
const val TEST_PERMISSION = "test_permission"
|
||||
const val TEST_PACKAGE = "test_package"
|
||||
const val TEST_UID1 = 1000
|
||||
const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
|
||||
}
|
||||
|
||||
@Mock
|
||||
private lateinit var packageManager: PackageManager
|
||||
|
||||
private lateinit var executor: FakeExecutor
|
||||
private lateinit var flagsCache: PermissionFlagsCache
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
executor = FakeExecutor(FakeSystemClock())
|
||||
|
||||
flagsCache = PermissionFlagsCache(packageManager, executor)
|
||||
executor.runAllReady()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotListeningByDefault() {
|
||||
verify(packageManager, never()).addOnPermissionsChangeListener(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCorrectFlags() {
|
||||
`when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
|
||||
`when`(packageManager.getPermissionFlags(
|
||||
TEST_PERMISSION,
|
||||
TEST_PACKAGE,
|
||||
UserHandle.getUserHandleForUid(TEST_UID1))
|
||||
).thenReturn(1)
|
||||
|
||||
assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
|
||||
assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFlagIsCached() {
|
||||
flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
|
||||
|
||||
flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
|
||||
|
||||
verify(packageManager, times(1)).getPermissionFlags(
|
||||
TEST_PERMISSION,
|
||||
TEST_PACKAGE,
|
||||
UserHandle.getUserHandleForUid(TEST_UID1)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListeningAfterFirstRequest() {
|
||||
flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
|
||||
|
||||
verify(packageManager).addOnPermissionsChangeListener(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListeningOnlyOnce() {
|
||||
flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
|
||||
|
||||
flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
|
||||
|
||||
verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUpdateFlag() {
|
||||
assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
|
||||
|
||||
`when`(packageManager.getPermissionFlags(
|
||||
TEST_PERMISSION,
|
||||
TEST_PACKAGE,
|
||||
UserHandle.getUserHandleForUid(TEST_UID1))
|
||||
).thenReturn(1)
|
||||
|
||||
flagsCache.onPermissionsChanged(TEST_UID1)
|
||||
|
||||
executor.runAllReady()
|
||||
|
||||
assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
|
||||
flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
|
||||
|
||||
flagsCache.onPermissionsChanged(TEST_UID2)
|
||||
|
||||
executor.runAllReady()
|
||||
|
||||
verify(packageManager, never()).getPermissionFlags(
|
||||
TEST_PERMISSION,
|
||||
TEST_PACKAGE,
|
||||
UserHandle.getUserHandleForUid(TEST_UID2)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user