From 5c837108e2509105b8e6d2aabedbbecca8dfc1c6 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Thu, 13 Jun 2019 10:54:40 -0400 Subject: [PATCH] DO NOT MERGE Revert Privacy Indicators to P Revert Privacy indicators to P. Bug: 135180592 Test: build and check that behavior is as P Change-Id: I24bfc3806e2d12f6a1cc968d6979107e7002a3c4 --- .../sysui/SystemUiDeviceConfigFlags.java | 7 - .../SystemUI/res/drawable/privacy_chip_bg.xml | 23 -- .../res/layout/ongoing_privacy_chip.xml | 41 --- .../res/layout/ongoing_privacy_text_item.xml | 24 -- .../quick_status_bar_header_system_icons.xml | 27 -- .../src/com/android/systemui/Dependency.java | 3 - .../systemui/appops/AppOpsControllerImpl.java | 76 +---- .../systemui/appops/PermissionFlagsCache.kt | 70 ----- .../systemui/privacy/OngoingPrivacyChip.kt | 110 ------- .../systemui/privacy/PrivacyDialogBuilder.kt | 56 ---- .../android/systemui/privacy/PrivacyItem.kt | 80 ----- .../systemui/privacy/PrivacyItemController.kt | 294 ------------------ .../systemui/qs/QuickStatusBarHeader.java | 127 +------- .../statusbar/phone/PhoneStatusBarPolicy.java | 64 +--- .../systemui/appops/AppOpsControllerTest.java | 37 +-- .../appops/PermissionFlagsCacheTest.kt | 88 ------ .../privacy/PrivacyDialogBuilderTest.kt | 77 ----- .../privacy/PrivacyItemControllerTest.kt | 293 ----------------- 18 files changed, 14 insertions(+), 1483 deletions(-) delete mode 100644 packages/SystemUI/res/drawable/privacy_chip_bg.xml delete mode 100644 packages/SystemUI/res/layout/ongoing_privacy_chip.xml delete mode 100644 packages/SystemUI/res/layout/ongoing_privacy_text_item.xml delete mode 100644 packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 55c24eb3e9977..f5607018e5c40 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -106,13 +106,6 @@ public final class SystemUiDeviceConfigFlags { */ public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days"; - // Flag related to Privacy Indicators - - /** - * Whether the Permissions Hub is showing. - */ - public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; - // Flags related to Assistant /** diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml deleted file mode 100644 index b7b21fa53b626..0000000000000 --- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml deleted file mode 100644 index dce9ce16e9cd3..0000000000000 --- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml deleted file mode 100644 index 5595b130e0417..0000000000000 --- a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml index cd9f780ca2492..54fb2168dfa9c 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml @@ -22,19 +22,11 @@ android:layout_height="@*android:dimen/quick_qs_offset_height" android:clipChildren="false" android:clipToPadding="false" - android:gravity="center" android:orientation="horizontal" android:clickable="true" android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end" > - - - - - - - - - - - diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 0ee9bff50bf33..6c36bc98996b7 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -48,7 +48,6 @@ import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.PowerUI; -import com.android.systemui.privacy.PrivacyItemController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -285,7 +284,6 @@ public class Dependency extends SystemUI { @Inject Lazy mSensorPrivacyManager; @Inject Lazy mAutoHideController; @Inject Lazy mForegroundServiceNotificationListener; - @Inject Lazy mPrivacyItemController; @Inject @Named(BG_LOOPER_NAME) Lazy mBgLooper; @Inject @Named(BG_HANDLER_NAME) Lazy mBgHandler; @Inject @Named(MAIN_HANDLER_NAME) Lazy mMainHandler; @@ -469,7 +467,6 @@ public class Dependency extends SystemUI { mProviders.put(ForegroundServiceNotificationListener.class, mForegroundServiceNotificationListener::get); mProviders.put(ClockManager.class, mClockManager::get); - mProviders.put(PrivacyItemController.class, mPrivacyItemController::get); mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get); mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get); mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get); diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 858ed6dcf93db..7a74dba27dce3 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -20,7 +20,6 @@ import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.app.AppOpsManager; import android.content.Context; -import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -62,7 +61,6 @@ public class AppOpsControllerImpl implements AppOpsController, private H mBGHandler; private final List mCallbacks = new ArrayList<>(); private final ArrayMap> mCallbacksByCode = new ArrayMap<>(); - private final PermissionFlagsCache mFlagsCache; private boolean mListening; @GuardedBy("mActiveItems") @@ -80,14 +78,8 @@ public class AppOpsControllerImpl implements AppOpsController, @Inject public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { - this(context, bgLooper, new PermissionFlagsCache(context)); - } - - @VisibleForTesting - protected AppOpsControllerImpl(Context context, Looper bgLooper, PermissionFlagsCache cache) { mContext = context; 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++) { @@ -219,59 +211,6 @@ public class AppOpsControllerImpl implements AppOpsController, mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS); } - /** - * 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, UserHandle.getUserHandleForUid(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()); - } - - - /** - * 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 does not correspond to a platform permission - // which may be user senstive, so for now always show it to the user. - if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { - return true; - } - - return isUserSensitive(appOpCode, uid, packageName); - } - /** * Returns a copy of the list containing all the active AppOps that the controller tracks. * @@ -295,8 +234,8 @@ public class AppOpsControllerImpl implements AppOpsController, final int numActiveItems = mActiveItems.size(); for (int i = 0; i < numActiveItems; i++) { AppOpItem item = mActiveItems.get(i); - if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item)) { + if ((userId == UserHandle.USER_ALL + || UserHandle.getUserId(item.getUid()) == userId)) { list.add(item); } } @@ -305,8 +244,8 @@ public class AppOpsControllerImpl implements AppOpsController, final int numNotedItems = mNotedItems.size(); for (int i = 0; i < numNotedItems; i++) { AppOpItem item = mNotedItems.get(i); - if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item)) { + if ((userId == UserHandle.USER_ALL + || UserHandle.getUserId(item.getUid()) == userId)) { list.add(item); } } @@ -317,7 +256,7 @@ public class AppOpsControllerImpl implements AppOpsController, @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { if (updateActives(code, uid, packageName, active)) { - mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active)); + notifySuscribers(code, uid, packageName, active); } } @@ -328,12 +267,11 @@ public class AppOpsControllerImpl implements AppOpsController, } if (result != AppOpsManager.MODE_ALLOWED) return; addNoted(code, uid, packageName); - mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true)); + notifySuscribers(code, uid, packageName, true); } private void notifySuscribers(int code, int uid, String packageName, boolean active) { - if (mCallbacksByCode.containsKey(code) - && isUserVisible(code, uid, packageName)) { + if (mCallbacksByCode.containsKey(code)) { 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 f02c7afdb9ca6..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.systemui.appops - -import android.content.Context -import android.content.pm.PackageManager -import android.os.UserHandle -import android.util.ArrayMap -import com.android.internal.annotations.VisibleForTesting - -private data class PermissionFlag(val flag: Int, val timestamp: Long) - -private data class PermissionFlagKey( - val permission: String, - val packageName: String, - val user: UserHandle -) - -internal const val CACHE_EXPIRATION = 10000L - -/** - * Cache for PackageManager's PermissionFlags. - * - * Flags older than [CACHE_EXPIRATION] will be retrieved again. - */ -internal open class PermissionFlagsCache(context: Context) { - private val packageManager = context.packageManager - private val permissionFlagsCache = ArrayMap() - - /** - * 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. - */ - fun getPermissionFlags(permission: String, packageName: String, user: UserHandle): Int { - val key = PermissionFlagKey(permission, packageName, user) - val now = getCurrentTime() - val value = permissionFlagsCache.getOrPut(key) { - PermissionFlag(getFlags(key), now) - } - if (now - value.timestamp > CACHE_EXPIRATION) { - val newValue = PermissionFlag(getFlags(key), now) - permissionFlagsCache.put(key, newValue) - return newValue.flag - } else { - return value.flag - } - } - - private fun getFlags(key: PermissionFlagKey) = - packageManager.getPermissionFlags(key.permission, key.packageName, key.user) - - @VisibleForTesting - protected open fun getCurrentTime() = System.currentTimeMillis() -} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt deleted file mode 100644 index a5a915b88cad6..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2018 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.privacy - -import android.content.Context -import android.util.AttributeSet -import android.view.Gravity -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ImageView -import android.widget.LinearLayout -import com.android.systemui.R - -class OngoingPrivacyChip @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttrs: Int = 0, - defStyleRes: Int = 0 -) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) { - - private val iconMarginExpanded = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_chip_icon_margin_expanded) - private val iconMarginCollapsed = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_chip_icon_margin_collapsed) - private val iconSize = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) - private val iconColor = context.resources.getColor( - R.color.status_bar_clock_color, context.theme) - private val sidePadding = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) - private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg) - private lateinit var iconsContainer: LinearLayout - private lateinit var back: FrameLayout - var expanded = false - set(value) { - if (value != field) { - field = value - updateView() - } - } - - var builder = PrivacyDialogBuilder(context, emptyList()) - var privacyList = emptyList() - set(value) { - field = value - builder = PrivacyDialogBuilder(context, value) - updateView() - } - - override fun onFinishInflate() { - super.onFinishInflate() - - back = findViewById(R.id.background) - iconsContainer = findViewById(R.id.icons_container) - } - - // Should only be called if the builder icons or app changed - private fun updateView() { - back.background = if (expanded) backgroundDrawable else null - val padding = if (expanded) sidePadding else 0 - back.setPaddingRelative(padding, 0, padding, 0) - fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) { - iconsContainer.removeAllViews() - dialogBuilder.generateIcons().forEachIndexed { i, it -> - it.mutate() - it.setTint(iconColor) - val image = ImageView(context).apply { - setImageDrawable(it) - scaleType = ImageView.ScaleType.CENTER_INSIDE - } - iconsContainer.addView(image, iconSize, iconSize) - if (i != 0) { - val lp = image.layoutParams as MarginLayoutParams - lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed - image.layoutParams = lp - } - } - } - - if (!privacyList.isEmpty()) { - generateContentDescription() - setIcons(builder, iconsContainer) - val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams - lp.gravity = Gravity.CENTER_VERTICAL or - (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END) - iconsContainer.layoutParams = lp - } else { - iconsContainer.removeAllViews() - } - requestLayout() - } - - private fun generateContentDescription() { - val typesText = builder.joinTypes() - contentDescription = context.getString( - R.string.ongoing_privacy_chip_content_multiple_apps, typesText) - } -} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt deleted file mode 100644 index d08a3733703b0..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2018 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.privacy - -import android.content.Context -import android.graphics.drawable.Drawable -import com.android.systemui.R - -class PrivacyDialogBuilder(private val context: Context, itemsList: List) { - - val appsAndTypes: List>> - val types: List - private val separator = context.getString(R.string.ongoing_privacy_dialog_separator) - private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator) - - init { - appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType }) - .toList() - .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps - { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest) - types = itemsList.map { it.privacyType }.distinct().sorted() - } - - fun generateIconsForApp(types: List): List { - return types.sorted().map { it.getIcon(context) } - } - - fun generateIcons() = types.map { it.getIcon(context) } - - private fun List.joinWithAnd(): StringBuilder { - return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply { - append(lastSeparator) - append(this@joinWithAnd.last()) - } - } - - fun joinTypes(): String { - return when (types.size) { - 0 -> "" - 1 -> types[0].getName(context) - else -> types.map { it.getName(context) }.joinWithAnd().toString() - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt deleted file mode 100644 index 290942412eedf..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 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.privacy - -import android.content.Context -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.UserHandle -import android.util.IconDrawableFactory -import com.android.systemui.R - -typealias Privacy = PrivacyType - -enum class PrivacyType(private val nameId: Int, val iconId: Int) { - // This is uses the icons used by the corresponding permission groups in the AndroidManifest - TYPE_CAMERA(R.string.privacy_type_camera, - com.android.internal.R.drawable.perm_group_camera), - TYPE_MICROPHONE(R.string.privacy_type_microphone, - com.android.internal.R.drawable.perm_group_microphone), - TYPE_LOCATION(R.string.privacy_type_location, - com.android.internal.R.drawable.perm_group_location); - - fun getName(context: Context) = context.resources.getString(nameId) - - fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme) -} - -data class PrivacyItem( - val privacyType: PrivacyType, - val application: PrivacyApplication -) - -data class PrivacyApplication(val packageName: String, val uid: Int, val context: Context) - : Comparable { - - override fun compareTo(other: PrivacyApplication): Int { - return applicationName.compareTo(other.applicationName) - } - - private val applicationInfo: ApplicationInfo? by lazy { - try { - val userHandle = UserHandle.getUserHandleForUid(uid) - context.createPackageContextAsUser(packageName, 0, userHandle).getPackageManager() - .getApplicationInfo(packageName, 0) - } catch (_: PackageManager.NameNotFoundException) { - null - } - } - val icon: Drawable by lazy { - applicationInfo?.let { - try { - val iconFactory = IconDrawableFactory.newInstance(context, true) - iconFactory.getBadgedIcon(it, UserHandle.getUserId(uid)) - } catch (_: Exception) { - null - } - } ?: context.getDrawable(android.R.drawable.sym_def_app_icon) - } - - val applicationName: String by lazy { - applicationInfo?.let { - context.packageManager.getApplicationLabel(it) as String - } ?: packageName - } - - override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)" -} diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt deleted file mode 100644 index 82a2c1fb43fb2..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2018 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.privacy - -import android.app.ActivityManager -import android.app.AppOpsManager -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.os.Handler -import android.os.Looper -import android.os.Message -import android.os.UserHandle -import android.os.UserManager -import android.provider.DeviceConfig -import com.android.internal.annotations.VisibleForTesting -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags -import com.android.systemui.Dependency.BG_HANDLER_NAME -import com.android.systemui.Dependency.MAIN_HANDLER_NAME -import com.android.systemui.R -import com.android.systemui.appops.AppOpItem -import com.android.systemui.appops.AppOpsController -import com.android.systemui.Dumpable -import java.io.FileDescriptor -import java.io.PrintWriter -import java.lang.ref.WeakReference -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Singleton - -fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) - -@Singleton -class PrivacyItemController @Inject constructor( - val context: Context, - private val appOpsController: AppOpsController, - @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler, - @Named(BG_HANDLER_NAME) private val bgHandler: Handler -) : Dumpable { - - @VisibleForTesting - internal companion object { - val OPS = intArrayOf(AppOpsManager.OP_CAMERA, - AppOpsManager.OP_RECORD_AUDIO, - AppOpsManager.OP_COARSE_LOCATION, - AppOpsManager.OP_FINE_LOCATION) - val intents = listOf(Intent.ACTION_USER_FOREGROUND, - Intent.ACTION_MANAGED_PROFILE_ADDED, - Intent.ACTION_MANAGED_PROFILE_REMOVED) - const val TAG = "PrivacyItemController" - const val SYSTEM_UID = 1000 - const val MSG_ADD_CALLBACK = 0 - const val MSG_REMOVE_CALLBACK = 1 - const val MSG_UPDATE_LISTENING_STATE = 2 - } - - @VisibleForTesting - internal var privacyList = emptyList() - @Synchronized get() = field.toList() // Returns a shallow copy of the list - @Synchronized set - - private val userManager = context.getSystemService(UserManager::class.java) - private var currentUserIds = emptyList() - private var listening = false - val systemApp = - PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context) - private val callbacks = mutableListOf>() - private val messageHandler = H(WeakReference(this), uiHandler.looper) - - private val notifyChanges = Runnable { - val list = privacyList - callbacks.forEach { it.get()?.privacyChanged(list) } - } - - private val updateListAndNotifyChanges = Runnable { - updatePrivacyList() - uiHandler.post(notifyChanges) - } - - private var indicatorsAvailable = isPermissionsHubEnabled() - @VisibleForTesting - internal val devicePropertyChangedListener = - object : DeviceConfig.OnPropertyChangedListener { - override fun onPropertyChanged(namespace: String, name: String, value: String?) { - if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace) && - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(name)) { - indicatorsAvailable = java.lang.Boolean.parseBoolean(value) - messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE) - messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE) - } - } - } - - private val cb = object : AppOpsController.Callback { - override fun onActiveStateChanged( - code: Int, - uid: Int, - packageName: String, - active: Boolean - ) { - val userId = UserHandle.getUserId(uid) - if (userId in currentUserIds) { - update(false) - } - } - } - - @VisibleForTesting - internal var userSwitcherReceiver = Receiver() - set(value) { - context.unregisterReceiver(field) - field = value - registerReceiver() - } - - init { - DeviceConfig.addOnPropertyChangedListener( - DeviceConfig.NAMESPACE_PRIVACY, context.mainExecutor, devicePropertyChangedListener) - } - - private fun unregisterReceiver() { - context.unregisterReceiver(userSwitcherReceiver) - } - - private fun registerReceiver() { - context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply { - intents.forEach { - addAction(it) - } - }, null, null) - } - - private fun update(updateUsers: Boolean) { - if (updateUsers) { - val currentUser = ActivityManager.getCurrentUser() - currentUserIds = userManager.getProfiles(currentUser).map { it.id } - } - bgHandler.post(updateListAndNotifyChanges) - } - - /** - * Updates listening status based on whether there are callbacks and the indicators are enabled - * - * This is only called from private (add/remove)Callback and from the config listener, all in - * main thread. - */ - private fun setListeningState() { - val listen = !callbacks.isEmpty() and indicatorsAvailable - if (listening == listen) return - listening = listen - if (listening) { - appOpsController.addCallback(OPS, cb) - registerReceiver() - update(true) - } else { - appOpsController.removeCallback(OPS, cb) - unregisterReceiver() - // Make sure that we remove all indicators and notify listeners if we are not - // listening anymore due to indicators being disabled - update(false) - } - } - - private fun addCallback(callback: WeakReference) { - callbacks.add(callback) - if (callbacks.isNotEmpty() && !listening) { - messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE) - messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE) - } - // Notify this callback if we didn't set to listening - else if (listening) uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList)) - } - - private fun removeCallback(callback: WeakReference) { - // Removes also if the callback is null - callbacks.removeIf { it.get()?.equals(callback.get()) ?: true } - if (callbacks.isEmpty()) { - messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE) - messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE) - } - } - - fun addCallback(callback: Callback) { - messageHandler.obtainMessage(MSG_ADD_CALLBACK, callback).sendToTarget() - } - - fun removeCallback(callback: Callback) { - messageHandler.obtainMessage(MSG_REMOVE_CALLBACK, callback).sendToTarget() - } - - private fun updatePrivacyList() { - if (!listening) { - privacyList = emptyList() - return - } - val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) } - .mapNotNull { toPrivacyItem(it) }.distinct() - privacyList = list - } - - private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? { - val type: PrivacyType = when (appOpItem.code) { - AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA - AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION - AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION - AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE - else -> return null - } - if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp) - val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid, context) - return PrivacyItem(type, app) - } - - // Used by containing class to get notified of changes - interface Callback { - fun privacyChanged(privacyItems: List) - } - - internal inner class Receiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action in intents) { - update(true) - } - } - } - - private class NotifyChangesToCallback( - private val callback: Callback?, - private val list: List - ) : Runnable { - override fun run() { - callback?.privacyChanged(list) - } - } - - override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array?) { - pw?.println("PrivacyItemController state:") - pw?.println(" Listening: $listening") - pw?.println(" Current user ids: $currentUserIds") - pw?.println(" Privacy Items:") - privacyList.forEach { - pw?.print(" ") - pw?.println(it.toString()) - } - pw?.println(" Callbacks:") - callbacks.forEach { - it.get()?.let { - pw?.print(" ") - pw?.println(it.toString()) - } - } - } - - private class H( - private val outerClass: WeakReference, - looper: Looper - ) : Handler(looper) { - override fun handleMessage(msg: Message) { - super.handleMessage(msg) - when (msg.what) { - MSG_UPDATE_LISTENING_STATE -> outerClass.get()?.setListeningState() - - MSG_ADD_CALLBACK -> { - if (msg.obj !is PrivacyItemController.Callback) return - outerClass.get()?.addCallback( - WeakReference(msg.obj as PrivacyItemController.Callback)) - } - - MSG_REMOVE_CALLBACK -> { - if (msg.obj !is PrivacyItemController.Callback) return - outerClass.get()?.removeCallback( - WeakReference(msg.obj as PrivacyItemController.Callback)) - } - else -> {} - } - } - } -} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 96533bce5bcca..a28b60dbe13c7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -32,30 +32,24 @@ import android.graphics.Color; import android.graphics.Rect; import android.media.AudioManager; import android.os.Handler; -import android.os.Looper; import android.provider.AlarmClock; -import android.provider.DeviceConfig; import android.provider.Settings; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; -import android.util.StatsLog; import android.view.ContextThemeWrapper; import android.view.DisplayCutout; import android.view.View; import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.RelativeLayout; -import android.widget.Space; import android.widget.TextView; import androidx.annotation.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; import com.android.systemui.DualToneHandler; @@ -63,11 +57,6 @@ import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.privacy.OngoingPrivacyChip; -import com.android.systemui.privacy.PrivacyDialogBuilder; -import com.android.systemui.privacy.PrivacyItem; -import com.android.systemui.privacy.PrivacyItemController; -import com.android.systemui.privacy.PrivacyItemControllerKt; import com.android.systemui.qs.QSDetail.Callback; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -78,8 +67,6 @@ import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; import java.util.Objects; @@ -121,7 +108,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements private TintedIconManager mIconManager; private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; - private TouchAnimator mPrivacyChipAlphaAnimator; private DualToneHandler mDualToneHandler; private View mSystemIconsView; @@ -141,12 +127,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements private View mRingerContainer; private Clock mClockView; private DateView mDateView; - private OngoingPrivacyChip mPrivacyChip; - private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; - private boolean mPermissionsHubEnabled; - - private PrivacyItemController mPrivacyItemController; private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() { @Override @@ -156,41 +137,17 @@ public class QuickStatusBarHeader extends RelativeLayout implements } }; private boolean mHasTopCutout = false; - private boolean mPrivacyChipLogged = false; - - private final DeviceConfig.OnPropertyChangedListener mPropertyListener = - new DeviceConfig.OnPropertyChangedListener() { - @Override - public void onPropertyChanged(String namespace, String name, String value) { - if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace) - && SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals( - name)) { - mPermissionsHubEnabled = Boolean.valueOf(value); - StatusIconContainer iconContainer = findViewById(R.id.statusIcons); - iconContainer.setIgnoredSlots(getIgnoredIconSlots()); - } - } - }; - - private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() { - @Override - public void privacyChanged(List privacyItems) { - mPrivacyChip.setPrivacyList(privacyItems); - setChipVisibility(!privacyItems.isEmpty()); - } - }; @Inject public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, NextAlarmController nextAlarmController, ZenModeController zenModeController, StatusBarIconController statusBarIconController, - ActivityStarter activityStarter, PrivacyItemController privacyItemController) { + ActivityStarter activityStarter) { super(context, attrs); mAlarmController = nextAlarmController; mZenController = zenModeController; mStatusBarIconController = statusBarIconController; mActivityStarter = activityStarter; - mPrivacyItemController = privacyItemController; mDualToneHandler = new DualToneHandler( new ContextThemeWrapper(context, R.style.QSHeaderTheme)); } @@ -203,8 +160,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons); mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons); StatusIconContainer iconContainer = findViewById(R.id.statusIcons); - // Ignore privacy icons because they show in the space above QQS - iconContainer.addIgnoredSlots(getIgnoredIconSlots()); iconContainer.setShouldRestrictIcons(false); mIconManager = new TintedIconManager(iconContainer); @@ -218,9 +173,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mRingerModeIcon = findViewById(R.id.ringer_mode_icon); mRingerModeTextView = findViewById(R.id.ringer_mode_text); mRingerContainer = findViewById(R.id.ringer_container); - mRingerContainer.setOnClickListener(this::onClick); - mPrivacyChip = findViewById(R.id.privacy_chip); - mPrivacyChip.setOnClickListener(this::onClick); mCarrierGroup = findViewById(R.id.carrier_group); @@ -243,7 +195,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mClockView = findViewById(R.id.clock); mClockView.setOnClickListener(this); mDateView = findViewById(R.id.date); - mSpace = findViewById(R.id.space); // Tint for the battery icons are handled in setupHost() mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon); @@ -254,26 +205,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE); mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); - - mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled(); - // Change the ignored slots when DeviceConfig flag changes - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_PRIVACY, - mContext.getMainExecutor(), mPropertyListener); - - } - - private List getIgnoredIconSlots() { - ArrayList ignored = new ArrayList<>(); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_camera)); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_microphone)); - if (mPermissionsHubEnabled) { - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_location)); - } - - return ignored; } private void updateStatusText() { @@ -287,21 +218,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements } } - private void setChipVisibility(boolean chipVisible) { - if (chipVisible && mPermissionsHubEnabled) { - mPrivacyChip.setVisibility(View.VISIBLE); - // Makes sure that the chip is logged as viewed at most once each time QS is opened - // mListening makes sure that the callback didn't return after the user closed QS - if (!mPrivacyChipLogged && mListening) { - mPrivacyChipLogged = true; - StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED, - StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_VIEWED); - } - } else { - mPrivacyChip.setVisibility(View.GONE); - } - } - private boolean updateRingerStatus() { boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE; CharSequence originalRingerText = mRingerModeTextView.getText(); @@ -408,7 +324,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateStatusIconAlphaAnimator(); updateHeaderTextContainerAlphaAnimator(); - updatePrivacyChipAlphaAnimator(); } private void updateStatusIconAlphaAnimator() { @@ -423,12 +338,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements .build(); } - private void updatePrivacyChipAlphaAnimator() { - mPrivacyChipAlphaAnimator = new TouchAnimator.Builder() - .addFloat(mPrivacyChip, "alpha", 1, 0, 1) - .build(); - } - public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; mExpanded = expanded; @@ -467,10 +376,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mHeaderTextContainerView.setVisibility(INVISIBLE); } } - if (mPrivacyChipAlphaAnimator != null) { - mPrivacyChip.setExpanded(expansionFraction > 0.5); - mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction); - } } public void disable(int state1, int state2, boolean animate) { @@ -503,21 +408,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mSystemIconsView.setPadding(padding.first, 0, padding.second, 0); } - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams(); - if (cutout != null) { - Rect topCutout = cutout.getBoundingRectTop(); - if (topCutout.isEmpty()) { - mHasTopCutout = false; - lp.width = 0; - mSpace.setVisibility(View.GONE); - } else { - mHasTopCutout = true; - lp.width = topCutout.width(); - mSpace.setVisibility(View.VISIBLE); - } - } - mSpace.setLayoutParams(lp); - setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE); return super.onApplyWindowInsets(insets); } @@ -542,13 +432,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements mAlarmController.addCallback(this); mContext.registerReceiver(mRingerReceiver, new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)); - mPrivacyItemController.addCallback(mPICCallback); } else { mZenController.removeCallback(this); mAlarmController.removeCallback(this); - mPrivacyItemController.removeCallback(mPICCallback); mContext.unregisterReceiver(mRingerReceiver); - mPrivacyChipLogged = false; } } @@ -566,18 +453,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mActivityStarter.postStartActivityDismissingKeyguard(new Intent( AlarmClock.ACTION_SHOW_ALARMS), 0); } - } else if (v == mPrivacyChip) { - // Makes sure that the builder is grabbed as soon as the chip is pressed - PrivacyDialogBuilder builder = mPrivacyChip.getBuilder(); - if (builder.getAppsAndTypes().size() == 0) return; - Handler mUiHandler = new Handler(Looper.getMainLooper()); - StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED, - StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_CLICKED); - mUiHandler.post(() -> { - mActivityStarter.postStartActivityDismissingKeyguard( - new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0); - mHost.collapsePanels(); - }); } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) { mActivityStarter.postStartActivityDismissingKeyguard(new Intent( Settings.ACTION_SOUND_SETTINGS), 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index ee43879527923..17c200eaef6a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -42,10 +42,6 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.UiOffloadThread; -import com.android.systemui.privacy.PrivacyItem; -import com.android.systemui.privacy.PrivacyItemController; -import com.android.systemui.privacy.PrivacyItemControllerKt; -import com.android.systemui.privacy.PrivacyType; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.statusbar.CommandQueue; @@ -66,9 +62,6 @@ import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.List; import java.util.Locale; /** @@ -83,12 +76,12 @@ public class PhoneStatusBarPolicy ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback, - PrivacyItemController.Callback, LocationController.LocationChangeCallback { private static final String TAG = "PhoneStatusBarPolicy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - public static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId(); + public static final int LOCATION_STATUS_ICON_ID = + com.android.internal.R.drawable.perm_group_location; private final String mSlotCast; private final String mSlotHotspot; @@ -102,8 +95,6 @@ public class PhoneStatusBarPolicy private final String mSlotHeadset; private final String mSlotDataSaver; private final String mSlotLocation; - private final String mSlotMicrophone; - private final String mSlotCamera; private final String mSlotSensorsOff; private final Context mContext; @@ -121,7 +112,6 @@ public class PhoneStatusBarPolicy private final DeviceProvisionedController mProvisionedController; private final KeyguardMonitor mKeyguardMonitor; private final LocationController mLocationController; - private final PrivacyItemController mPrivacyItemController; private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); private final SensorPrivacyController mSensorPrivacyController; @@ -154,7 +144,6 @@ public class PhoneStatusBarPolicy mProvisionedController = Dependency.get(DeviceProvisionedController.class); mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); mLocationController = Dependency.get(LocationController.class); - mPrivacyItemController = Dependency.get(PrivacyItemController.class); mSensorPrivacyController = Dependency.get(SensorPrivacyController.class); mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast); @@ -170,8 +159,6 @@ public class PhoneStatusBarPolicy mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset); mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver); mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location); - mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone); - mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera); mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off); // listen for broadcasts @@ -231,13 +218,6 @@ public class PhoneStatusBarPolicy context.getString(R.string.accessibility_data_saver_on)); mIconController.setIconVisibility(mSlotDataSaver, false); - // privacy items - mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(), - PrivacyType.TYPE_MICROPHONE.getName(mContext)); - mIconController.setIconVisibility(mSlotMicrophone, false); - mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(), - PrivacyType.TYPE_CAMERA.getName(mContext)); - mIconController.setIconVisibility(mSlotCamera, false); mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, mContext.getString(R.string.accessibility_location_active)); mIconController.setIconVisibility(mSlotLocation, false); @@ -257,7 +237,6 @@ public class PhoneStatusBarPolicy mNextAlarmController.addCallback(mNextAlarmCallback); mDataSaver.addCallback(this); mKeyguardMonitor.addCallback(this); - mPrivacyItemController.addCallback(this); mSensorPrivacyController.addCallback(mSensorPrivacyListener); mLocationController.addCallback(this); @@ -601,46 +580,9 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); } - @Override // PrivacyItemController.Callback - public void privacyChanged(List privacyItems) { - updatePrivacyItems(privacyItems); - } - - private void updatePrivacyItems(List items) { - boolean showCamera = false; - boolean showMicrophone = false; - boolean showLocation = false; - for (PrivacyItem item : items) { - if (item == null /* b/124234367 */) { - if (DEBUG) { - Log.e(TAG, "updatePrivacyItems - null item found"); - StringWriter out = new StringWriter(); - mPrivacyItemController.dump(null, new PrintWriter(out), null); - Log.e(TAG, out.toString()); - } - continue; - } - switch (item.getPrivacyType()) { - case TYPE_CAMERA: - showCamera = true; - break; - case TYPE_LOCATION: - showLocation = true; - break; - case TYPE_MICROPHONE: - showMicrophone = true; - break; - } - } - - mIconController.setIconVisibility(mSlotCamera, showCamera); - mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); - mIconController.setIconVisibility(mSlotLocation, showLocation); - } - @Override public void onLocationActiveChanged(boolean active) { - if (!PrivacyItemControllerKt.isPermissionsHubEnabled()) updateLocation(); + updateLocation(); } // Updates the status view based on the current state of location requests. 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 540ac843ad8ec..59d5c243af730 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -16,20 +16,17 @@ package com.android.systemui.appops; -import static junit.framework.TestCase.assertFalse; - import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.pm.PackageManager; @@ -39,6 +36,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -54,7 +52,6 @@ public class AppOpsControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; 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); @Mock private AppOpsManager mAppOpsManager; @@ -64,32 +61,16 @@ public class AppOpsControllerTest extends SysuiTestCase { private AppOpsController.Callback mCallback; @Mock private AppOpsControllerImpl.H mMockHandler; - @Mock - private PermissionFlagsCache mFlagsCache; private AppOpsControllerImpl mController; - private TestableLooper mTestableLooper; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTestableLooper = TestableLooper.get(this); 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(UserHandle.getUserHandleForUid(TEST_UID)))).thenReturn( - PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED); - when(mFlagsCache.getPermissionFlags(anyString(), anyString(), - eq(UserHandle.getUserHandleForUid(TEST_UID_OTHER)))).thenReturn( - PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED); - when(mFlagsCache.getPermissionFlags(anyString(), anyString(), - eq(UserHandle.getUserHandleForUid(TEST_UID_NON_USER_SENSITIVE)))).thenReturn(0); - - mController = new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mFlagsCache); + mController = new AppOpsControllerImpl(mContext, Dependency.get(Dependency.BG_LOOPER)); } @Test @@ -113,7 +94,6 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); - mTestableLooper.processAllMessages(); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); } @@ -123,7 +103,6 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - mTestableLooper.processAllMessages(); verify(mCallback, never()).onActiveStateChanged( anyInt(), anyInt(), anyString(), anyBoolean()); } @@ -134,7 +113,6 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - mTestableLooper.processAllMessages(); verify(mCallback, never()).onActiveStateChanged( anyInt(), anyInt(), anyString(), anyBoolean()); } @@ -145,7 +123,6 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - mTestableLooper.processAllMessages(); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); } @@ -184,14 +161,6 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size()); } - @Test - public void nonUserSensitiveOpsAreIgnored() { - mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, - TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true); - assertEquals(0, mController.getActiveAppOpsForUser( - UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size()); - } - @Test public void opNotedScheduledForRemoval() { mController.setBGHandler(mMockHandler); 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 dc070dea6d898..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.systemui.appops - -import android.content.Context -import android.content.pm.PackageManager -import android.os.UserHandle -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -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.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" - } - - @Mock - private lateinit var mPackageManager: PackageManager - @Mock - private lateinit var mUserHandle: UserHandle - private lateinit var flagsCache: TestPermissionFlagsCache - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - mContext.setMockPackageManager(mPackageManager) - flagsCache = TestPermissionFlagsCache(mContext) - } - - @Test - fun testCallsPackageManager_exactlyOnce() { - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - flagsCache.time = CACHE_EXPIRATION - 1 - verify(mPackageManager).getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - } - - @Test - fun testCallsPackageManager_cacheExpired() { - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - flagsCache.time = CACHE_EXPIRATION + 1 - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - verify(mPackageManager, times(2)) - .getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - } - - @Test - fun testCallsPackageMaanger_multipleKeys() { - flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle) - flagsCache.getPermissionFlags(TEST_PERMISSION, "", mUserHandle) - verify(mPackageManager, times(2)) - .getPermissionFlags(anyString(), anyString(), any()) - } - - private class TestPermissionFlagsCache(context: Context) : PermissionFlagsCache(context) { - var time = 0L - - override fun getCurrentTime(): Long { - return time - } - } -} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt deleted file mode 100644 index 6302f9d56dc3f..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 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.privacy - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.systemui.SysuiTestCase -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class PrivacyDialogBuilderTest : SysuiTestCase() { - - companion object { - val TEST_UID = 1 - } - - @Test - fun testGenerateAppsList() { - val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication( - "Bar", TEST_UID, context)) - val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication( - "Bar", TEST_UID, context)) - val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication( - "Foo", TEST_UID, context)) - val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication( - "Baz", TEST_UID, context)) - - val items = listOf(bar2, foo0, baz1, bar3) - - val textBuilder = PrivacyDialogBuilder(context, items) - - val list = textBuilder.appsAndTypes - assertEquals(3, list.size) - val appsList = list.map { it.first } - val typesList = list.map { it.second } - // List is sorted by number of types and then by types - assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName }) - assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0]) - assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1]) - assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2]) - } - - @Test - fun testOrder() { - // We want location to always go last, so it will go in the "+ other apps" - val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, - PrivacyApplication("Camera", TEST_UID, context)) - val appMicrophone = - PrivacyItem(PrivacyType.TYPE_MICROPHONE, - PrivacyApplication("Microphone", TEST_UID, context)) - val appLocation = - PrivacyItem(PrivacyType.TYPE_LOCATION, - PrivacyApplication("Location", TEST_UID, context)) - - val items = listOf(appLocation, appMicrophone, appCamera) - val textBuilder = PrivacyDialogBuilder(context, items) - val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName } - assertEquals(listOf("Camera", "Microphone", "Location"), appList) - } -} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt deleted file mode 100644 index e2e0bb1512284..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2018 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.privacy - -import android.app.ActivityManager -import android.app.AppOpsManager -import android.content.Context -import android.content.Intent -import android.content.pm.UserInfo -import android.os.Handler -import android.os.UserHandle -import android.os.UserManager -import android.provider.DeviceConfig -import android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags -import com.android.systemui.Dependency -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.appops.AppOpItem -import com.android.systemui.appops.AppOpsController -import org.hamcrest.Matchers.hasItem -import org.hamcrest.Matchers.not -import org.hamcrest.Matchers.nullValue -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertThat -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyList -import org.mockito.ArgumentMatchers.eq -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.spy -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyNoMoreInteractions -import org.mockito.MockitoAnnotations - -@RunWith(AndroidTestingRunner::class) -@SmallTest -@RunWithLooper -class PrivacyItemControllerTest : SysuiTestCase() { - - companion object { - val CURRENT_USER_ID = ActivityManager.getCurrentUser() - val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE - const val SYSTEM_UID = 1000 - const val TEST_PACKAGE_NAME = "test" - const val DEVICE_SERVICES_STRING = "Device services" - const val TAG = "PrivacyItemControllerTest" - fun capture(argumentCaptor: ArgumentCaptor): T = argumentCaptor.capture() - } - - @Mock - private lateinit var appOpsController: AppOpsController - @Mock - private lateinit var callback: PrivacyItemController.Callback - @Mock - private lateinit var userManager: UserManager - @Captor - private lateinit var argCaptor: ArgumentCaptor> - @Captor - private lateinit var argCaptorCallback: ArgumentCaptor - - private lateinit var testableLooper: TestableLooper - private lateinit var privacyItemController: PrivacyItemController - private lateinit var handler: Handler - - fun PrivacyItemController(context: Context) = - PrivacyItemController(context, appOpsController, handler, handler) - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - testableLooper = TestableLooper.get(this) - handler = Handler(testableLooper.looper) - - appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) - mDependency.injectTestDependency(Dependency.BG_HANDLER, handler) - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler) - mContext.addMockSystemService(UserManager::class.java, userManager) - mContext.getOrCreateTestableResources().addOverride(R.string.device_services, - DEVICE_SERVICES_STRING) - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "true", false) - - doReturn(listOf(object : UserInfo() { - init { - id = CURRENT_USER_ID - } - })).`when`(userManager).getProfiles(anyInt()) - - privacyItemController = PrivacyItemController(mContext) - } - - @After - fun tearDown() { - DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_PRIVACY) - } - - @Test - fun testSetListeningTrueByAddingCallback() { - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), - any(AppOpsController.Callback::class.java)) - testableLooper.processAllMessages() - verify(callback).privacyChanged(anyList()) - } - - @Test - fun testSetListeningFalseByRemovingLastCallback() { - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - verify(appOpsController, never()).removeCallback(any(IntArray::class.java), - any(AppOpsController.Callback::class.java)) - privacyItemController.removeCallback(callback) - testableLooper.processAllMessages() - verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS), - any(AppOpsController.Callback::class.java)) - verify(callback).privacyChanged(emptyList()) - } - - @Test - fun testDistinctItems() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), - AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - verify(callback).privacyChanged(capture(argCaptor)) - assertEquals(1, argCaptor.value.size) - } - - @Test - fun testSystemApps() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME, - 0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - verify(callback).privacyChanged(capture(argCaptor)) - assertEquals(1, argCaptor.value.size) - assertEquals(context.getString(R.string.device_services), - argCaptor.value[0].application.applicationName) - } - - @Test - fun testRegisterReceiver_allUsers() { - val spiedContext = spy(mContext) - val itemController = PrivacyItemController(spiedContext) - itemController.addCallback(callback) - testableLooper.processAllMessages() - verify(spiedContext, atLeastOnce()).registerReceiverAsUser( - eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null), - eq(null)) - verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver)) - } - - @Test - fun testReceiver_ACTION_USER_FOREGROUND() { - privacyItemController.userSwitcherReceiver.onReceive(context, - Intent(Intent.ACTION_USER_FOREGROUND)) - verify(userManager).getProfiles(anyInt()) - } - - @Test - fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() { - privacyItemController.userSwitcherReceiver.onReceive(context, - Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)) - verify(userManager).getProfiles(anyInt()) - } - - @Test - fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() { - privacyItemController.userSwitcherReceiver.onReceive(context, - Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED)) - verify(userManager).getProfiles(anyInt()) - } - - @Test - fun testAddMultipleCallbacks() { - val otherCallback = mock(PrivacyItemController.Callback::class.java) - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - verify(callback).privacyChanged(anyList()) - - privacyItemController.addCallback(otherCallback) - testableLooper.processAllMessages() - verify(otherCallback).privacyChanged(anyList()) - // Adding a callback should not unnecessarily call previous ones - verifyNoMoreInteractions(callback) - } - - @Test - fun testMultipleCallbacksAreUpdated() { - doReturn(emptyList()).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - - val otherCallback = mock(PrivacyItemController.Callback::class.java) - privacyItemController.addCallback(callback) - privacyItemController.addCallback(otherCallback) - testableLooper.processAllMessages() - reset(callback) - reset(otherCallback) - - verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) - argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true) - testableLooper.processAllMessages() - verify(callback).privacyChanged(anyList()) - verify(otherCallback).privacyChanged(anyList()) - } - - @Test - fun testRemoveCallback() { - doReturn(emptyList()).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - val otherCallback = mock(PrivacyItemController.Callback::class.java) - privacyItemController.addCallback(callback) - privacyItemController.addCallback(otherCallback) - testableLooper.processAllMessages() - reset(callback) - reset(otherCallback) - - verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) - privacyItemController.removeCallback(callback) - argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true) - testableLooper.processAllMessages() - verify(callback, never()).privacyChanged(anyList()) - verify(otherCallback).privacyChanged(anyList()) - } - - @Test - fun testListShouldNotHaveNull() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0), - AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - - verify(callback).privacyChanged(capture(argCaptor)) - assertEquals(1, argCaptor.value.size) - assertThat(argCaptor.value, not(hasItem(nullValue()))) - } - - @Test - fun testListShouldBeCopy() { - val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA, - PrivacyApplication("", TEST_UID, mContext))) - privacyItemController.privacyList = list - val privacyList = privacyItemController.privacyList - assertEquals(list, privacyList) - assertTrue(list !== privacyList) - } - - @Test - fun testNotListeningWhenIndicatorsDisabled() { - privacyItemController.devicePropertyChangedListener.onPropertyChanged( - DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "false") - privacyItemController.addCallback(callback) - testableLooper.processAllMessages() - verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS), - any(AppOpsController.Callback::class.java)) - } -} \ No newline at end of file