diff options
8 files changed, 228 insertions, 0 deletions
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 <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <!-- A string representing a message (sms, email, etc.) telling the user about a one time password. Used for testing [CHAR LIMIT=NONE] --> <string name="test_otp_msg">Your one time password is 132435</string> + <!-- [CHAR LIMIT=50] Manage applications, unlock restricted setting from app permissions options menu --> + <string name="allow_restricted_settings">Allow restricted settings</string> + <!-- START ENHANCED CONFIRMATION DIALOG --> <!--Title for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=50] --> 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 @@ -47,6 +47,15 @@ flag { } 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 namespace: "permissions" 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" + } +} |