From 03a8c27d927cb0ca0ce60fdfd1b2baa8b7200346 Mon Sep 17 00:00:00 2001 From: Aseem Kumar Date: Wed, 16 Apr 2025 11:22:07 -0700 Subject: [PATCH 01/13] Add ComponentName explicitly to make sure arbitary intents aren't launched from Settings. Bug: 378902342 Flag: EXEMPT security fix (cherry picked from commit 6a896b6b26d445800773e1b4649895bea17eac1f) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:947f4571bcd7aef74fd70d533fa26e06e088dab3) Merged-In: I0e67f1258cb427c5b998e40a8a0c104af3ead042 Change-Id: I0e67f1258cb427c5b998e40a8a0c104af3ead042 --- .../settings/accounts/AccountTypePreferenceLoader.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java index 7cfeb1cc193..a90bea09747 100644 --- a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java +++ b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java @@ -264,7 +264,14 @@ public class AccountTypePreferenceLoader { try { // Allows to launch only authenticator owned activities. ApplicationInfo authenticatorAppInf = pm.getApplicationInfo(authDesc.packageName, 0); - return resolvedAppInfo.uid == authenticatorAppInf.uid; + if (resolvedAppInfo.uid == authenticatorAppInf.uid) { + // Explicitly set the component to be same as authenticator to + // prevent launching arbitrary activities. + intent.setComponent(resolvedActivityInfo.getComponentName()); + return true; + } else { + return false; + } } catch (NameNotFoundException e) { Log.e(TAG, "Intent considered unsafe due to exception.", -- GitLab From 9ac49c8f41424a32b1adce16ad8d722656e54bcd Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Mon, 17 Mar 2025 14:34:08 -0700 Subject: [PATCH 02/13] AppRestrictions - use vetted component After vetting the intent, use the component we used for the vetting. Bug: 353680402 Bug: 365739560 Test: manual Flag: EXEMPT bugfix (cherry picked from commit d3e34060803c97ae05719fe9301026e5c54892c8) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3abc4e193a1757cacdcd20090f538f090853c16d) Merged-In: Iff0d820c1261c29eb6703bf89194339cba700688 Change-Id: Iff0d820c1261c29eb6703bf89194339cba700688 --- .../users/AppRestrictionsFragment.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index 0676ec894d9..379e4bbaec6 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -643,8 +643,11 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen } else if (restrictionsIntent != null) { preference.setRestrictions(restrictions); if (invokeIfCustom && AppRestrictionsFragment.this.isResumed()) { + // We don't necessarily trust the given intent to launch its component. + // We will first check it, and only use parts of it that were indeed checked. + final Intent vettedIntent; try { - assertSafeToStartCustomActivity(restrictionsIntent); + vettedIntent = assertSafeToStartCustomActivity(restrictionsIntent); } catch (ActivityNotFoundException | SecurityException e) { // return without startActivity Log.e(TAG, "Cannot start restrictionsIntent " + e); @@ -655,12 +658,16 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen int requestCode = generateCustomActivityRequestCode( RestrictionsResultReceiver.this.preference); AppRestrictionsFragment.this.startActivityForResult( - new Intent(restrictionsIntent), requestCode); + vettedIntent, requestCode); } } } - private void assertSafeToStartCustomActivity(Intent intent) { + /** + * Checks that it is safe to start the custom activity, and, if so, returns a copy of the + * Intent using its vetted components. + */ + private Intent assertSafeToStartCustomActivity(Intent intent) { EventLog.writeEvent(0x534e4554, "223578534", -1 /* UID */, ""); ResolveInfo resolveInfo = mPackageManager.resolveActivity( intent, PackageManager.MATCH_DEFAULT_ONLY); @@ -674,6 +681,13 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen throw new SecurityException("Application " + packageName + " is not allowed to start activity " + intent); } + + // We were able to vet the given intent this time. Make a copy using the components + // that were used to do the vetting, since that's as much as we've verified is safe. + final Intent vettedIntent = new Intent(intent); + vettedIntent.setComponent(activityInfo.getComponentName()); + vettedIntent.setPackage(activityInfo.packageName); + return vettedIntent; } } -- GitLab From 0ec7e0d956926777234669bba4802ea65e1fd6a4 Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Sat, 5 Apr 2025 02:30:30 +0000 Subject: [PATCH 03/13] Drop PendingIntent extras from external packages during enrollment. Bug: 388528350 Flag: EXEMPT bugfix Test: atest FingerprintEnrollIntroductionTest FaceEnrollIntroductionTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4ccdeee849d5fef78498ba33cadc525523efcbd7) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3f44c3b1d7a528640849b4085e0d7ca9e1e0f7dc) Merged-In: I61281dcf95e53100a96d6a218f3f00fd1b4ea3f9 Change-Id: I61281dcf95e53100a96d6a218f3f00fd1b4ea3f9 --- .../biometrics/BiometricEnrollIntroduction.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java index e3607601840..d1987d6ee56 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java @@ -47,6 +47,8 @@ import com.google.android.setupdesign.span.LinkSpan; import com.google.android.setupdesign.template.RequireScrollMixin; import com.google.android.setupdesign.util.DynamicColorPalette; +import java.util.List; + /** * Abstract base class for the intro onboarding activity for biometric enrollment. */ @@ -224,6 +226,18 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase }); } + @Override + protected void onStart() { + super.onStart(); + + if (!getPackageName().equals(getCallingPackage())) { + for (String key : List.of(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, + MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE)) { + getIntent().removeExtra(key); + } + } + } + @Override protected void onResume() { super.onResume(); -- GitLab From a266b616fe620c31b27bcc3a345690543bc61e01 Mon Sep 17 00:00:00 2001 From: Rubin Xu Date: Wed, 21 May 2025 15:34:51 +0100 Subject: [PATCH 04/13] Use correct API to get calling package name in CredentialStorage Activity.getCallingPackage() does not always return the package name of the actual calling app. getLaunchedFromPackage() should be used instead. Bug: 389681530 Test: manual Flag: EXEMPT bugfix (cherry picked from commit 70bd3efe0674bccb0d454845d86fb2402779a7bf) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:131e19e3fdf184332015e2592cff3467a360ff99) Merged-In: Ibdbc45e53f4aa46fae79fa234705b3735bfda4cd Change-Id: Ibdbc45e53f4aa46fae79fa234705b3735bfda4cd --- .../settings/security/CredentialStorage.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/security/CredentialStorage.java b/src/com/android/settings/security/CredentialStorage.java index ea336314566..57a59cd287f 100644 --- a/src/com/android/settings/security/CredentialStorage.java +++ b/src/com/android/settings/security/CredentialStorage.java @@ -17,6 +17,7 @@ package com.android.settings.security; import android.app.Activity; +import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.DialogInterface; @@ -331,15 +332,25 @@ public final class CredentialStorage extends FragmentActivity { } } + private String getCallingPackageName() { + try { + return ActivityManager.getService().getLaunchedFromPackage(getActivityToken()); + } catch (RemoteException re) { + // Error talking to ActivityManager, just give up + return null; + } + } + /** * Check that the caller is either certinstaller or Settings running in a profile of this user. */ private boolean checkCallerIsCertInstallerOrSelfInProfile() { - if (TextUtils.equals("com.android.certinstaller", getCallingPackage())) { + String callingPackage = getCallingPackageName(); + if (TextUtils.equals("com.android.certinstaller", callingPackage)) { // CertInstaller is allowed to install credentials if it has the same signature as // Settings package. return getPackageManager().checkSignatures( - getCallingPackage(), getPackageName()) == PackageManager.SIGNATURE_MATCH; + callingPackage, getPackageName()) == PackageManager.SIGNATURE_MATCH; } final int launchedFromUserId; -- GitLab From 699bee9c6561e49793d2186503069f640ef0b9f4 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 12 May 2025 14:50:40 -0400 Subject: [PATCH 05/13] Hide notification content in history - if the user is locked - and the user has chosen to hide sensistive content when locked Test: manual with a work profile with a different pin Bug: 378088320 Flag: EXEMPT bug fix (cherry picked from commit 9df37c3f8be2dedd2e44e52da4de45fba33c6a6e) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:360bf692d0165f1ee6cdcef4e116eec5da2a1a6f) Merged-In: Ia70454d9859fb788ffa1f48f88760f88c354cdff Change-Id: Ia70454d9859fb788ffa1f48f88760f88c354cdff --- .../history/NotificationHistoryActivity.java | 32 ++++++++++++++++--- .../history/NotificationHistoryAdapter.java | 22 ++++++++++--- .../history/NotificationSbnAdapter.java | 15 +++++++-- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java index 4fecfc9b722..9786a99b9de 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java +++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java @@ -16,6 +16,7 @@ package com.android.settings.notification.history; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; import static androidx.core.view.accessibility.AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED; @@ -25,9 +26,11 @@ import android.annotation.ColorInt; import android.app.ActionBar; import android.app.ActivityManager; import android.app.INotificationManager; +import android.app.KeyguardManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Outline; @@ -56,6 +59,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.NotificationExpandButton; import com.android.settings.R; import com.android.settings.notification.NotificationBackend; @@ -66,6 +70,7 @@ import com.android.settingslib.widget.OnMainSwitchChangeListener; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -106,6 +111,9 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { }; private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + // List of users that have the setting "hide sensitive content" enabled on the lockscreen + private ArrayList mContentRestrictedUsers = new ArrayList<>(); + enum NotificationHistoryEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "User turned on notification history") NOTIFICATION_HISTORY_ON(504), @@ -204,7 +212,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { final NotificationHistoryRecyclerView rv = viewForPackage.findViewById(R.id.notification_list); - rv.setAdapter(new NotificationHistoryAdapter(mNm, rv, + rv.setAdapter(new NotificationHistoryAdapter(NotificationHistoryActivity.this, mNm, rv, newCount -> { count.setText(getResources().getQuantityString( R.plurals.notification_history_count, @@ -212,7 +220,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { if (newCount == 0) { viewForPackage.setVisibility(View.GONE); } - }, mUiEventLogger)); + }, mUiEventLogger, mContentRestrictedUsers)); ((NotificationHistoryAdapter) rv.getAdapter()).onRebuildComplete( new ArrayList<>(nhp.notifications)); @@ -254,6 +262,21 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { mPm = getPackageManager(); mUm = getSystemService(UserManager.class); + + mContentRestrictedUsers.clear(); + List users = mUm.getProfiles(getUserId()); + mContentRestrictedUsers.clear(); + for (UserInfo user : users) { + if (Settings.Secure.getIntForUser(getContentResolver(), + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, user.id) == 0) { + LockPatternUtils lpu = new LockPatternUtils(this); + KeyguardManager km = getSystemService(KeyguardManager.class); + if (lpu.isSecure(user.id) && km.isDeviceLocked(user.id)) { + mContentRestrictedUsers.add(user.id); + } + } + } + // wait for history loading and recent/snooze loading mCountdownLatch = new CountDownLatch(2); @@ -308,6 +331,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { if (mCountdownFuture != null) { mCountdownFuture.cancel(true); } + mContentRestrictedUsers.clear(); super.onDestroy(); } @@ -398,7 +422,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { mSnoozedRv.setLayoutManager(lm); mSnoozedRv.setAdapter( new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm, mUm, - true, mUiEventLogger)); + true, mUiEventLogger, mContentRestrictedUsers)); mSnoozedRv.setNestedScrollingEnabled(false); if (snoozed == null || snoozed.length == 0) { @@ -414,7 +438,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { mDismissedRv.setLayoutManager(dismissLm); mDismissedRv.setAdapter( new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm, mUm, - false, mUiEventLogger)); + false, mUiEventLogger, mContentRestrictedUsers)); mDismissedRv.setNestedScrollingEnabled(false); if (dismissed == null || dismissed.length == 0) { diff --git a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java index 96bc14a6fb2..b1fd4c63c67 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java +++ b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java @@ -22,6 +22,7 @@ import static android.provider.Settings.EXTRA_CONVERSATION_ID; import android.app.INotificationManager; import android.app.NotificationHistory.HistoricalNotification; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.RemoteException; @@ -52,16 +53,23 @@ public class NotificationHistoryAdapter extends private List mValues; private OnItemDeletedListener mListener; private UiEventLogger mUiEventLogger; - public NotificationHistoryAdapter(INotificationManager nm, + private ArrayList mContentRestrictedUsers = new ArrayList<>(); + Context mContext; + + public NotificationHistoryAdapter(Context context, + INotificationManager nm, NotificationHistoryRecyclerView listView, OnItemDeletedListener listener, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + ArrayList contentRestrictedUsers) { + mContext = context; mValues = new ArrayList<>(); setHasStableIds(true); listView.setOnItemSwipeDeleteListener(this); mNm = nm; mListener = listener; mUiEventLogger = uiEventLogger; + mContentRestrictedUsers = contentRestrictedUsers; } @Override @@ -80,8 +88,14 @@ public class NotificationHistoryAdapter extends public void onBindViewHolder(final @NonNull NotificationHistoryViewHolder holder, int position) { final HistoricalNotification hn = mValues.get(position); - holder.setTitle(hn.getTitle()); - holder.setSummary(hn.getText()); + // Redact sensitive notification content if needed + if (mContentRestrictedUsers.contains(hn.getUserId())) { + holder.setSummary(mContext.getString( + com.android.internal.R.string.notification_hidden_text)); + } else { + holder.setTitle(hn.getTitle()); + holder.setSummary(hn.getText()); + } holder.setPostedTime(hn.getPostedTimeMs()); final View.OnClickListener onClick = v -> { mUiEventLogger.logWithPosition(NotificationHistoryActivity.NotificationHistoryEvent diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java index 1d61ae79cd0..9bb9a563ec4 100644 --- a/src/com/android/settings/notification/history/NotificationSbnAdapter.java +++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java @@ -74,9 +74,11 @@ public class NotificationSbnAdapter extends private List mEnabledProfiles = new ArrayList<>(); private boolean mIsSnoozed; private UiEventLogger mUiEventLogger; + private ArrayList mContentRestrictedUsers = new ArrayList<>(); public NotificationSbnAdapter(Context context, PackageManager pm, UserManager um, - boolean isSnoozed, UiEventLogger uiEventLogger) { + boolean isSnoozed, UiEventLogger uiEventLogger, + ArrayList contentRestrictedUsers) { mContext = context; mPm = pm; mUserBadgeCache = new HashMap<>(); @@ -97,6 +99,7 @@ public class NotificationSbnAdapter extends // If true, this is the panel for snoozed notifs, otherwise the one for dismissed notifs. mIsSnoozed = isSnoozed; mUiEventLogger = uiEventLogger; + mContentRestrictedUsers = contentRestrictedUsers; } @Override @@ -114,8 +117,14 @@ public class NotificationSbnAdapter extends holder.setIconBackground(loadBackground(sbn)); holder.setIcon(loadIcon(sbn)); holder.setPackageLabel(loadPackageLabel(sbn.getPackageName()).toString()); - holder.setTitle(getTitleString(sbn.getNotification())); - holder.setSummary(getTextString(mContext, sbn.getNotification())); + // If the notification is from a content restricted user, show generic text. + if (mContentRestrictedUsers.contains(sbn.getNormalizedUserId())) { + holder.setSummary(mContext.getString( + com.android.internal.R.string.notification_hidden_text)); + } else { + holder.setTitle(getTitleString(sbn.getNotification())); + holder.setSummary(getTextString(mContext, sbn.getNotification())); + } holder.setPostedTime(sbn.getPostTime()); holder.setDividerVisible(position < (mValues.size() -1)); int userId = normalizeUserId(sbn); -- GitLab From f4c0cc872f77451853b70c13712a837fa0ca676c Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Wed, 11 Dec 2024 17:22:46 -0800 Subject: [PATCH 06/13] startActivityForResult with earlier new Intent We already make sure to use a copy of the Intent, but now we do so earlier. See bug. Bug: 353680402 Flag: EXEMPT bugfix Test: manual Test: atest com.android.settings.users.UserSettingsTest com.android.settings.users.UserDetailsSettingsTest (cherry picked from commit b7240e2f0c50455a1c8f3ae1fc4f27d55b86e89b) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f86a8b75b191c7025599ea6911abf3aa36bb2056) Merged-In: I860e9e606de6b8d3c99fa52a63b72ba7a99ce179 Change-Id: I860e9e606de6b8d3c99fa52a63b72ba7a99ce179 --- src/com/android/settings/users/AppRestrictionsFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index 379e4bbaec6..60b7925967d 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -669,8 +669,9 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen */ private Intent assertSafeToStartCustomActivity(Intent intent) { EventLog.writeEvent(0x534e4554, "223578534", -1 /* UID */, ""); + final Intent vettedIntent = new Intent(intent); ResolveInfo resolveInfo = mPackageManager.resolveActivity( - intent, PackageManager.MATCH_DEFAULT_ONLY); + vettedIntent, PackageManager.MATCH_DEFAULT_ONLY); if (resolveInfo == null) { throw new ActivityNotFoundException("No result for resolving " + intent); @@ -684,7 +685,6 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen // We were able to vet the given intent this time. Make a copy using the components // that were used to do the vetting, since that's as much as we've verified is safe. - final Intent vettedIntent = new Intent(intent); vettedIntent.setComponent(activityInfo.getComponentName()); vettedIntent.setPackage(activityInfo.packageName); return vettedIntent; -- GitLab From bd95757b7aafc4348c42a1380628b70ba3f13fa8 Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Sat, 7 Jun 2025 03:02:15 +0000 Subject: [PATCH 07/13] Ignore face settings extras when called by an external package. Bug: 411418366 Flag: EXEMPT bug fix Test: manual from any user not 0 (adb shell am start -a android.settings.FACE_SETTINGS --ei android.intent.extra.USER_ID 0) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:486947205e05e83314bd76e4822af442ca82be9c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:387df9edf18fbfd42af166033c1532695f858c01) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:945e3401a4066f8a099c7499136b7ea2ba5f4009) Merged-In: I06193e421a140a90568251fc25baa7fc81c12d78 Change-Id: I06193e421a140a90568251fc25baa7fc81c12d78 --- .../biometrics/face/FaceSettings.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java index dc94376b91d..f8cc2172b08 100644 --- a/src/com/android/settings/biometrics/face/FaceSettings.java +++ b/src/com/android/settings/biometrics/face/FaceSettings.java @@ -70,8 +70,8 @@ public class FaceSettings extends DashboardFragment { private FaceManager mFaceManager; private DevicePolicyManager mDevicePolicyManager; private int mUserId; - private int mSensorId; - private long mChallenge; + private int mSensorId = -1; + private long mChallenge = 0; private byte[] mToken; private FaceSettingsAttentionPreferenceController mAttentionController; private FaceSettingsRemoveButtonPreferenceController mRemoveController; @@ -152,12 +152,19 @@ public class FaceSettings extends DashboardFragment { mUserManager = context.getSystemService(UserManager.class); mFaceManager = context.getSystemService(FaceManager.class); mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); - mToken = getIntent().getByteArrayExtra(KEY_TOKEN); - mSensorId = getIntent().getIntExtra(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, -1); - mChallenge = getIntent().getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, 0L); - mUserId = getActivity().getIntent().getIntExtra( - Intent.EXTRA_USER_ID, UserHandle.myUserId()); + final SettingsActivity activity = (SettingsActivity) requireActivity(); + final String callingPackage = activity.getInitialCallingPackage(); + if (callingPackage == null || !callingPackage.equals(activity.getPackageName())) { + mUserId = UserHandle.myUserId(); + } else { + // only allow these extras when called internally by Settings + mToken = getIntent().getByteArrayExtra(KEY_TOKEN); + mSensorId = getIntent().getIntExtra(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, -1); + mChallenge = getIntent().getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, 0L); + mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); + } + mFaceFeatureProvider = FeatureFactory.getFactory(getContext()).getFaceFeatureProvider(); if (mUserManager.getUserInfo(mUserId).isManagedProfile()) { -- GitLab From c79c6e4e89f0884166b06d7255d50729a14049cf Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Mon, 2 Jun 2025 09:40:27 +0000 Subject: [PATCH 08/13] Prevent SettingsSliceProvider from accessing unused packages DISABLE_TOPIC_PROTECTOR Bug: 388034510 Test: adb shell cmd slice get-permissions com.android.settings.slices atest SettingsSliceProviderTest Flag: EXEMPT security issue Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:5c904325b7db848b2897e50c278b76cb00cdcced Merged-In: Ia655fbb9cb46f192559b82f957e3b2f0dd86946c Change-Id: Ia655fbb9cb46f192559b82f957e3b2f0dd86946c --- res/values/config.xml | 3 +++ .../slices/SettingsSliceProvider.java | 20 +++++++++++++++---- tests/robotests/res/values-mcc998/config.xml | 3 +++ tests/robotests/res/values-mcc999/config.xml | 5 +++++ .../slices/SettingsSliceProviderTest.java | 20 +++++++++++++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index 2de2951a732..847e4e28573 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -175,6 +175,9 @@ + + + true diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 7fd9f3d8d34..8d15440d678 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -27,6 +27,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.StrictMode; import android.provider.Settings; import android.provider.SettingsSlicesContract; @@ -361,19 +362,30 @@ public class SettingsSliceProvider extends SliceProvider { if (descendants == null) { Log.d(TAG, "No descendants to grant permission with, skipping."); } - final String[] allowlistPackages = + final List allowlist = new ArrayList<>(); + final String[] packages = context.getResources().getStringArray(R.array.slice_allowlist_package_names); - if (allowlistPackages == null || allowlistPackages.length == 0) { + if (packages != null) { + allowlist.addAll(Arrays.asList(packages)); + } + if (Build.IS_DEBUGGABLE) { + final String[] devPackages = context.getResources().getStringArray( + R.array.slice_allowlist_package_names_for_dev); + if (devPackages != null) { + allowlist.addAll(Arrays.asList(devPackages)); + } + } + if (allowlist.size() == 0) { Log.d(TAG, "No packages to allowlist, skipping."); return; } else { Log.d(TAG, String.format( "Allowlisting %d uris to %d pkgs.", - descendants.size(), allowlistPackages.length)); + descendants.size(), allowlist.size())); } final SliceManager sliceManager = context.getSystemService(SliceManager.class); for (Uri descendant : descendants) { - for (String toPackage : allowlistPackages) { + for (String toPackage : allowlist) { sliceManager.grantSlicePermission(toPackage, descendant); } } diff --git a/tests/robotests/res/values-mcc998/config.xml b/tests/robotests/res/values-mcc998/config.xml index 6572b1485b5..18174983a1f 100644 --- a/tests/robotests/res/values-mcc998/config.xml +++ b/tests/robotests/res/values-mcc998/config.xml @@ -17,4 +17,7 @@ + + + diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml index b1d4be9ea1c..fe7412e6440 100644 --- a/tests/robotests/res/values-mcc999/config.xml +++ b/tests/robotests/res/values-mcc999/config.xml @@ -86,6 +86,11 @@ com.android.settings.slice_allowlist_package + + + com.android.settings.slice_allowlist_package_dev + + test@test.test diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index df38e7f72a0..b6f964e4c8f 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -43,6 +43,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources.Theme; import android.net.Uri; +import android.os.Build; import android.os.StrictMode; import android.provider.Settings; import android.provider.SettingsSlicesContract; @@ -83,6 +84,7 @@ import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowAccessibilityManager; import org.robolectric.shadows.ShadowBinder; import org.robolectric.shadows.ShadowPackageManager; +import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.Arrays; @@ -662,6 +664,7 @@ public class SettingsSliceProviderTest { @Test @Config(qualifiers = "mcc999") public void grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant() { + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false); final List uris = new ArrayList<>(); uris.add(Uri.parse("content://settings/slice")); @@ -669,6 +672,23 @@ public class SettingsSliceProviderTest { verify(mManager) .grantSlicePermission("com.android.settings.slice_allowlist_package", uris.get(0)); + verify(mManager, never()) + .grantSlicePermission("com.android.settings.slice_allowlist_package_dev", + uris.get(0)); + } + + @Test + @Config(qualifiers = "mcc999") + public void grantAllowlistedPackagePermissions_hasPackageAllowlistAndDebuggable_shouldGrant() { + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + final List uris = new ArrayList<>(); + uris.add(Uri.parse("content://settings/slice")); + + SettingsSliceProvider.grantAllowlistedPackagePermissions(mContext, uris); + + verify(mManager) + .grantSlicePermission("com.android.settings.slice_allowlist_package_dev", + uris.get(0)); } @Test -- GitLab From 8911d4a84996eb666a008b8c5d2344e87448fc48 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 3 Jun 2025 10:46:31 -0400 Subject: [PATCH 09/13] Hide sensistive content from locked profiles Test: manual Flag: EXEMPT bug fix Bug: 396666065 (cherry picked from commit b1bfda5a03c6d21a16da0de2eb77340536400ca6) Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:762dd9f5b78fcdfddc16abc718d98a1bfffbf9ac Merged-In: Ie7705a1ceea54203585c3b3737d0bada68aa1469 Change-Id: Ie7705a1ceea54203585c3b3737d0bada68aa1469 --- .../history/NotificationStation.java | 70 ++++++++++++++----- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/src/com/android/settings/notification/history/NotificationStation.java b/src/com/android/settings/notification/history/NotificationStation.java index 3ed8cdfd9b4..77bb88f2b27 100644 --- a/src/com/android/settings/notification/history/NotificationStation.java +++ b/src/com/android/settings/notification/history/NotificationStation.java @@ -18,10 +18,12 @@ package com.android.settings.notification.history; import static android.provider.Settings.EXTRA_APP_PACKAGE; import static android.provider.Settings.EXTRA_CHANNEL_ID; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import android.app.Activity; import android.app.ActivityManager; import android.app.INotificationManager; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; @@ -32,6 +34,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.graphics.PorterDuff; import android.graphics.Typeface; import android.graphics.drawable.Drawable; @@ -40,6 +43,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; @@ -60,6 +64,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import androidx.recyclerview.widget.RecyclerView; +import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; @@ -117,6 +122,7 @@ public class NotificationStation extends SettingsPreferenceFragment { private INotificationManager mNoMan; private RankingMap mRanking; private LinkedList mNotificationInfos; + private ArrayList mContentRestrictedUsers = new ArrayList<>(); private final NotificationListenerService mListener = new NotificationListenerService() { @Override @@ -208,6 +214,21 @@ public class NotificationStation extends SettingsPreferenceFragment { public void onResume() { logd("onResume()"); super.onResume(); + + mContentRestrictedUsers.clear(); + List users = + getSystemService(UserManager.class).getProfiles(mContext.getUserId()); + for (UserInfo user : users) { + if (Settings.Secure.getIntForUser(getContentResolver(), + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, user.id) == 0) { + LockPatternUtils lpu = new LockPatternUtils(mContext); + KeyguardManager km = getSystemService(KeyguardManager.class); + if (lpu.isSecure(user.id) && km.isDeviceLocked(user.id)) { + mContentRestrictedUsers.add(user.id); + } + } + } + try { mListener.registerAsSystemService(mContext, new ComponentName(mContext.getPackageName(), this.getClass().getCanonicalName()), ActivityManager.getCurrentUser()); @@ -229,7 +250,8 @@ public class NotificationStation extends SettingsPreferenceFragment { getPreferenceScreen().removeAll(); for (int i = 0; i < N; i++) { getPreferenceScreen().addPreference(new HistoricalNotificationPreference( - getPrefContext(), mNotificationInfos.get(i), i)); + getPrefContext(), mNotificationInfos.get(i), i, + mContentRestrictedUsers.contains(mNotificationInfos.get(i).user))); } } @@ -243,7 +265,8 @@ public class NotificationStation extends SettingsPreferenceFragment { if (TextUtils.equals(info.key, sbn.getKey())) { info.active = false; ((HistoricalNotificationPreference) getPreferenceScreen().findPreference( - sbn.getKey())).updatePreference(info); + sbn.getKey())).updatePreference( + info, mContentRestrictedUsers.contains(info.user)); break; } } @@ -264,7 +287,8 @@ public class NotificationStation extends SettingsPreferenceFragment { info.updateFrom(newInfo); ((HistoricalNotificationPreference) getPreferenceScreen().findPreference( - sbn.getKey())).updatePreference(info); + sbn.getKey())).updatePreference( + info, mContentRestrictedUsers.contains(info.user)); needsAdd = false; break; } @@ -273,7 +297,8 @@ public class NotificationStation extends SettingsPreferenceFragment { mNotificationInfos.addFirst(newInfo); getPreferenceScreen().addPreference(new HistoricalNotificationPreference( getPrefContext(), mNotificationInfos.peekFirst(), - -1 * mNotificationInfos.size())); + -1 * mNotificationInfos.size(), + mContentRestrictedUsers.contains(newInfo.user))); } } @@ -290,7 +315,7 @@ public class NotificationStation extends SettingsPreferenceFragment { updateFromRanking(info); ((HistoricalNotificationPreference) getPreferenceScreen().findPreference( - info.key)).updatePreference(info); + info.key)).updatePreference(info, mContentRestrictedUsers.contains(info.user)); } } @@ -405,6 +430,7 @@ public class NotificationStation extends SettingsPreferenceFragment { private HistoricalNotificationInfo createFromSbn(StatusBarNotification sbn, boolean active) { final Notification n = sbn.getNotification(); final HistoricalNotificationInfo info = new HistoricalNotificationInfo(); + info.pkg = sbn.getPackageName(); info.user = sbn.getUserId() == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : sbn.getUserId(); @@ -416,6 +442,7 @@ public class NotificationStation extends SettingsPreferenceFragment { info.pkgname = loadPackageName(info.pkg); info.title = getTitleString(n); info.text = getTextString(sbn.getPackageContext(mContext), n); + info.timestamp = sbn.getPostTime(); info.priority = n.priority; info.key = sbn.getKey(); @@ -673,15 +700,17 @@ public class NotificationStation extends SettingsPreferenceFragment { private static long sLastExpandedTimestamp; // quick hack to keep things from collapsing public ViewGroup mItemView; // hack to update prefs fast; private Context mContext; + private boolean mRestrictContent; public HistoricalNotificationPreference(Context context, HistoricalNotificationInfo info, - int order) { + int order, boolean restrictContent) { super(context); setLayoutResource(R.layout.notification_log_row); setOrder(order); setKey(info.key); mInfo = info; mContext = context; + mRestrictContent = restrictContent; } @Override @@ -690,7 +719,7 @@ public class NotificationStation extends SettingsPreferenceFragment { mItemView = (ViewGroup) row.itemView; - updatePreference(mInfo); + updatePreference(mInfo, mRestrictContent); row.findViewById(R.id.timestamp).setOnLongClickListener(v -> { final View extras = row.findViewById(R.id.extra); @@ -701,7 +730,7 @@ public class NotificationStation extends SettingsPreferenceFragment { }); } - public void updatePreference(HistoricalNotificationInfo info) { + public void updatePreference(HistoricalNotificationInfo info, boolean restrictContent) { if (mItemView == null) { return; } @@ -710,17 +739,17 @@ public class NotificationStation extends SettingsPreferenceFragment { } ((TextView) mItemView.findViewById(R.id.pkgname)).setText(mInfo.pkgname); ((DateTimeView) mItemView.findViewById(R.id.timestamp)).setTime(info.timestamp); - if (!TextUtils.isEmpty(info.title)) { + if (restrictContent || TextUtils.isEmpty(info.title)) { + mItemView.findViewById(R.id.title).setVisibility(View.GONE); + } else { ((TextView) mItemView.findViewById(R.id.title)).setText(info.title); mItemView.findViewById(R.id.title).setVisibility(View.VISIBLE); - } else { - mItemView.findViewById(R.id.title).setVisibility(View.GONE); } - if (!TextUtils.isEmpty(info.text)) { + if (restrictContent || TextUtils.isEmpty(info.text)) { + mItemView.findViewById(R.id.text).setVisibility(View.GONE); + } else { ((TextView) mItemView.findViewById(R.id.text)).setText(info.text); mItemView.findViewById(R.id.text).setVisibility(View.VISIBLE); - } else { - mItemView.findViewById(R.id.text).setVisibility(View.GONE); } if (info.icon != null) { ((ImageView) mItemView.findViewById(R.id.icon)).setImageDrawable(info.icon); @@ -734,10 +763,15 @@ public class NotificationStation extends SettingsPreferenceFragment { ((DateTimeView) mItemView.findViewById(R.id.timestamp)).setTime(mInfo.timestamp); - ((TextView) mItemView.findViewById(R.id.notification_extra)) - .setText(mInfo.notificationExtra); - ((TextView) mItemView.findViewById(R.id.ranking_extra)) - .setText(mInfo.rankingExtra); + if (restrictContent) { + mItemView.findViewById(R.id.notification_extra).setVisibility(View.GONE); + mItemView.findViewById(R.id.ranking_extra).setVisibility(View.GONE); + } else { + ((TextView) mItemView.findViewById(R.id.notification_extra)) + .setText(mInfo.notificationExtra); + ((TextView) mItemView.findViewById(R.id.ranking_extra)) + .setText(mInfo.rankingExtra); + } mItemView.findViewById(R.id.extra).setVisibility( mInfo.timestamp == sLastExpandedTimestamp ? View.VISIBLE : View.GONE); -- GitLab From c0273bfed6f0f8341db16d79e22b2dc78e5c7f81 Mon Sep 17 00:00:00 2001 From: Michael Cheng Date: Tue, 5 Aug 2025 23:18:50 -0700 Subject: [PATCH 10/13] Restrict WifiScanModeActivity when user restriction applies Bug: 299633613 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:7a792e0b8f68bc4aeb939af703790fd76b51ccbd) Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:a6e56fc6db9800228b713b067ced3bff978ad754 Merged-In: If8cfb7047c0131da451a7af0d2b5108080876b85 Change-Id: If8cfb7047c0131da451a7af0d2b5108080876b85 --- .../settings/wifi/WifiScanModeActivity.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/settings/wifi/WifiScanModeActivity.java b/src/com/android/settings/wifi/WifiScanModeActivity.java index d37213522aa..d082a55f60e 100644 --- a/src/com/android/settings/wifi/WifiScanModeActivity.java +++ b/src/com/android/settings/wifi/WifiScanModeActivity.java @@ -18,11 +18,14 @@ package com.android.settings.wifi; import android.app.Dialog; import android.app.settings.SettingsEnums; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.wifi.WifiManager; import android.os.Bundle; +import android.os.UserManager; import android.text.TextUtils; +import android.util.Log; import android.view.WindowManager; import androidx.annotation.VisibleForTesting; @@ -39,6 +42,7 @@ import com.android.settingslib.wifi.WifiPermissionChecker; * This activity requests users permission to allow scanning even when Wi-Fi is turned off */ public class WifiScanModeActivity extends FragmentActivity { + private static final String TAG = "WifiScanModeActivity"; private DialogFragment mDialog; @VisibleForTesting String mApp; @@ -79,6 +83,12 @@ public class WifiScanModeActivity extends FragmentActivity { } private void createDialog() { + if (!isWifiScanModeConfigAllowed(getApplicationContext())) { + Log.e(TAG, "This user is not allowed to configure Wi-Fi Scan Mode!"); + finish(); + return; + } + if (mDialog == null) { mDialog = AlertDialogFragment.newInstance(mApp); mDialog.show(getSupportFragmentManager(), "dialog"); @@ -169,4 +179,10 @@ public class WifiScanModeActivity extends FragmentActivity { ((WifiScanModeActivity) getActivity()).doNegativeClick(); } } + + private static boolean isWifiScanModeConfigAllowed(Context context) { + final UserManager userManager = context.getSystemService(UserManager.class); + if (userManager == null) return true; + return !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_LOCATION); + } } -- GitLab From 27853c4ba169efcec1c050a842a209c508f145c0 Mon Sep 17 00:00:00 2001 From: James Eidson Date: Thu, 14 Aug 2025 19:54:00 +0000 Subject: [PATCH 11/13] [nfc] Fix string injection in default payment app selector Backwards compatible port of ag/35084316 Bug: 429417453 Test: Manually by installing settings app Flag: EXEMPT security fix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6c723a4361950e8e43cc5caf67455bd2f00911d1) Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:289ec2569280400bc7148de60fa24ba6e07217c1 Merged-In: I670774a5efa6f543a5e1e06798a5d6ebb1c48c1d Change-Id: I670774a5efa6f543a5e1e06798a5d6ebb1c48c1d --- .../settings/nfc/DefaultPaymentSettings.java | 29 ++++++++++--------- .../android/settings/nfc/PaymentBackend.java | 19 ++++++++++++ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/nfc/DefaultPaymentSettings.java b/src/com/android/settings/nfc/DefaultPaymentSettings.java index 5224d92fb4e..9243e586e0c 100644 --- a/src/com/android/settings/nfc/DefaultPaymentSettings.java +++ b/src/com/android/settings/nfc/DefaultPaymentSettings.java @@ -17,7 +17,6 @@ package com.android.settings.nfc; import android.app.settings.SettingsEnums; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -45,6 +44,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; /** * DefaultPaymentSettings handles the NFC default payment app selection. @@ -53,7 +55,7 @@ public class DefaultPaymentSettings extends DefaultAppPickerFragment { public static final String TAG = "DefaultPaymentSettings"; private PaymentBackend mPaymentBackend; - private List mAppInfos; + private Map mAppInfos; private FooterPreference mFooterPreference; @Override @@ -67,22 +69,19 @@ public class DefaultPaymentSettings extends DefaultAppPickerFragment { } @Override + @SuppressWarnings("NullAway") protected String getDefaultKey() { PaymentAppInfo defaultAppInfo = mPaymentBackend.getDefaultApp(); - if (defaultAppInfo != null) { - return defaultAppInfo.componentName.flattenToString() + " " - + defaultAppInfo.userHandle.getIdentifier(); - } - return null; + if (defaultAppInfo == null) return null; + return defaultAppInfo.getKey(); } @Override protected boolean setDefaultKey(String key) { - String[] keys = key.split(" "); - if (keys.length >= 2) { - mPaymentBackend.setDefaultPaymentApp(ComponentName.unflattenFromString(keys[0]), - Integer.parseInt(keys[1])); - } + PaymentAppInfo appInfo = mAppInfos.get(key); + if (appInfo == null) return true; + mPaymentBackend.setDefaultPaymentApp( + appInfo.componentName, appInfo.userHandle.getIdentifier()); return true; } @@ -90,7 +89,9 @@ public class DefaultPaymentSettings extends DefaultAppPickerFragment { public void onAttach(Context context) { super.onAttach(context); mPaymentBackend = new PaymentBackend(getActivity()); - mAppInfos = mPaymentBackend.getPaymentAppInfos(); + mAppInfos = mPaymentBackend.getPaymentAppInfos() + .stream() + .collect(Collectors.toMap(PaymentAppInfo::getKey, Function.identity())); } @Override @@ -147,7 +148,7 @@ public class DefaultPaymentSettings extends DefaultAppPickerFragment { @Override protected List getCandidates() { final List candidates = new ArrayList<>(); - for (PaymentAppInfo appInfo: mAppInfos) { + for (PaymentAppInfo appInfo: mAppInfos.values()) { UserManager um = getContext().createContextAsUser( appInfo.userHandle, /*flags=*/0).getSystemService(UserManager.class); boolean isManagedProfile = um.isManagedProfile(appInfo.userHandle.getIdentifier()); diff --git a/src/com/android/settings/nfc/PaymentBackend.java b/src/com/android/settings/nfc/PaymentBackend.java index 021d673e152..de733081a1d 100644 --- a/src/com/android/settings/nfc/PaymentBackend.java +++ b/src/com/android/settings/nfc/PaymentBackend.java @@ -36,6 +36,7 @@ import com.android.internal.content.PackageMonitor; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class PaymentBackend { public static final String TAG = "Settings.PaymentBackend"; @@ -52,6 +53,24 @@ public class PaymentBackend { public ComponentName settingsComponent; public UserHandle userHandle; public Drawable icon; + + public String getKey() { + return Integer.toString(hashCode()); + } + + @Override + public int hashCode() { + return Objects.hash(componentName, userHandle); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof PaymentAppInfo)) return false; + PaymentAppInfo appInfo = (PaymentAppInfo) o; + return componentName.equals(appInfo.componentName) + && userHandle.equals(appInfo.userHandle); + } } /** -- GitLab From a3a1cbdb61ed27c4e04863c68dce7f89cdaa44bb Mon Sep 17 00:00:00 2001 From: Haijie Hong Date: Mon, 12 May 2025 13:44:55 +0800 Subject: [PATCH 12/13] Backport BT pairing dialog changes This is a combination of two commits against b/409868905, merged for backport convenience. It updates the string and button style for the BT pairing dialog. Bug: 236134583 Bug: 236134775 Test: local tested Flag: EXEMPT minor style update Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:cdd1489f54c901532984bac346999a6dee1dfcd5 Merged-In: Icd9f509b5363b3ec3900738e21486e54d4e66e4b Change-Id: Icd9f509b5363b3ec3900738e21486e54d4e66e4b --- res/layout/bluetooth_pin_confirm.xml | 41 +++++++++++- res/layout/bluetooth_pin_entry.xml | 27 +++++++- res/values/strings.xml | 6 ++ .../BluetoothPairingDialogFragment.java | 67 ++++++++++++------- .../bluetooth/BluetoothPairingDialogTest.java | 36 +++++++--- 5 files changed, 142 insertions(+), 35 deletions(-) diff --git a/res/layout/bluetooth_pin_confirm.xml b/res/layout/bluetooth_pin_confirm.xml index 0024ad42413..663b900f48a 100644 --- a/res/layout/bluetooth_pin_confirm.xml +++ b/res/layout/bluetooth_pin_confirm.xml @@ -19,6 +19,7 @@ @@ -30,6 +31,18 @@ android:layout_marginTop="@dimen/bluetooth_dialog_padding_top" android:orientation="vertical"> + + + +