From ee17b3ec38f649354ba3d131d6490dedf6d7a22f Mon Sep 17 00:00:00 2001 From: Faye Yan Date: Fri, 21 Jun 2024 22:07:02 +0000 Subject: [ECM Backport] Add allow restricted settings into options menu of App permissions. This change implements the UX change for ECM Backport to T and U builds. In V, no UX change, "allow restricted settings" is in the options menu of AppInfo screen. In U, the "allow restricted settings" is added into the options menu of App permissions screen. In T, the "allow restricted settings" is added into the options menu of App permisisons screen. Flag: com.android.permission.flags.enhanced_confirmation_backport_enabled Bug: 347876543 Test: Manual tested on T, U, V devices, atest to be added Test: atest EnhancedConfirmationBackportTest LOW_COVERAGE_REASON=FLAG_NOT_ENABLED Change-Id: I6aee8baccb2ea60c39d6c67b097f8d2dcfc2c757 --- PermissionController/res/values/strings.xml | 3 + .../ui/handheld/AppPermissionGroupsFragment.java | 20 +++ .../ui/handheld/PermissionsFrameFragment.java | 1 + .../ui/model/AppPermissionGroupsViewModel.kt | 33 +++++ flags/flags.aconfig | 9 ++ tests/cts/permissionui/Android.bp | 1 + .../permissionui/cts/BaseUsePermissionTest.kt | 15 +++ .../cts/EnhancedConfirmationBackportTest.kt | 146 +++++++++++++++++++++ 8 files changed, 228 insertions(+) create mode 100644 tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml index 698a9bbb4..57268f670 100644 --- a/PermissionController/res/values/strings.xml +++ b/PermissionController/res/values/strings.xml @@ -1992,6 +1992,9 @@ Allow %4$s to upload a bug repo Your one time password is 132435 + + Allow restricted settings + diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java index eff5738fc..e8d0fb22b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java @@ -252,6 +252,12 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i mPackageName, mUser)); return true; } + + case MENU_ALLOW_RESTRICTED_SETTINGS: { + mViewModel.clearRestriction(); + getActivity().invalidateOptionsMenu(); + return true; + } } return super.onOptionsItemSelected(item); } @@ -266,6 +272,20 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i getClass().getName()); } } + + if (SdkLevel.isAtLeastT() && !SdkLevel.isAtLeastV() + && Flags.enhancedConfirmationBackportEnabled()) { + menu.add(Menu.NONE, MENU_ALLOW_RESTRICTED_SETTINGS, Menu.NONE, + R.string.allow_restricted_settings); + } + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + final MenuItem allowRestrictedSettingsMenu = menu.findItem(MENU_ALLOW_RESTRICTED_SETTINGS); + if (allowRestrictedSettingsMenu != null) { + allowRestrictedSettingsMenu.setVisible(mViewModel.isClearRestrictedAllowed()); + } } private static void bindUi(SettingsWithLargeHeader fragment, String packageName, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java index 17e72b413..a44c761a1 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java @@ -42,6 +42,7 @@ public abstract class PermissionsFrameFragment extends PreferenceFragmentCompat static final int MENU_ALL_PERMS = Menu.FIRST + 1; public static final int MENU_SHOW_SYSTEM = Menu.FIRST + 2; public static final int MENU_HIDE_SYSTEM = Menu.FIRST + 3; + static final int MENU_ALLOW_RESTRICTED_SETTINGS = Menu.FIRST + 4; private ViewGroup mPreferencesContainer; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt index ee0c5d2f2..f92cc08d7 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.ui.model import android.Manifest +import android.annotation.SuppressLint import android.app.AppOpsManager import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_IGNORED @@ -31,6 +32,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.PermissionControllerStatsLog import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION @@ -128,6 +130,37 @@ class AppPermissionGroupsViewModel( private val fullStoragePermsLiveData = FullStoragePermissionAppsLiveData private val packagePermsExternalDeviceLiveData = PackagePermissionsExternalDeviceLiveData[packageName, user] + private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! + private val packageManager = app.packageManager + + /** Check if the application is in restricted settings mode. */ + @SuppressLint("NewApi") + fun isClearRestrictedAllowed(): Boolean { + if (Flags.enhancedConfirmationBackportEnabled()) { + // TODO(b/347876543): Replace this when EnhancedConfirmtionServiceImpl is + // available. + val isRestricted = + appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, + packageManager.getApplicationInfoAsUser(packageName, 0, user).uid, + packageName, null, null) == MODE_IGNORED + return isRestricted + } + return false + } + + /** Allow restricted settings on the applications. */ + @SuppressLint("NewApi") + fun clearRestriction() { + if (Flags.enhancedConfirmationBackportEnabled()) { + // TODO(b/347876543): Replace this when EnhancedConfirmationServiceImpl is + // available. + appOpsManager.setMode( + AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, + packageManager.getApplicationInfoAsUser(packageName, 0, user).uid, + packageName, MODE_ALLOWED + ) + } + } /** * LiveData whose data is a map of grant category (either allowed or denied) to a list of diff --git a/flags/flags.aconfig b/flags/flags.aconfig index 2dd34e5ff..4f0f08938 100644 --- a/flags/flags.aconfig +++ b/flags/flags.aconfig @@ -46,6 +46,15 @@ flag { is_fixed_read_only: true } +flag { + name: "enhanced_confirmation_backport_enabled" + is_exported: true + namespace: "permissions" + description: "Flag to backport enhanced confirmation in permission mainline to T and U." + bug: "347876543" + is_fixed_read_only: true +} + flag { name: "enable_coarse_fine_location_prompt_for_aaos" is_exported: true diff --git a/tests/cts/permissionui/Android.bp b/tests/cts/permissionui/Android.bp index c1ec017ff..72624f5e2 100644 --- a/tests/cts/permissionui/Android.bp +++ b/tests/cts/permissionui/Android.bp @@ -44,6 +44,7 @@ android_test { "platform-test-annotations", "android.content.pm.flags-aconfig-java-export", "android.permission.flags-aconfig-java-export", + "com.android.permission.flags-aconfig-java-export", "Harrier", ], data: [ diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt index fdc13b3af..7e3f7f8dc 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt @@ -1102,6 +1102,21 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } } + @Suppress("DEPRECATION") + protected fun startManageAppPermissionsActivity() { + doAndWaitForWindowTransition { + runWithShellPermissionIdentity { + context.startActivity( + Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME) + } + ) + } + } + } + /** Starts activity with intent [ACTION_REVIEW_APP_DATA_SHARING_UPDATES]. */ fun startAppDataSharingUpdatesActivity() { doAndWaitForWindowTransition { diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt new file mode 100644 index 000000000..f38f678e9 --- /dev/null +++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationBackportTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2024 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 android.permissionui.cts + +import android.Manifest.permission_group.SMS +import android.os.Build +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import androidx.test.filters.SdkSuppress +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags +import org.junit.Assume.assumeFalse +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +/** Enhanced Confirmation Backport UI tests. */ +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") +class EnhancedConfirmationBackportTest : BaseUsePermissionTest() { + + @JvmField + @Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Before + fun setup() { + assumeFalse(isAutomotive) + assumeFalse(isTv) + assumeFalse(isWatch) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installDownloadedFile_clickAppPermissions_clickAllowRestrictedSettings_clickSMSPermGroup_clickAllowed() { + installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).clickAndWait( + Until.newWindow(), + BasePermissionTest.TIMEOUT_MILLIS + ) + + if (!SdkLevel.isAtLeastV()) { + waitFindObject(By.text(ALLOW_RESTRICTED_SETTINGS)).click() + + pressBack() + + navigateToIndividualPermissionSetting(SMS) + + assertAllowButtonIsEnabledAndClickAndChecked() + + pressBack() + } else { + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + } + + pressBack() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installFromLocalFile_clickAppPermissions_clickAllowRestrictedSettings_clickSMSPermGroup_clickAllowed() { + installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).click() + + if (!SdkLevel.isAtLeastV()) { + waitFindObject(By.text(ALLOW_RESTRICTED_SETTINGS)).click() + + pressBack() + + navigateToIndividualPermissionSetting(SMS) + + assertAllowButtonIsEnabledAndClickAndChecked() + + pressBack() + } else { + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + } + + pressBack() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installDownloadedFile_clickAppPermissions_noAllowRestrictedSettings() { + installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).clickAndWait( + Until.newWindow(), + BasePermissionTest.TIMEOUT_MILLIS + ) + + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + + pressBack() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @RequiresFlagsDisabled(Flags.FLAG_ENHANCED_CONFIRMATION_BACKPORT_ENABLED) + @Test + fun installFromLocalFile_clickAppPermissions_noAllowRestrictedSettings() { + installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_LATEST) + + startManageAppPermissionsActivity() + waitFindObject(By.descContains(MORE_OPTIONS)).click() + + findView(By.text(ALLOW_RESTRICTED_SETTINGS), false) + + pressBack() + } + + private fun assertAllowButtonIsEnabledAndClickAndChecked() { + waitFindObject(By.res(ALLOW_RADIO_BUTTON).enabled(true).checked(false)) + .click() + waitFindObject(By.res(ALLOW_RADIO_BUTTON).checked(true)) + } + + companion object { + private const val MORE_OPTIONS = "More options" + private const val ALLOW_RESTRICTED_SETTINGS = "Allow restricted settings" + } +} -- cgit v1.2.3-59-g8ed1b