diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 751e9aafdf094..1a40f0640a8d7 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -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 diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index b427df7256639..53ba25924d3d7 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -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); } /** diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 74f855b02a43e..994401db43801 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -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 mLocationProviderPackages; - private H mBGHandler; private final List mCallbacks = new ArrayList<>(); private final SparseArray> 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. - * - *

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); diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt deleted file mode 100644 index 3fd838be781de..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt +++ /dev/null @@ -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>() - 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)) - } -} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 8252ff48bd816..3f0831cadd1f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -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(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt deleted file mode 100644 index 0fb0ce087ee38..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt +++ /dev/null @@ -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) - ) - } -} \ No newline at end of file