diff options
15 files changed, 399 insertions, 133 deletions
diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml index b6acf82a7..55181f066 100644 --- a/PermissionController/res/values/styles.xml +++ b/PermissionController/res/values/styles.xml @@ -1038,6 +1038,7 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginStart">16dp</item> + <item name="android:background">@null</item> </style> <!-- END REQUEST ROLE DIALOG ITEM --> diff --git a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt index 719ef33b5..0ee0e0d01 100644 --- a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt +++ b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt @@ -140,6 +140,14 @@ class DrivingDecisionReminderService : Service() { car.disconnect() } } + + fun cancelNotification(context: Context) { + val notificationManager = context.getSystemService(NotificationManager::class.java)!! + notificationManager.cancel( + DrivingDecisionReminderService::class.java.simpleName, + Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID + ) + } } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java index ddcdf1319..4fd62dc05 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission_group.LOCATION; import static android.Manifest.permission_group.READ_MEDIA_VISUAL; +import static android.content.Intent.getIntent; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; @@ -34,12 +35,15 @@ import static com.android.permissioncontroller.permission.ui.GrantPermissionsVie import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_PERMISSION_RATIONALE; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_SETTINGS; import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.APP_PERMISSION_REQUEST_CODE; +import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.ECM_REQUEST_CODE; import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.PHOTO_PICKER_REQUEST_CODE; import static com.android.permissioncontroller.permission.utils.Utils.getRequestMessage; import android.Manifest; import android.app.Activity; import android.app.KeyguardManager; +import android.app.ecm.EnhancedConfirmationManager; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; @@ -49,6 +53,7 @@ import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; import android.os.Process; +import android.permission.flags.Flags; import android.text.Annotation; import android.text.SpannableString; import android.text.Spanned; @@ -104,6 +109,13 @@ public class GrantPermissionsActivity extends SettingsActivity private static final String KEY_SESSION_ID = GrantPermissionsActivity.class.getName() + "_REQUEST_ID"; + public static final String KEY_RESTRICTED_REQUESTED_PERMISSIONS = + GrantPermissionsActivity.class.getName() + "_RESTRICTED_REQUESTED_PERMISSIONS"; + public static final String KEY_UNRESTRICTED_REQUESTED_PERMISSIONS = + GrantPermissionsActivity.class.getName() + "_UNRESTRICTED_REQUESTED_PERMISSIONS"; + public static final String KEY_ORIGINAL_REQUESTED_PERMISSIONS = + GrantPermissionsActivity.class.getName() + "_ORIGINAL_REQUESTED_PERMISSIONS"; + public static final String ANNOTATION_ID = "link"; public static final int NEXT_BUTTON = 15; @@ -158,6 +170,17 @@ public class GrantPermissionsActivity extends SettingsActivity /** The current list of permissions requested, across all current requests for this app */ private List<String> mRequestedPermissions = new ArrayList<>(); + /** + * If any requested permissions are considered restricted by ECM, they will be stored here. + */ + private ArrayList<String> mRestrictedRequestedPermissionGroups = null; + + /** + * If any requested permissions are considered restricted by ECM, the non-restricted + * permissions will be stored here. + */ + private List<String> mUnrestrictedRequestedPermissions = null; + /** A list of permissions requested on an app's behalf by the system. Usually Implicitly * requested, although this isn't necessarily always the case. */ @@ -297,6 +320,56 @@ public class GrantPermissionsActivity extends SettingsActivity mOriginalRequestedPermissions = mRequestedPermissions.toArray(new String[0]); + if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) { + EnhancedConfirmationManager ecm = getEnhancedConfirmationManager(); + + // Retrieve ECM-related persisted permission lists + if (icicle != null) { + mOriginalRequestedPermissions = icicle.getStringArray( + KEY_ORIGINAL_REQUESTED_PERMISSIONS); + mRestrictedRequestedPermissionGroups = icicle.getStringArrayList( + KEY_RESTRICTED_REQUESTED_PERMISSIONS); + mUnrestrictedRequestedPermissions = icicle.getStringArrayList( + KEY_UNRESTRICTED_REQUESTED_PERMISSIONS); + } + // If these lists aren't persisted yet, it means we haven't yet divided + // mRequestedPermissions into restricted-vs-unrestricted, so do so. + if (mRestrictedRequestedPermissionGroups == null) { + String packageName = getCallingPackage(); + ArraySet<String> restrictedPermGroups = new ArraySet<>(); + ArrayList<String> unrestrictedPermissions = new ArrayList<>(); + + for (String requestedPermission : mRequestedPermissions) { + String requestedPermGroup = + PermissionMapping.getGroupOfPlatformPermission(requestedPermission); + if (restrictedPermGroups.contains(requestedPermGroup) + || isPermissionEcmRestricted(ecm, requestedPermission, packageName)) { + restrictedPermGroups.add(requestedPermGroup); + } else { + unrestrictedPermissions.add(requestedPermission); + } + } + mRestrictedRequestedPermissionGroups = new ArrayList<>(restrictedPermGroups); + mUnrestrictedRequestedPermissions = unrestrictedPermissions; + } + // If there are remaining restricted permission groups to process, show the ECM dialog + // for the next one, then recreate this activity. + if (!mRestrictedRequestedPermissionGroups.isEmpty()) { + String nextRestrictedPermissionGroup = mRestrictedRequestedPermissionGroups.remove( + 0); + try { + Intent intent = ecm.createRestrictedSettingDialogIntent(getPackageName(), + nextRestrictedPermissionGroup); + startActivityForResult(intent, ECM_REQUEST_CODE); + return; + } catch (PackageManager.NameNotFoundException e) { + mRequestedPermissions = mUnrestrictedRequestedPermissions; + } + } else { + mRequestedPermissions = mUnrestrictedRequestedPermissions; + } + } + synchronized (sCurrentGrantRequests) { mKey = new Pair<>(mTargetPackage, getTaskId()); if (!sCurrentGrantRequests.containsKey(mKey)) { @@ -390,6 +463,20 @@ public class GrantPermissionsActivity extends SettingsActivity } } + private EnhancedConfirmationManager getEnhancedConfirmationManager() { + Context userContext = Utils.getUserContext(this, Process.myUserHandle()); + return Utils.getSystemServiceSafe(userContext, EnhancedConfirmationManager.class); + } + + private boolean isPermissionEcmRestricted(EnhancedConfirmationManager ecm, + String requestedPermission, String packageName) { + try { + return ecm.isRestricted(packageName, requestedPermission); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + /** * A new GrantPermissionsActivity has opened for this same package. Merge its requested * permissions with the original ones set in the intent, and recalculate the grant states. @@ -713,6 +800,15 @@ public class GrantPermissionsActivity extends SettingsActivity protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); + if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) { + outState.putStringArrayList(KEY_RESTRICTED_REQUESTED_PERMISSIONS, new ArrayList<>( + mRestrictedRequestedPermissionGroups)); + outState.putStringArrayList(KEY_UNRESTRICTED_REQUESTED_PERMISSIONS, new ArrayList<>( + mUnrestrictedRequestedPermissions)); + outState.putStringArray(KEY_ORIGINAL_REQUESTED_PERMISSIONS, + mOriginalRequestedPermissions); + } + if (mViewHandler == null || mViewModel == null) { return; } @@ -738,6 +834,12 @@ public class GrantPermissionsActivity extends SettingsActivity @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); + if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) { + if (requestCode == ECM_REQUEST_CODE) { + recreate(); + return; + } + } if (requestCode != APP_PERMISSION_REQUEST_CODE && requestCode != PHOTO_PICKER_REQUEST_CODE) { return; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt index 99f5c85e4..2350a5d71 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt @@ -35,6 +35,7 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERM import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED import com.android.permissioncontroller.R import com.android.permissioncontroller.auto.AutoSettingsFrameFragment +import com.android.permissioncontroller.auto.DrivingDecisionReminderService import com.android.permissioncontroller.permission.data.v33.PermissionDecision import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModel @@ -98,6 +99,7 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { requireArguments().containsKey(EXTRA_SOURCE) && (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION) ) { + DrivingDecisionReminderService.cancelNotification(requireActivity()) logDecisionReminderNotificationClicked() } val factory = diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt index 91b94da9a..dca1cbf3a 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt @@ -1371,6 +1371,7 @@ class GrantPermissionsViewModel( companion object { const val APP_PERMISSION_REQUEST_CODE = 1 const val PHOTO_PICKER_REQUEST_CODE = 2 + const val ECM_REQUEST_CODE = 3 const val SAVED_REQUEST_CODE_KEY = "saved_request_code" private const val STATE_UNKNOWN = 0 private const val STATE_GRANTED = 1 diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java index 5b21638f4..d9b4a9c44 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java @@ -35,7 +35,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.ImageView; @@ -155,7 +154,6 @@ public class RequestRoleFragment extends DialogFragment { View viewLayout = inflater.inflate(R.layout.request_role_view, null); mListView = viewLayout.requireViewById(R.id.list); - mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); mListView.setOnItemClickListener((parent, view, position, id) -> onItemClicked(position)); mAdapter = new Adapter(mListView, mRole); if (savedInstanceState != null) { @@ -415,9 +413,9 @@ public class RequestRoleFragment extends DialogFragment { // Skip the "None" item. continue; } - ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first; - if (Objects.equals(qualifyingApplicationInfo.packageName, packageName)) { - return qualifyingApplicationInfo.uid; + ApplicationInfo applicationInfo = qualifyingApplication.first; + if (Objects.equals(applicationInfo.packageName, packageName)) { + return applicationInfo.uid; } } return -1; @@ -440,19 +438,7 @@ public class RequestRoleFragment extends DialogFragment { if (mAdapter == null) { return null; } - int count = mAdapter.getCount(); - for (int i = 0; i < count; i++) { - Pair<ApplicationInfo, Boolean> qualifyingApplication = mAdapter.getItem(i); - if (qualifyingApplication == null) { - // Skip the "None" item. - continue; - } - boolean isHolderApplication = qualifyingApplication.second; - if (isHolderApplication) { - return qualifyingApplication.first.packageName; - } - } - return null; + return mAdapter.mHolderPackageName; } static void reportRequestResult(int requestingUid, String requestingPackageName, @@ -478,8 +464,8 @@ public class RequestRoleFragment extends DialogFragment { private static final String STATE_USER_CHECKED = Adapter.class.getName() + ".state.USER_CHECKED"; - private static final String STATE_USER_CHECKED_PACKAGE_NAME = Adapter.class.getName() - + ".state.USER_CHECKED_PACKAGE_NAME"; + private static final String STATE_CHECKED_PACKAGE_NAME = Adapter.class.getName() + + ".state.CHECKED_PACKAGE_NAME"; private static final int LAYOUT_TRANSITION_DURATION_MILLIS = 150; @@ -494,7 +480,8 @@ public class RequestRoleFragment extends DialogFragment { private final List<Pair<ApplicationInfo, Boolean>> mQualifyingApplications = new ArrayList<>(); - private boolean mHasHolderApplication; + @Nullable + private String mHolderPackageName; private boolean mDontAskAgain; @@ -502,10 +489,8 @@ public class RequestRoleFragment extends DialogFragment { // the current holder as checked. private boolean mUserChecked; - private boolean mPendingUserChecked; - // We may use a null to represent the "None" item. @Nullable - private String mPendingUserCheckedPackageName; + private String mCheckedPackageName; Adapter(@NonNull ListView listView, @NonNull Role role) { mListView = listView; @@ -515,15 +500,14 @@ public class RequestRoleFragment extends DialogFragment { public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBoolean(STATE_USER_CHECKED, mUserChecked); if (mUserChecked) { - outState.putString(STATE_USER_CHECKED_PACKAGE_NAME, getCheckedPackageName()); + outState.putString(STATE_CHECKED_PACKAGE_NAME, mCheckedPackageName); } } public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - mPendingUserChecked = savedInstanceState.getBoolean(STATE_USER_CHECKED); - if (mPendingUserChecked) { - mPendingUserCheckedPackageName = savedInstanceState.getString( - STATE_USER_CHECKED_PACKAGE_NAME); + mUserChecked = savedInstanceState.getBoolean(STATE_USER_CHECKED); + if (mUserChecked) { + mCheckedPackageName = savedInstanceState.getString(STATE_CHECKED_PACKAGE_NAME); } } @@ -534,14 +518,28 @@ public class RequestRoleFragment extends DialogFragment { mDontAskAgain = dontAskAgain; if (mDontAskAgain) { mUserChecked = false; - updateItemChecked(); + mCheckedPackageName = mHolderPackageName; } notifyDataSetChanged(); } public void onItemClicked(int position) { - mUserChecked = true; - // We may need to change description based on checked state. + Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position); + if (qualifyingApplication == null) { + mUserChecked = true; + mCheckedPackageName = null; + } else { + ApplicationInfo applicationInfo = qualifyingApplication.first; + Intent restrictionIntent = mRole.getApplicationRestrictionIntentAsUser( + applicationInfo, Process.myUserHandle(), mListView.getContext()); + if (restrictionIntent != null) { + mListView.getContext().startActivity(restrictionIntent); + return; + } else { + mUserChecked = true; + mCheckedPackageName = applicationInfo.packageName; + } + } notifyDataSetChanged(); } @@ -551,42 +549,10 @@ public class RequestRoleFragment extends DialogFragment { mQualifyingApplications.add(0, null); } mQualifyingApplications.addAll(qualifyingApplications); - mHasHolderApplication = hasHolderApplication(qualifyingApplications); - notifyDataSetChanged(); - - if (mPendingUserChecked) { - restoreItemChecked(); - mPendingUserChecked = false; - mPendingUserCheckedPackageName = null; - } - - if (!mUserChecked) { - updateItemChecked(); - } - } - - private static boolean hasHolderApplication( - @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) { - int qualifyingApplicationsSize = qualifyingApplications.size(); - for (int i = 0; i < qualifyingApplicationsSize; i++) { - Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get( - i); - boolean isHolderApplication = qualifyingApplication.second; - - if (isHolderApplication) { - return true; - } - } - return false; - } + mHolderPackageName = getHolderPackageName(qualifyingApplications); - private void restoreItemChecked() { - if (mPendingUserCheckedPackageName == null) { - if (mRole.shouldShowNone()) { - mUserChecked = true; - mListView.setItemChecked(0, true); - } - } else { + if (mUserChecked && mCheckedPackageName != null) { + boolean isCheckedPackageNameFound = false; int count = getCount(); for (int i = 0; i < count; i++) { Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(i); @@ -595,55 +561,52 @@ public class RequestRoleFragment extends DialogFragment { } String packageName = qualifyingApplication.first.packageName; - if (Objects.equals(packageName, mPendingUserCheckedPackageName)) { + if (Objects.equals(packageName, mCheckedPackageName)) { mUserChecked = true; - mListView.setItemChecked(i, true); + isCheckedPackageNameFound = true; break; } } + if (!isCheckedPackageNameFound) { + mUserChecked = false; + mCheckedPackageName = null; + } } + + if (!mUserChecked) { + mCheckedPackageName = mHolderPackageName; + } + + notifyDataSetChanged(); } - private void updateItemChecked() { - if (!mHasHolderApplication) { - if (mRole.shouldShowNone()) { - mListView.setItemChecked(0, true); - } else { - mListView.clearChoices(); + @Nullable + private static String getHolderPackageName( + @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) { + int qualifyingApplicationSize = qualifyingApplications.size(); + for (int i = 0; i < qualifyingApplicationSize; i++) { + Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get( + i); + if (qualifyingApplication == null) { + continue; } - } else { - int count = getCount(); - for (int i = 0; i < count; i++) { - Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(i); - if (qualifyingApplication == null) { - continue; - } - boolean isHolderApplication = qualifyingApplication.second; + ApplicationInfo applicationInfo = qualifyingApplication.first; + boolean isHolderApplication = qualifyingApplication.second; - if (isHolderApplication) { - mListView.setItemChecked(i, true); - break; - } + if (isHolderApplication) { + return applicationInfo.packageName; } } - } - - @Nullable - public Pair<ApplicationInfo, Boolean> getCheckedItem() { - int position = mListView.getCheckedItemPosition(); - return position != AdapterView.INVALID_POSITION ? getItem(position) : null; + return null; } @Nullable public String getCheckedPackageName() { - Pair<ApplicationInfo, Boolean> qualifyingApplication = getCheckedItem(); - return qualifyingApplication == null ? null : qualifyingApplication.first.packageName; + return mCheckedPackageName; } public boolean isHolderApplicationChecked() { - Pair<ApplicationInfo, Boolean> qualifyingApplication = getCheckedItem(); - return qualifyingApplication == null ? !mHasHolderApplication - : qualifyingApplication.second; + return Objects.equals(mCheckedPackageName, mHolderPackageName); } @Override @@ -685,7 +648,7 @@ public class RequestRoleFragment extends DialogFragment { } Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position); if (qualifyingApplication == null) { - return !mHasHolderApplication; + return mHolderPackageName == null; } else { boolean isHolderApplication = qualifyingApplication.second; return isHolderApplication; @@ -696,13 +659,13 @@ public class RequestRoleFragment extends DialogFragment { @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { Context context = parent.getContext(); - View view = convertView; + CheckableLinearLayout view = (CheckableLinearLayout) convertView; ViewHolder holder; if (view != null) { holder = (ViewHolder) view.getTag(); } else { - view = LayoutInflater.from(context).inflate(R.layout.request_role_item, parent, - false); + view = (CheckableLinearLayout) LayoutInflater.from(context).inflate( + R.layout.request_role_item, parent, false); holder = new ViewHolder(view); view.setTag(holder); @@ -710,28 +673,36 @@ public class RequestRoleFragment extends DialogFragment { LAYOUT_TRANSITION_DURATION_MILLIS); } - UiUtils.setViewTreeEnabled(view, isEnabled(position)); - Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position); + boolean restricted; + boolean checked; Drawable icon; String title; String subtitle; if (qualifyingApplication == null) { + restricted = false; + checked = mCheckedPackageName == null; icon = AppCompatResources.getDrawable(context, R.drawable.ic_remove_circle); title = context.getString(R.string.default_app_none); - subtitle = !mHasHolderApplication ? context.getString( + subtitle = mHolderPackageName != null ? context.getString( R.string.request_role_current_default) : null; } else { - ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first; - icon = Utils.getBadgedIcon(context, qualifyingApplicationInfo); - title = Utils.getAppLabel(qualifyingApplicationInfo, context); + ApplicationInfo applicationInfo = qualifyingApplication.first; + restricted = mRole.getApplicationRestrictionIntentAsUser(applicationInfo, + Process.myUserHandle(), context) != null; + checked = Objects.equals(applicationInfo.packageName, mCheckedPackageName); + icon = Utils.getBadgedIcon(context, applicationInfo); + title = Utils.getAppLabel(applicationInfo, context); boolean isHolderApplication = qualifyingApplication.second; subtitle = isHolderApplication ? context.getString(R.string.request_role_current_default) - : mListView.isItemChecked(position) - ? context.getString(mRole.getRequestDescriptionResource()) : null; + : checked ? context.getString(mRole.getRequestDescriptionResource()) : null; } + boolean enabled = isEnabled(position); + UiUtils.setViewTreeEnabled(view, enabled && !restricted); + view.setEnabled(enabled); + view.setChecked(checked); holder.iconImage.setImageDrawable(icon); holder.titleText.setText(title); holder.subtitleText.setVisibility(!TextUtils.isEmpty(subtitle) ? View.VISIBLE diff --git a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java index 1b7398251..74062165e 100644 --- a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java +++ b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java @@ -33,7 +33,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.RemoteException; import android.permission.flags.Flags; -import android.provider.Settings; import android.util.ArraySet; import androidx.annotation.NonNull; @@ -343,7 +342,7 @@ public final class EnhancedConfirmationManager { */ public @NonNull Intent createRestrictedSettingDialogIntent(@NonNull String packageName, @NonNull String settingIdentifier) throws NameNotFoundException { - Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG); + Intent intent = new Intent(ACTION_SHOW_ECM_RESTRICTED_SETTING_DIALOG); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); intent.putExtra(Intent.EXTRA_UID, getPackageUid(packageName)); intent.putExtra(Intent.EXTRA_SUBJECT, settingIdentifier); diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java index 2e8221399..4b866157a 100644 --- a/service/java/com/android/ecm/EnhancedConfirmationService.java +++ b/service/java/com/android/ecm/EnhancedConfirmationService.java @@ -16,6 +16,7 @@ package com.android.ecm; +import android.Manifest; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.UserIdInt; @@ -114,6 +115,24 @@ public class EnhancedConfirmationService extends SystemService { private static final ArraySet<String> PROTECTED_SETTINGS = new ArraySet<>(); static { + // Runtime permissions + // TODO(b/310654818): Construct this list by permission group instead of by permission + PROTECTED_SETTINGS.add(Manifest.permission.READ_PHONE_STATE); + PROTECTED_SETTINGS.add(Manifest.permission.READ_PHONE_NUMBERS); + PROTECTED_SETTINGS.add(Manifest.permission.CALL_PHONE); + PROTECTED_SETTINGS.add(Manifest.permission.ADD_VOICEMAIL); + PROTECTED_SETTINGS.add(Manifest.permission.USE_SIP); + PROTECTED_SETTINGS.add(Manifest.permission.ANSWER_PHONE_CALLS); + PROTECTED_SETTINGS.add(Manifest.permission.ACCEPT_HANDOVER); + + PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS); + PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_SMS); + PROTECTED_SETTINGS.add(Manifest.permission.READ_SMS); + PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_MMS); + PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_WAP_PUSH); + PROTECTED_SETTINGS.add(Manifest.permission.READ_CELL_BROADCASTS); + // TODO(b/310654818): Add other explicitly protected runtime permissions + // App ops PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); // Default application roles. @@ -125,6 +144,8 @@ public class EnhancedConfirmationService extends SystemService { PROTECTED_SETTINGS.add(RoleManager.ROLE_HOME); PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS); PROTECTED_SETTINGS.add(RoleManager.ROLE_WALLET); + // Other settings + PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); // TODO(b/310654015): Add other explicitly protected settings } @@ -324,10 +345,15 @@ public class EnhancedConfirmationService extends SystemService { } private boolean isSettingEcmProtected(@NonNull String settingIdentifier) { + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + return false; + } + if (PROTECTED_SETTINGS.contains(settingIdentifier)) { return true; } - // TODO(b/310654818): If this is a permission, coerce it into a PermissionGroup. // TODO(b/310218979): Add role selections as protected settings return false; } diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml index 61acf280d..ef85fdf60 100644 --- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml +++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml @@ -7790,7 +7790,7 @@ @SystemApi @hide --> <permission android:name="android.permission.GET_APP_METADATA" - android:protectionLevel="signature|installer" /> + android:protectionLevel="signature|installer|verifier" /> <!-- @hide @SystemApi Allows an application to stage HealthConnect's remote data so that HealthConnect can later integrate it. --> diff --git a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml index d87fe65b8..4729d44c2 100644 --- a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml +++ b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml @@ -218,10 +218,10 @@ android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_read_impact_sensors" android:description="@string/car_permission_desc_read_impact_sensors"/> - <permission android:name="android.car.permission.READ_HEAD_UP_DISPLAY" + <permission android:name="android.car.permission.READ_HEAD_UP_DISPLAY_STATUS" android:protectionLevel="signature|privileged" - android:label="@string/car_permission_label_read_head_up_display" - android:description="@string/car_permission_desc_read_head_up_display"/> + android:label="@string/car_permission_label_read_head_up_display_status" + android:description="@string/car_permission_desc_read_head_up_display_status"/> <permission android:name="android.car.permission.CONTROL_HEAD_UP_DISPLAY" android:protectionLevel="signature|privileged" android:label="@string/car_permission_label_control_head_up_display" diff --git a/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml index 49e45fc01..0b92f5ef1 100644 --- a/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml +++ b/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml @@ -20,6 +20,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="android.permissionui.cts.usepermission"> + <uses-permission android:name="android.permission.READ_PHONE_STATE"/> + <uses-permission android:name="android.permission.CALL_PHONE"/> + <!-- Request two different permissions within the same group --> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt index 6f80ea5d6..8c300e328 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt @@ -281,9 +281,13 @@ abstract class BasePermissionTest { protected fun installPackageViaSession( apkName: String, appMetadata: PersistableBundle? = null, - packageSource: Int? = null + packageSource: Int? = null, + allowlistedRestrictedPermissions: Set<String>? = null ) { - val (sessionId, session) = createPackageInstallerSession(packageSource) + val (sessionId, session) = createPackageInstallerSession( + packageSource, + allowlistedRestrictedPermissions + ) runWithShellPermissionIdentity { writePackageInstallerSession(session, apkName) if (appMetadata != null) { @@ -448,10 +452,15 @@ abstract class BasePermissionTest { } private fun createPackageInstallerSession( - packageSource: Int? = null + packageSource: Int? = null, + allowlistedRestrictedPermissions: Set<String>? = null ): Pair<Int, PackageInstaller.Session> { // Create session val sessionParam = SessionParams(SessionParams.MODE_FULL_INSTALL) + allowlistedRestrictedPermissions?.let { + sessionParam.setWhitelistedRestrictedPermissions(it) + } + if (packageSource != null) { sessionParam.setPackageSource(packageSource) } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt index d24f4fd27..42555c2d7 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt @@ -29,6 +29,7 @@ import android.content.pm.PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE import android.content.pm.PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE import android.content.pm.PackageInstaller.PACKAGE_SOURCE_OTHER import android.content.pm.PackageInstaller.PACKAGE_SOURCE_STORE +import android.content.pm.PackageInstaller.SessionParams import android.content.pm.PackageManager import android.net.Uri import android.os.Build @@ -63,6 +64,7 @@ import org.junit.Before abstract class BaseUsePermissionTest : BasePermissionTest() { companion object { const val APP_APK_NAME_31 = "CtsUsePermissionApp31.apk" + const val APP_APK_NAME_LATEST = "CtsUsePermissionAppLatest.apk" const val APP_APK_PATH_22 = "$APK_DIRECTORY/CtsUsePermissionApp22.apk" const val APP_APK_PATH_22_CALENDAR_ONLY = @@ -443,6 +445,17 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { ) } + protected fun installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( + apkName: String + ) { + installPackageViaSession( + apkName, + AppMetadata.createDefaultAppMetadata(), + PACKAGE_SOURCE_DOWNLOADED_FILE, + allowlistedRestrictedPermissions = SessionParams.RESTRICTED_PERMISSIONS_ALL + ) + } + protected fun installPackageWithInstallSourceAndMetadataFromOther(apkName: String) { installPackageViaSession( apkName, diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt index 4d1118a6f..4a505d1fd 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt @@ -16,6 +16,7 @@ package android.permissionui.cts +import android.Manifest import android.app.AppOpsManager import android.app.Instrumentation import android.app.ecm.EnhancedConfirmationManager @@ -29,11 +30,14 @@ import android.platform.test.flag.junit.CheckFlagsRule import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By import com.android.compatibility.common.util.SystemUtil.eventually import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue +import org.junit.Assume +import org.junit.Before import org.junit.Rule import org.junit.Test @@ -41,8 +45,6 @@ import org.junit.Test @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { - - private val apkName = APP_APK_NAME_31 private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val context: Context = instrumentation.targetContext private val ecm by lazy { context.getSystemService(EnhancedConfirmationManager::class.java)!! } @@ -51,10 +53,19 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @JvmField val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before + fun assumeNotAutoTvOrWear() { + Assume.assumeFalse(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) + Assume.assumeFalse( + context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + ) + Assume.assumeFalse(context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) + } + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenStoreAppThenIsNotRestrictedFromProtectedSetting() { - installPackageWithInstallSourceAndMetadataFromStore(apkName) + installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } } @@ -63,7 +74,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenLocalAppThenIsRestrictedFromProtectedSetting() { - installPackageWithInstallSourceAndMetadataFromLocalFile(apkName) + installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } } @@ -72,7 +83,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenDownloadedThenAppIsRestrictedFromProtectedSetting() { - installPackageWithInstallSourceAndMetadataFromDownloadedFile(apkName) + installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } } @@ -81,7 +92,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenExplicitlyRestrictedAppThenIsRestrictedFromProtectedSetting() { - installPackageWithInstallSourceAndMetadataFromStore(apkName) + installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } setAppEcmState(context, APP_PACKAGE_NAME, MODE_ERRORED) @@ -92,7 +103,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenRestrictedAppThenIsNotRestrictedFromNonProtectedSetting() { - installPackageWithInstallSourceAndMetadataFromDownloadedFile(apkName) + installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, NON_PROTECTED_SETTING)) } } @@ -101,7 +112,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenRestrictedAppThenClearRestrictionNotAllowedByDefault() { - installPackageWithInstallSourceAndMetadataFromDownloadedFile(apkName) + installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertFalse(ecm.isClearRestrictionAllowed(APP_PACKAGE_NAME)) } } @@ -110,7 +121,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenRestrictedAppWhenClearRestrictionThenNotRestrictedFromProtectedSetting() { - installPackageWithInstallSourceAndMetadataFromDownloadedFile(apkName) + installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) runWithShellPermissionIdentity { eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } ecm.setClearRestrictionAllowed(APP_PACKAGE_NAME) @@ -123,14 +134,113 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun createRestrictedSettingDialogIntentReturnsIntent() { - installPackageWithInstallSourceAndMetadataFromDownloadedFile(apkName) + installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) val intent = ecm.createRestrictedSettingDialogIntent(APP_PACKAGE_NAME, PROTECTED_SETTING) assertNotNull(intent) } + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + @Test + fun grantDialogBlocksRestrictedPermissionsOfSameGroupTogether() { + installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( + APP_APK_NAME_LATEST + ) + + requestAppPermissionsAndAssertResult( + GROUP_1_PERMISSION_1_RESTRICTED to false, + GROUP_1_PERMISSION_2_RESTRICTED to false + ) { + click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) + } + } + + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + @Test + fun grantDialogBlocksRestrictedPermissionsOfDifferentGroupsIndividually() { + installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( + APP_APK_NAME_LATEST + ) + + requestAppPermissionsAndAssertResult( + GROUP_1_PERMISSION_1_RESTRICTED to false, + GROUP_2_PERMISSION_1_RESTRICTED to false, + waitForWindowTransition = false + ) { + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + } + } + + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + @Test + fun grantDialogBlocksRestrictedGroupsThenRequestsUnrestrictedGroupsDespiteOutOfOrderRequest() { + installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( + APP_APK_NAME_LATEST + ) + + requestAppPermissionsAndAssertResult( + GROUP_3_PERMISSION_1_UNRESTRICTED to true, + GROUP_2_PERMISSION_1_RESTRICTED to false, + GROUP_3_PERMISSION_2_UNRESTRICTED to true, + GROUP_2_PERMISSION_2_RESTRICTED to false, + waitForWindowTransition = false + ) { + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() } + } + } + + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + @Test + fun grantDialogBlocksRestrictedGroupsThenRequestsUnrestrictedHighPriorityGroups() { + installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( + APP_APK_NAME_LATEST + ) + + requestAppPermissionsAndAssertResult( + GROUP_3_PERMISSION_1_UNRESTRICTED to true, + GROUP_2_PERMISSION_1_RESTRICTED to false, + GROUP_1_PERMISSION_1_RESTRICTED to false, + waitForWindowTransition = false + ) { + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() } + } + } + + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + @Test + fun grantDialogBlocksRestrictedGroupsThenRequestsUnrestrictedLowPriorityGroups() { + installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( + APP_APK_NAME_LATEST + ) + + requestAppPermissionsAndAssertResult( + GROUP_4_PERMISSION_1_UNRESTRICTED to true, + GROUP_2_PERMISSION_1_RESTRICTED to false, + GROUP_1_PERMISSION_1_RESTRICTED to false, + waitForWindowTransition = false + ) { + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) } + doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() } + } + } + companion object { + private const val GROUP_1_PERMISSION_1_RESTRICTED = Manifest.permission.CALL_PHONE + private const val GROUP_1_PERMISSION_2_RESTRICTED = Manifest.permission.READ_PHONE_STATE + private const val GROUP_2_PERMISSION_1_RESTRICTED = Manifest.permission.SEND_SMS + private const val GROUP_2_PERMISSION_2_RESTRICTED = Manifest.permission.READ_SMS + private const val GROUP_3_PERMISSION_1_UNRESTRICTED = + Manifest.permission.ACCESS_FINE_LOCATION + private const val GROUP_3_PERMISSION_2_UNRESTRICTED = + Manifest.permission.ACCESS_COARSE_LOCATION + private const val GROUP_4_PERMISSION_1_UNRESTRICTED = Manifest.permission.BODY_SENSORS + private const val NON_PROTECTED_SETTING = "example_setting_which_is_not_protected" private const val PROTECTED_SETTING = "android:bind_accessibility_service" private const val MODE_ERRORED = 2 diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java index 4b618a1d7..fe9b8d578 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java +++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java @@ -233,6 +233,25 @@ public class RoleManagerTest { assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true); } + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO") + public void requestRoleAndSelectRestrictedAppThenRestrictedSettingDialog() throws Exception { + assumeFalse(sIsWatch || sIsAutomotive || sIsTelevision); + runWithShellPermissionIdentity( + () -> setEnhancedConfirmationRestrictedAppOpMode(sContext, APP_PACKAGE_NAME, + AppOpsManager.MODE_ERRORED)); + + requestRole(ROLE_NAME); + waitFindObject(By.text(APP_LABEL).enabled(false)) + .clickAndWait(Until.newWindow(), TIMEOUT_MILLIS); + waitFindObject(By.textContains("Restricted setting"), TIMEOUT_MILLIS); + pressBack(); + respondToRoleRequest(false); + } + @Test @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO") public void requestRoleFirstTimeNoDontAskAgain() throws Exception { @@ -636,6 +655,8 @@ public class RoleManagerTest { pressBack(); } + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") @Test @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO") @@ -653,11 +674,11 @@ public class RoleManagerTest { .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK))); - waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).enabled(false).checked( - false)).hasDescendant(By.text(APP_LABEL))).clickAndWait(Until.newWindow(), + waitFindObject(By.text(APP_LABEL).enabled(false)).clickAndWait(Until.newWindow(), TIMEOUT_MILLIS); - waitFindObject(By.textContains("Restricted setting"), UNEXPECTED_TIMEOUT_MILLIS); + waitFindObject(By.textContains("Restricted setting"), TIMEOUT_MILLIS); + pressBack(); pressBack(); } |