summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/res/values/styles.xml1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java102
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java191
-rw-r--r--framework-s/java/android/app/ecm/EnhancedConfirmationManager.java3
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationService.java28
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml2
-rw-r--r--tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml6
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml3
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt15
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt13
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt130
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerTest.java27
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();
}