diff options
Diffstat (limited to 'service/java/com/android/safetycenter/SafetyCenterDataTracker.java')
-rw-r--r-- | service/java/com/android/safetycenter/SafetyCenterDataTracker.java | 122 |
1 files changed, 105 insertions, 17 deletions
diff --git a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java index fe0b6768c..cd2fdf94d 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java @@ -24,9 +24,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; import android.icu.text.ListFormatter; import android.icu.text.MessageFormat; import android.icu.util.ULocale; @@ -72,7 +74,6 @@ import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Objects; -import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; @@ -88,7 +89,9 @@ final class SafetyCenterDataTracker { private static final String TAG = "SafetyCenterDataTracker"; - private static final String ANDROID_LOCK_SCREEN_SOURCES_ID = "AndroidLockScreenSources"; + private static final String ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID = "AndroidLockScreenSources"; + private static final String ANDROID_LOCK_SCREEN_SOURCE_ID = "AndroidLockScreen"; + private static final int ANDROID_LOCK_SCREEN_ICON_ACTION_REQ_CODE = 86; private static final SafetyCenterIssuesBySeverityDescending SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING = @@ -301,11 +304,17 @@ final class SafetyCenterDataTracker { } /** - * Clears all safety source errors received so far, this is useful e.g. when starting a new - * broadcast. + * Clears all safety source errors received so far for the given {@link UserProfileGroup}, this + * is useful e.g. when starting a new broadcast. */ - void clearSafetySourceErrors() { - mSafetySourceErrors.clear(); + void clearSafetySourceErrors(@NonNull UserProfileGroup userProfileGroup) { + // Loop in reverse index order to be able to remove entries while iterating. + for (int i = mSafetySourceErrors.size() - 1; i >= 0; i--) { + SafetySourceKey sourceKey = mSafetySourceErrors.valueAt(i); + if (userProfileGroup.contains(sourceKey.getUserId())) { + mSafetySourceErrors.removeAt(i); + } + } } /** @@ -478,6 +487,13 @@ final class SafetyCenterDataTracker { } } // Loop in reverse index order to be able to remove entries while iterating. + for (int i = mSafetySourceErrors.size() - 1; i >= 0; i--) { + SafetySourceKey sourceKey = mSafetySourceErrors.valueAt(i); + if (sourceKey.getUserId() == userId) { + mSafetySourceErrors.removeAt(i); + } + } + // Loop in reverse index order to be able to remove entries while iterating. for (int i = mSafetyCenterIssueCache.size() - 1; i >= 0; i--) { SafetyCenterIssueKey issueKey = mSafetyCenterIssueCache.keyAt(i); if (issueKey.getUserId() == userId) { @@ -705,7 +721,7 @@ final class SafetyCenterDataTracker { int safetyCenterOverallSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK; int safetyCenterEntriesSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK; List<SafetyCenterIssue> safetyCenterIssues = new ArrayList<>(); - Set<Integer> allCurrentIssueCategories = new ArraySet<>(); + ArraySet<Integer> allCurrentIssueCategories = new ArraySet<>(); List<SafetyCenterEntryOrGroup> safetyCenterEntryOrGroups = new ArrayList<>(); List<SafetyCenterStaticEntryGroup> safetyCenterStaticEntryGroups = new ArrayList<>(); SafetyCenterOverallStatusErrorState safetyCenterOverallStatusErrorState = @@ -779,7 +795,7 @@ final class SafetyCenterDataTracker { @SafetyCenterStatus.OverallSeverityLevel private int addSafetyCenterIssues( @NonNull List<SafetyCenterIssue> safetyCenterIssues, - @NonNull Set<Integer> allCurrentIssueCategories, + @NonNull ArraySet<Integer> allCurrentIssueCategories, @NonNull SafetySourcesGroup safetySourcesGroup, @NonNull UserProfileGroup userProfileGroup) { int safetyCenterIssuesOverallSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK; @@ -826,7 +842,7 @@ final class SafetyCenterDataTracker { @SafetyCenterStatus.OverallSeverityLevel private int addSafetyCenterIssues( @NonNull List<SafetyCenterIssue> safetyCenterIssues, - @NonNull Set<Integer> allCurrentIssueCategories, + @NonNull ArraySet<Integer> allCurrentIssueCategories, @NonNull SafetySource safetySource, @UserIdInt int userId) { SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId); @@ -995,7 +1011,7 @@ final class SafetyCenterDataTracker { break; } } - } else if (safetySourcesGroup.getId().equals(ANDROID_LOCK_SCREEN_SOURCES_ID) + } else if (safetySourcesGroup.getId().equals(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID) && TextUtils.isEmpty(groupSummary)) { List<CharSequence> titles = new ArrayList<>(); for (int i = 0; i < entries.size(); i++) { @@ -1098,12 +1114,16 @@ final class SafetyCenterDataTracker { .setSeverityUnspecifiedIconType(severityUnspecifiedIconType) .setPendingIntent(pendingIntent); SafetySourceStatus.IconAction iconAction = safetySourceStatus.getIconAction(); - if (iconAction != null) { - builder.setIconAction( - new SafetyCenterEntry.IconAction( - toSafetyCenterEntryIconActionType(iconAction.getIconType()), - iconAction.getPendingIntent())); + if (iconAction == null) { + return builder.build(); } + PendingIntent iconActionPendingIntent = + toIconActionPendingIntent( + safetySource.getId(), iconAction.getPendingIntent()); + builder.setIconAction( + new SafetyCenterEntry.IconAction( + toSafetyCenterEntryIconActionType(iconAction.getIconType()), + iconActionPendingIntent)); return builder.build(); } return toDefaultSafetyCenterEntry( @@ -1343,16 +1363,84 @@ final class SafetyCenterDataTracker { // TODO(b/222838784): Validate that the intent action is available. + return toPendingIntent(context, 0, new Intent(intentAction)); + } + + @NonNull + private static PendingIntent toPendingIntent( + @NonNull Context packageContext, int requestCode, @NonNull Intent intent) { // This call is required for getIntentSender() to be allowed to send as another package. final long identity = Binder.clearCallingIdentity(); try { return PendingIntent.getActivity( - context, 0, new Intent(intentAction), PendingIntent.FLAG_IMMUTABLE); + packageContext, requestCode, intent, PendingIntent.FLAG_IMMUTABLE); } finally { Binder.restoreCallingIdentity(identity); } } + /** + * Potentially overrides the Settings IconAction PendingIntent for the AndroidLockScreen source. + * + * <p>This is done because of a bug in the Settings app where the PendingIntent created ends up + * referencing the one from the main entry. The reason for this is that PendingIntent instances + * are cached and keyed by an object which does not take into account the underlying intent + * extras; and these two intents only differ by the extras that they set. We fix this issue by + * recreating the desired Intent and PendingIntent here, using a specific request code for the + * PendingIntent to ensure a new instance is created (the key does take into account the request + * code). + */ + @NonNull + private PendingIntent toIconActionPendingIntent( + @NonNull String sourceId, @NonNull PendingIntent pendingIntent) { + if (!ANDROID_LOCK_SCREEN_SOURCE_ID.equals(sourceId)) { + return pendingIntent; + } + if (!SafetyCenterFlags.getReplaceLockScreenIconAction()) { + return pendingIntent; + } + String settingsPackageName = pendingIntent.getCreatorPackage(); + int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); + Context packageContext = toPackageContextAsUser(settingsPackageName, userId); + if (packageContext == null) { + return pendingIntent; + } + Resources settingsResources = packageContext.getResources(); + int hasSettingsFixedIssueResourceId = + settingsResources.getIdentifier( + "config_isSafetyCenterLockScreenPendingIntentFixed", + "bool", + settingsPackageName); + if (hasSettingsFixedIssueResourceId != Resources.ID_NULL) { + boolean hasSettingsFixedIssue = + settingsResources.getBoolean(hasSettingsFixedIssueResourceId); + if (hasSettingsFixedIssue) { + return pendingIntent; + } + } + Intent intent = + new Intent(Intent.ACTION_MAIN) + .setComponent( + new ComponentName( + settingsPackageName, settingsPackageName + ".SubSettings")) + .putExtra( + ":settings:show_fragment", + settingsPackageName + ".security.screenlock.ScreenLockSettings") + .putExtra(":settings:source_metrics", 1917) + .putExtra("page_transition_type", 0); + PendingIntent offendingPendingIntent = toPendingIntent(packageContext, 0, intent); + if (!offendingPendingIntent.equals(pendingIntent)) { + return pendingIntent; + } + // If creating that PendingIntent with request code 0 returns the same value as the + // PendingIntent that was sent to Safety Center, then we’re most likely hitting the caching + // issue described in this method’s documentation. + // i.e. the intent action and component of the cached PendingIntent are the same, but the + // extras are actually different so we should ensure we create a brand new PendingIntent by + // changing the request code. + return toPendingIntent(packageContext, ANDROID_LOCK_SCREEN_ICON_ACTION_REQ_CODE, intent); + } + @Nullable private Context toPackageContextAsUser(@NonNull String packageName, @UserIdInt int userId) { // This call requires the INTERACT_ACROSS_USERS permission. @@ -1496,7 +1584,7 @@ final class SafetyCenterDataTracker { private String getSafetyCenterStatusTitle( @SafetyCenterStatus.OverallSeverityLevel int overallSeverityLevel, @SafetyCenterStatus.RefreshStatus int refreshStatus, - @NonNull Set<Integer> allCurrentIssueCategories, + @NonNull ArraySet<Integer> allCurrentIssueCategories, boolean hasSettingsToReview) { String refreshStatusTitle = getSafetyCenterRefreshStatusTitle(refreshStatus); if (refreshStatusTitle != null) { |