diff options
9 files changed, 665 insertions, 748 deletions
diff --git a/PermissionController/res/layout-v33/preference_static_entry.xml b/PermissionController/res/layout-v33/preference_static_entry.xml deleted file mode 100644 index a76e880e6..000000000 --- a/PermissionController/res/layout-v33/preference_static_entry.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/SafetyCenterStaticEntry"> - - <TextView android:id="@android:id/title" - style="@style/SafetyCenterStaticEntryTitle" /> - - <TextView android:id="@android:id/summary" - style="@style/SafetyCenterStaticEntrySummary" /> - -</LinearLayout>
\ No newline at end of file diff --git a/PermissionController/res/values-v33/dimens.xml b/PermissionController/res/values-v33/dimens.xml index 61328d7db..e94eac6d8 100644 --- a/PermissionController/res/values-v33/dimens.xml +++ b/PermissionController/res/values-v33/dimens.xml @@ -28,6 +28,7 @@ <dimen name="sc_card_margin">@dimen/sc_spacing_xxxsmall</dimen> <dimen name="sc_list_margin">@dimen/sc_spacing_large</dimen> + <dimen name="sc_list_margin_top">@dimen/sc_spacing_small</dimen> <dimen name="sc_action_button_list_margin">@dimen/sc_spacing_large</dimen> <dimen name="sc_top_action_button_margin">@dimen/sc_spacing_xxxlarge</dimen> <dimen name="sc_entry_padding_end">@dimen/sc_spacing_xxxlarge</dimen> @@ -38,7 +39,6 @@ <dimen name="sc_card_margin_bottom">@dimen/sc_spacing_xxxlarge</dimen> <dimen name="sc_icon_button_touch_target_size">48dp</dimen> - <dimen name="sc_indicator_expand_button_background">24dp</dimen> <dimen name="sc_button_corner_radius">12dp</dimen> <dimen name="sc_card_corner_radius_large">28dp</dimen> <dimen name="sc_card_corner_radius_medium">20dp</dimen> diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml index e395c9777..afb9f0db5 100644 --- a/PermissionController/res/values-v33/styles.xml +++ b/PermissionController/res/values-v33/styles.xml @@ -764,35 +764,4 @@ <item name="android:gravity">center_vertical</item> <item name="android:orientation">horizontal</item> </style> - - <style name="SafetyCenterStaticEntry" - parent="android:Widget.DeviceDefault"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:paddingStart">@dimen/sc_spacing_xxxlarge</item> - <item name="android:paddingEnd">@dimen/sc_spacing_xxxlarge</item> - <item name="android:paddingTop">@dimen/sc_spacing_large</item> - <item name="android:paddingBottom">@dimen/sc_spacing_large</item> - <item name="android:minHeight">?android:attr/listPreferredItemHeight</item> - <item name="android:gravity">center_vertical</item> - <item name="android:orientation">vertical</item> - </style> - - <style name="SafetyCenterStaticEntryTitle" - parent="android:Widget.DeviceDefault"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Headline.Entry</item> - <item name="android:ellipsize">end</item> - <item name="android:maxLines">4</item> - </style> - - <style name="SafetyCenterStaticEntrySummary" - parent="android:Widget.DeviceDefault"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Body</item> - <item name="android:ellipsize">end</item> - <item name="android:maxLines">4</item> - </style> </resources> diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml index c448d88cf..6e7973fb1 100644 --- a/PermissionController/res/values/overlayable.xml +++ b/PermissionController/res/values/overlayable.xml @@ -321,6 +321,7 @@ <item type="dimen" name="sc_spacing_xxxlarge" /> <item type="dimen" name="sc_card_margin" /> <item type="dimen" name="sc_list_margin" /> + <item type="dimen" name="sc_list_margin_top" /> <item type="dimen" name="sc_action_button_list_margin" /> <item type="dimen" name="sc_top_action_button_margin" /> <item type="dimen" name="sc_entry_padding_end" /> @@ -329,8 +330,10 @@ <item type="dimen" name="sc_entry_group_collapsed_padding_top" /> <item type="dimen" name="sc_entry_group_collapsed_padding_bottom" /> <item type="dimen" name="sc_card_margin_bottom" /> - <item type="dimen" name="sc_icon_button_touch_target_size" /> - <item type="dimen" name="sc_indicator_expand_button_background" /> + <item type="dimen" name="sc_button_corner_radius" /> + <item type="dimen" name="sc_card_corner_radius_large" /> + <item type="dimen" name="sc_card_corner_radius_medium" /> + <item type="dimen" name="sc_card_corner_radius_xsmall" /> <item type="color" name="safety_center_button_info" /> <item type="color" name="safety_center_button_recommend" /> @@ -423,86 +426,6 @@ <item type="style" name="TextAppearance.SafetyCenter.Medium" /> <item type="style" name="TextAppearance.SafetyCenter.ActionButton" /> <item type="style" name="TextAppearance.SafetyCenter.ActionButton.Secondary" /> - - <item type="style" name="SafetyCenterCard" /> - <item type="style" name="SafetyCenterActionButton" /> - <item type="style" name="SafetyCenterActionButton.Secondary" /> - <item type="style" name="SafetyCenterCard.Status" /> - <item type="style" name="SafetyCenterStatusImage" /> - <item type="style" name="SafetyCenterStatusTitleAndSummaryContainer" /> - <item type="style" name="SafetyCenterStatusTitle" /> - <item type="style" name="SafetyCenterStatusSummary" /> - <item type="style" name="SafetyCenterStatusButton" /> - <item type="style" name="SafetyCenterStatusButton.ReviewSettings" /> - <item type="style" name="SafetyCenterStatusButton.Rescan" /> - <item type="style" name="SafetyCenterStatusButton.PendingActionsRescan" /> - <item type="style" name="SafetyCenterStatusSafetyProtectionView" /> - <item type="style" name="SafetyCenterCard.Issue" /> - <item type="style" name="SafetyCenterIssueDismiss" /> - <item type="style" name="SafetyCenterIssueTitle" /> - <item type="style" name="SafetyCenterIssueSubtitle" /> - <item type="style" name="SafetyCenterIssueSummary" /> - <item type="style" name="SafetyCenterIssueActionButtonList" /> - <item type="style" name="SafetyCenterIssueSafetyProtectionSection" /> - <item type="style" name="SafetyCenterIssueCardResolvedImage" /> - <item type="style" name="SafetyCenterIssueCardResolvedTitle" /> - <item type="style" name="SafetyCenterMoreIssues" /> - <item type="style" name="SafetyCenterMoreIssuesTitle" /> - <item type="style" name="SafetyCenterMoreIssuesIcon" /> - <item type="style" name="SafetyCenterMoreIssuesWidgetFrame" /> - <item type="style" name="SafetyCenterMoreIssuesWidget" /> - <item type="style" name="SafetyCenterMoreIssuesWidgetTitle" /> - <item type="style" name="SafetyCenterMoreIssuesWidgetIcon" /> - <item type="style" name="SafetyCenterEntry" /> - <item type="style" name="SafetyCenterEntryDivider" /> - <item type="style" name="SafetyCenterEntryWidgetFrame" /> - <item type="style" name="SafetyCenterEntryIconFrame" /> - <item type="style" name="SafetyCenterEntryIcon" /> - <item type="style" name="SafetyCenterEntryEmptySpace" /> - <item type="style" name="SafetyCenterEntryTextContainer" /> - <item type="style" name="SafetyCenterEntryTitle" /> - <item type="style" name="SafetyCenterEntrySummary" /> - <item type="style" name="SafetyCenterEntryIconAction" /> - <item type="style" name="SafetyCenterGroup" /> - <item type="style" name="SafetyCenterGroupHeader" /> - <item type="style" name="SafetyCenterCollapsedGroupHeader" /> - <item type="style" name="SafetyCenterExpandedGroupHeader" /> - <item type="style" name="SafetyCenterExpandedGroupTitle" /> - <item type="style" name="SafetyCenterGroupEntries" /> - <item type="style" name="SafetyCenterGroupWidgetFrame" /> - <item type="style" name="SafetyCenterExpandedGroupIcon" /> - <item type="style" name="SafetyCenterNoLabelPreferenceCategory" /> - <item type="style" name="SafetyCenterGroupEntry" /> - <item type="style" name="SafetyCenterStaticEntry" /> - <item type="style" name="SafetyCenterStaticEntryTitle" /> - <item type="style" name="SafetyCenterStaticEntrySummary" /> - - <item type="style" name="SafetyCenterQsContainer" /> - <item type="style" name="SafetyCenterLinkText" /> - <item type="style" name="SafetyCenterQsCloseButton" /> - <item type="style" name="SafetyCenterQsSectionTitle" /> - <item type="style" name="SafetyCenterQsPermissionUsage" /> - <item type="style" name="SafetyCenterQsPreferences" /> - <item type="style" name="SafetyCenterQsToggleContainer" /> - <item type="style" name="SafetyCenterQsToggleContainer.Top" /> - <item type="style" name="SafetyCenterQsToggleContainer.Bottom" /> - <item type="style" name="SafetyCenterQsToggleButton" /> - <item type="style" name="SafetyCenterQsToggleButton.Start" /> - <item type="style" name="SafetyCenterQsToggleButton.End" /> - <item type="style" name="SafetyCenterQsToggleTextContainer" /> - <item type="style" name="SafetyCenterQsToggleText" /> - <item type="style" name="SafetyCenterQsToggleText.Title" /> - <item type="style" name="SafetyCenterQsToggleText.Subtitle" /> - <item type="style" name="SafetyCenterQsToggleArrow" /> - <item type="style" name="SafetyCenterQsToggleIcon" /> - <item type="style" name="SafetyCenterIndicatorCardView" /> - <item type="style" name="SafetyCenterIndicatorImageView" /> - <item type="style" name="SafetyCenterIndicatorTitleText" /> - <item type="style" name="SafetyCenterIndicatorLabelText" /> - <item type="style" name="SafetyCenterIndicatorExpandView" /> - <item type="style" name="SafetyCenterIndicatorActionButton" /> - <item type="style" name="SafetyCenterIndicatorSecondaryActionButton" /> - <item type="style" name="SafetyCenterIndicatorForeground" /> </policy> </overlayable> diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt index 98848aa74..01e8f8d15 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PositionInCardList.kt @@ -54,7 +54,7 @@ internal enum class PositionInCardList(val backgroundDrawableResId: Int) { CARD_START, CARD_START_END, CARD_START_LIST_END -> context.resources.getDimensionPixelSize(R.dimen.sc_card_margin) LIST_START, LIST_START_CARD_END, LIST_START_END -> - context.resources.getDimensionPixelSize(R.dimen.sc_list_margin) + context.resources.getDimensionPixelSize(R.dimen.sc_list_margin_top) else -> 0 } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java index 6e6583ae0..d51f3714a 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java @@ -28,7 +28,6 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.preference.Preference; -import com.android.permissioncontroller.R; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; /** A preference which displays a visual representation of a {@link SafetyCenterStaticEntry}. */ @@ -48,7 +47,6 @@ public class StaticSafetyEntryPreference extends Preference implements Comparabl super(context); mEntry = entry; mViewModel = viewModel; - setLayoutResource(R.layout.preference_static_entry); setTitle(entry.getTitle()); setSummary(entry.getSummary()); if (entry.getPendingIntent() != null) { diff --git a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java index 5d72441a9..5c088b5d0 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java @@ -18,20 +18,15 @@ package com.android.safetycenter; import static android.os.Build.VERSION_CODES.TIRAMISU; -import static com.android.safetycenter.WestworldLogger.toSystemEventResult; -import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString; - import static java.util.Collections.emptyList; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.PendingIntent; -import android.content.Context; import android.icu.text.ListFormatter; import android.icu.text.MessageFormat; import android.icu.util.ULocale; -import android.os.SystemClock; import android.safetycenter.SafetyCenterData; import android.safetycenter.SafetyCenterEntry; import android.safetycenter.SafetyCenterEntryGroup; @@ -40,9 +35,7 @@ import android.safetycenter.SafetyCenterIssue; import android.safetycenter.SafetyCenterStaticEntry; import android.safetycenter.SafetyCenterStaticEntryGroup; import android.safetycenter.SafetyCenterStatus; -import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; -import android.safetycenter.SafetySourceErrorDetails; import android.safetycenter.SafetySourceIssue; import android.safetycenter.SafetySourceStatus; import android.safetycenter.config.SafetyCenterConfig; @@ -50,41 +43,35 @@ import android.safetycenter.config.SafetySource; import android.safetycenter.config.SafetySourcesGroup; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import android.util.StatsEvent; import androidx.annotation.RequiresApi; import com.android.permission.PermissionStatsLog; -import com.android.permission.util.UserUtils; -import com.android.safetycenter.SafetyCenterConfigReader.ExternalSafetySource; -import com.android.safetycenter.WestworldLogger.SystemEventResult; import com.android.safetycenter.internaldata.SafetyCenterEntryGroupId; import com.android.safetycenter.internaldata.SafetyCenterEntryId; import com.android.safetycenter.internaldata.SafetyCenterIds; import com.android.safetycenter.internaldata.SafetyCenterIssueActionId; import com.android.safetycenter.internaldata.SafetyCenterIssueId; import com.android.safetycenter.internaldata.SafetyCenterIssueKey; -import com.android.safetycenter.persistence.PersistedSafetyCenterIssue; import com.android.safetycenter.resources.SafetyCenterResourcesContext; -import java.io.PrintWriter; -import java.time.Duration; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; -import java.util.Objects; import javax.annotation.concurrent.NotThreadSafe; /** - * A class that keeps track of all the {@link SafetySourceData} set by safety sources, and - * aggregates them into a {@link SafetyCenterData} object to be used by PermissionController. + * Aggregates {@link SafetySourceData} into a {@link SafetyCenterData} object to be used by Safety + * Center listeners including PermissionController. * * <p>This class isn't thread safe. Thread safety must be handled by the caller. */ +// TODO(b/250812300): Change the name of this class to reflect the "aggregating them into +// SafetyCenterData object" responsibility only. @RequiresApi(TIRAMISU) @NotThreadSafe final class SafetyCenterDataTracker { @@ -97,195 +84,29 @@ final class SafetyCenterDataTracker { SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING = new SafetyCenterIssuesBySeverityDescending(); - private final ArrayMap<SafetySourceKey, SafetySourceData> mSafetySourceDataForKey = - new ArrayMap<>(); - - private final ArraySet<SafetySourceKey> mSafetySourceErrors = new ArraySet<>(); - - private final ArrayMap<SafetyCenterIssueActionId, Long> mSafetyCenterIssueActionsInFlight = - new ArrayMap<>(); - - @NonNull private final Context mContext; @NonNull private final SafetyCenterResourcesContext mSafetyCenterResourcesContext; @NonNull private final SafetyCenterConfigReader mSafetyCenterConfigReader; @NonNull private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker; @NonNull private final WestworldLogger mWestworldLogger; @NonNull private final PendingIntentFactory mPendingIntentFactory; @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache; + @NonNull private final SafetyCenterRepository mSafetyCenterRepository; SafetyCenterDataTracker( - @NonNull Context context, @NonNull SafetyCenterResourcesContext safetyCenterResourcesContext, @NonNull SafetyCenterConfigReader safetyCenterConfigReader, @NonNull SafetyCenterRefreshTracker safetyCenterRefreshTracker, @NonNull WestworldLogger westworldLogger, @NonNull PendingIntentFactory pendingIntentFactory, - @NonNull SafetyCenterIssueCache safetyCenterIssueCache) { - mContext = context; + @NonNull SafetyCenterIssueCache safetyCenterIssueCache, + @NonNull SafetyCenterRepository safetyCenterRepository) { mSafetyCenterResourcesContext = safetyCenterResourcesContext; mSafetyCenterConfigReader = safetyCenterConfigReader; mSafetyCenterRefreshTracker = safetyCenterRefreshTracker; mWestworldLogger = westworldLogger; mPendingIntentFactory = pendingIntentFactory; mSafetyCenterIssueCache = safetyCenterIssueCache; - } - - /** - * Returns whether the Safety Center issue cache has been modified since the last time a - * snapshot was taken. - */ - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - boolean isSafetyCenterIssueCacheDirty() { - return mSafetyCenterIssueCache.isDirty(); - } - - /** - * Takes a snapshot of the Safety Center issue cache that should be written to persistent - * storage. - * - * <p>This method will reset the value reported by {@link #isSafetyCenterIssueCacheDirty} to - * {@code false}. - */ - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - @NonNull - List<PersistedSafetyCenterIssue> snapshotSafetyCenterIssueCache() { - return mSafetyCenterIssueCache.snapshot(); - } - - /** - * Replaces the Safety Center issue cache with the given list of issues. - * - * <p>This method may modify the Safety Center issue cache and change the value reported by - * {@link #isSafetyCenterIssueCacheDirty} to {@code true}. - */ - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - void loadSafetyCenterIssueCache( - @NonNull List<PersistedSafetyCenterIssue> persistedSafetyCenterIssues) { - mSafetyCenterIssueCache.load(persistedSafetyCenterIssues); - } - - /** - * Sets the latest {@link SafetySourceData} for the given {@code safetySourceId}, {@link - * SafetyEvent}, {@code packageName} and {@code userId}, and returns whether there was a change - * to the underlying {@link SafetyCenterData}. - * - * <p>Throws if the request is invalid based on the {@link SafetyCenterConfig}: the given {@code - * safetySourceId}, {@code packageName} and/or {@code userId} are unexpected; or the {@link - * SafetySourceData} does not respect all constraints defined in the config. - * - * <p>Setting a {@code null} {@link SafetySourceData} evicts the current {@link - * SafetySourceData} entry and clears the Safety Center issue cache for the source. - * - * <p>This method may modify the Safety Center issue cache and change the value reported by - * {@link #isSafetyCenterIssueCacheDirty} to {@code true}. - */ - boolean setSafetySourceData( - @Nullable SafetySourceData safetySourceData, - @NonNull String safetySourceId, - @NonNull SafetyEvent safetyEvent, - @NonNull String packageName, - @UserIdInt int userId) { - if (!validateRequest(safetySourceData, safetySourceId, packageName, userId)) { - return false; - } - boolean safetyEventChangedSafetyCenterData = - processSafetyEvent(safetySourceId, safetyEvent, userId, false); - - SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId); - boolean removingSafetySourceErrorChangedSafetyCenterData = mSafetySourceErrors.remove(key); - SafetySourceData existingSafetySourceData = mSafetySourceDataForKey.get(key); - if (Objects.equals(safetySourceData, existingSafetySourceData)) { - return safetyEventChangedSafetyCenterData - || removingSafetySourceErrorChangedSafetyCenterData; - } - - ArraySet<String> issueIds = new ArraySet<>(); - if (safetySourceData == null) { - mSafetySourceDataForKey.remove(key); - } else { - mSafetySourceDataForKey.put(key, safetySourceData); - for (int i = 0; i < safetySourceData.getIssues().size(); i++) { - issueIds.add(safetySourceData.getIssues().get(i).getId()); - } - } - mSafetyCenterIssueCache.updateIssuesForSource(issueIds, safetySourceId, userId); - - return true; - } - - /** - * Returns the latest {@link SafetySourceData} that was set by {@link #setSafetySourceData} for - * the given {@code safetySourceId}, {@code packageName} and {@code userId}. - * - * <p>Throws if the request is invalid based on the {@link SafetyCenterConfig}: the given {@code - * safetySourceId}, {@code packageName} and/or {@code userId} are unexpected. - * - * <p>Returns {@code null} if it was never set since boot, or if the entry was evicted using - * {@link #setSafetySourceData} with a {@code null} value. - */ - @Nullable - SafetySourceData getSafetySourceData( - @NonNull String safetySourceId, @NonNull String packageName, @UserIdInt int userId) { - if (!validateRequest(null, safetySourceId, packageName, userId)) { - return null; - } - return mSafetySourceDataForKey.get(SafetySourceKey.of(safetySourceId, userId)); - } - - /** - * Reports the given {@link SafetySourceErrorDetails} for the given {@code safetySourceId} and - * {@code userId}, and returns whether there was a change to the underlying {@link - * SafetyCenterData}. - * - * <p>Throws if the request is invalid based on the {@link SafetyCenterConfig}: the given {@code - * safetySourceId}, {@code packageName} and/or {@code userId} are unexpected. - */ - boolean reportSafetySourceError( - @NonNull SafetySourceErrorDetails safetySourceErrorDetails, - @NonNull String safetySourceId, - @NonNull String packageName, - @UserIdInt int userId) { - if (!validateRequest(null, safetySourceId, packageName, userId)) { - return false; - } - SafetyEvent safetyEvent = safetySourceErrorDetails.getSafetyEvent(); - Log.w(TAG, "Error reported from source: " + safetySourceId + ", for event: " + safetyEvent); - - boolean safetyEventChangedSafetyCenterData = - processSafetyEvent(safetySourceId, safetyEvent, userId, true); - int safetyEventType = safetyEvent.getType(); - if (safetyEventType == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED - || safetyEventType == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) { - return safetyEventChangedSafetyCenterData; - } - - SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId); - boolean safetySourceErrorChangedSafetyCenterData = setSafetySourceError(key); - return safetyEventChangedSafetyCenterData || safetySourceErrorChangedSafetyCenterData; - } - - /** Marks the given {@link SafetySourceKey} as having errored-out. */ - boolean setSafetySourceError(@NonNull SafetySourceKey safetySourceKey) { - boolean removingSafetySourceDataChangedSafetyCenterData = - mSafetySourceDataForKey.remove(safetySourceKey) != null; - boolean addingSafetySourceErrorChangedSafetyCenterData = - mSafetySourceErrors.add(safetySourceKey); - return removingSafetySourceDataChangedSafetyCenterData - || addingSafetySourceErrorChangedSafetyCenterData; - } - - /** - * 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(@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); - } - } + mSafetyCenterRepository = safetyCenterRepository; } /** @@ -302,136 +123,6 @@ final class SafetyCenterDataTracker { mSafetyCenterConfigReader.getSafetySourcesGroups(), packageName, userProfileGroup); } - /** Marks the given {@link SafetyCenterIssueActionId} as in-flight. */ - void markSafetyCenterIssueActionInFlight( - @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId) { - mSafetyCenterIssueActionsInFlight.put( - safetyCenterIssueActionId, SystemClock.elapsedRealtime()); - } - - /** - * Unmarks the given {@link SafetyCenterIssueActionId} as in-flight, logs that event to - * Westworld with the given {@code result} value, and returns {@code true} if the underlying - * {@link SafetyCenterData} changed. - */ - boolean unmarkSafetyCenterIssueActionInFlight( - @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId, - @SystemEventResult int result) { - Long startElapsedMillis = - mSafetyCenterIssueActionsInFlight.remove(safetyCenterIssueActionId); - if (startElapsedMillis == null) { - Log.w( - TAG, - "Attempt to unmark unknown in-flight action: " - + toUserFriendlyString(safetyCenterIssueActionId)); - return false; - } - - SafetyCenterIssueKey issueKey = safetyCenterIssueActionId.getSafetyCenterIssueKey(); - SafetySourceIssue issue = getSafetySourceIssue(issueKey); - String issueTypeId = issue == null ? null : issue.getIssueTypeId(); - Duration duration = Duration.ofMillis(SystemClock.elapsedRealtime() - startElapsedMillis); - - mWestworldLogger.writeInlineActionSystemEvent( - issueKey.getSafetySourceId(), issueKey.getUserId(), issueTypeId, duration, result); - - if (issue == null || getSafetySourceIssueAction(safetyCenterIssueActionId) == null) { - Log.w( - TAG, - "Attempt to unmark in-flight action for a non-existent issue or action: " - + toUserFriendlyString(safetyCenterIssueActionId)); - return false; - } - - return true; - } - - /** - * Dismisses the given {@link SafetyCenterIssueKey}. - * - * <p>This method may modify the Safety Center issue cache and change the value reported by - * {@link #isSafetyCenterIssueCacheDirty} to {@code true}. - */ - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - void dismissSafetyCenterIssue(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) { - mSafetyCenterIssueCache.dismissIssue(safetyCenterIssueKey); - } - - /** - * Returns the {@link SafetySourceIssue} associated with the given {@link SafetyCenterIssueKey}. - * - * <p>Returns {@code null} if there is no such {@link SafetySourceIssue}, or if it's been - * dismissed. - */ - @Nullable - SafetySourceIssue getSafetySourceIssue(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) { - SafetySourceKey key = - SafetySourceKey.of( - safetyCenterIssueKey.getSafetySourceId(), safetyCenterIssueKey.getUserId()); - SafetySourceData safetySourceData = mSafetySourceDataForKey.get(key); - if (safetySourceData == null) { - return null; - } - List<SafetySourceIssue> safetySourceIssues = safetySourceData.getIssues(); - - SafetySourceIssue targetIssue = null; - for (int i = 0; i < safetySourceIssues.size(); i++) { - SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i); - - if (safetyCenterIssueKey.getSafetySourceIssueId().equals(safetySourceIssue.getId())) { - targetIssue = safetySourceIssue; - break; - } - } - if (targetIssue == null) { - return null; - } - - if (mSafetyCenterIssueCache.isIssueDismissed( - safetyCenterIssueKey, targetIssue.getSeverityLevel())) { - return null; - } - - return targetIssue; - } - - /** - * Returns the {@link SafetySourceIssue.Action} associated with the given {@link - * SafetyCenterIssueActionId}. - * - * <p>Returns {@code null} if there is no associated {@link SafetySourceIssue}, or if it's been - * dismissed. - * - * <p>Returns {@code null} if the {@link SafetySourceIssue.Action} is currently in flight. - */ - @Nullable - SafetySourceIssue.Action getSafetySourceIssueAction( - @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId) { - SafetySourceIssue safetySourceIssue = - getSafetySourceIssue(safetyCenterIssueActionId.getSafetyCenterIssueKey()); - - if (safetySourceIssue == null) { - return null; - } - - if (isInFlight(safetyCenterIssueActionId)) { - return null; - } - - List<SafetySourceIssue.Action> safetySourceIssueActions = safetySourceIssue.getActions(); - for (int i = 0; i < safetySourceIssueActions.size(); i++) { - SafetySourceIssue.Action safetySourceIssueAction = safetySourceIssueActions.get(i); - - if (safetyCenterIssueActionId - .getSafetySourceIssueActionId() - .equals(safetySourceIssueAction.getId())) { - return safetySourceIssueAction; - } - } - - return null; - } - /** * Returns a default {@link SafetyCenterData} object to be returned when the API is disabled. */ @@ -447,93 +138,6 @@ final class SafetyCenterDataTracker { } /** - * Clears all the {@link SafetySourceData} and errors, metadata associated with {@link - * SafetyCenterIssueKey}s, in flight {@link SafetyCenterIssueActionId} and any refresh in - * progress so far, for all users. - * - * <p>This method will modify the Safety Center issue cache and change the value reported by - * {@link #isSafetyCenterIssueCacheDirty} to {@code true}. - */ - void clear() { - mSafetySourceDataForKey.clear(); - mSafetySourceErrors.clear(); - - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - mSafetyCenterIssueCache.clear(); - - mSafetyCenterIssueActionsInFlight.clear(); - } - - /** - * Clears all the {@link SafetySourceData}, metadata associated with {@link - * SafetyCenterIssueKey}s, in flight {@link SafetyCenterIssueActionId} and any refresh in - * progress so far, for the given user. - * - * <p>This method may modify the Safety Center issue cache and change the value reported by - * {@link #isSafetyCenterIssueCacheDirty} to {@code true}. - */ - void clearForUser(@UserIdInt int userId) { - // Loop in reverse index order to be able to remove entries while iterating. - for (int i = mSafetySourceDataForKey.size() - 1; i >= 0; i--) { - SafetySourceKey sourceKey = mSafetySourceDataForKey.keyAt(i); - if (sourceKey.getUserId() == userId) { - mSafetySourceDataForKey.removeAt(i); - } - } - // 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); - } - } - - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - mSafetyCenterIssueCache.clearForUser(userId); - - // Loop in reverse index order to be able to remove entries while iterating. - for (int i = mSafetyCenterIssueActionsInFlight.size() - 1; i >= 0; i--) { - SafetyCenterIssueActionId issueActionId = mSafetyCenterIssueActionsInFlight.keyAt(i); - if (issueActionId.getSafetyCenterIssueKey().getUserId() == userId) { - mSafetyCenterIssueActionsInFlight.removeAt(i); - } - } - } - - /** Dumps state for debugging purposes. */ - void dump(@NonNull PrintWriter fout) { - int dataCount = mSafetySourceDataForKey.size(); - fout.println("SOURCE DATA (" + dataCount + ")"); - for (int i = 0; i < dataCount; i++) { - SafetySourceKey key = mSafetySourceDataForKey.keyAt(i); - SafetySourceData data = mSafetySourceDataForKey.valueAt(i); - fout.println("\t[" + i + "] " + key + " -> " + data); - } - fout.println(); - - int errorCount = mSafetySourceErrors.size(); - fout.println("SOURCE ERRORS (" + errorCount + ")"); - for (int i = 0; i < errorCount; i++) { - SafetySourceKey key = mSafetySourceErrors.valueAt(i); - fout.println("\t[" + i + "] " + key); - } - fout.println(); - - // TODO(b/249950069): Consider removing issue cache APIs from SafetyCenterDataTracker - mSafetyCenterIssueCache.dump(fout); - - int actionInFlightCount = mSafetyCenterIssueActionsInFlight.size(); - fout.println("ACTIONS IN FLIGHT (" + actionInFlightCount + ")"); - for (int i = 0; i < actionInFlightCount; i++) { - SafetyCenterIssueActionId id = mSafetyCenterIssueActionsInFlight.keyAt(i); - long startElapsedMillis = mSafetyCenterIssueActionsInFlight.valueAt(i); - long durationMillis = SystemClock.elapsedRealtime() - startElapsedMillis; - fout.println("\t[" + i + "] " + id + "(duration=" + durationMillis + "ms)"); - } - fout.println(); - } - - /** * Pulls the {@link PermissionStatsLog#SAFETY_STATE} atom and writes all relevant {@link * PermissionStatsLog#SAFETY_SOURCE_STATE_COLLECTED} atoms for the given {@link * UserProfileGroup}. @@ -594,7 +198,7 @@ final class SafetyCenterDataTracker { private void writeSafetySourceStateCollectedAtom( @NonNull String safetySourceId, @UserIdInt int userId, boolean isUserManaged) { SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId); - SafetySourceData safetySourceData = mSafetySourceDataForKey.get(key); + SafetySourceData safetySourceData = mSafetyCenterRepository.getSafetySourceData(key); SafetySourceStatus safetySourceStatus = getSafetySourceStatus(safetySourceData); List<SafetySourceIssue> safetySourceIssues = safetySourceData == null ? emptyList() : safetySourceData.getIssues(); @@ -631,177 +235,6 @@ final class SafetyCenterDataTracker { dismissedIssuesCount); } - private boolean isInFlight(@NonNull SafetyCenterIssueActionId safetyCenterIssueActionId) { - return mSafetyCenterIssueActionsInFlight.containsKey(safetyCenterIssueActionId); - } - - /** - * Checks if a request to the SafetyCenter is valid, and returns whether the request should be - * processed. - */ - private boolean validateRequest( - @Nullable SafetySourceData safetySourceData, - @NonNull String safetySourceId, - @NonNull String packageName, - @UserIdInt int userId) { - ExternalSafetySource externalSafetySource = - mSafetyCenterConfigReader.getExternalSafetySource(safetySourceId); - if (externalSafetySource == null) { - throw new IllegalArgumentException("Unexpected safety source: " + safetySourceId); - } - - SafetySource safetySource = externalSafetySource.getSafetySource(); - - // TODO(b/222330089): Security: check certs? - if (!packageName.equals(safetySource.getPackageName())) { - throw new IllegalArgumentException( - "Unexpected package name: " - + packageName - + ", for safety source: " - + safetySourceId); - } - - // TODO(b/222327845): Security: check package is installed for user? - - if (UserUtils.isManagedProfile(userId, mContext) - && !SafetySources.supportsManagedProfiles(safetySource)) { - throw new IllegalArgumentException( - "Unexpected managed profile request for safety source: " + safetySourceId); - } - - boolean retrievingOrClearingData = safetySourceData == null; - if (retrievingOrClearingData) { - return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId); - } - - SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); - - if (safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY - && safetySourceStatus != null) { - throw new IllegalArgumentException( - "Unexpected status for issue only safety source: " + safetySourceId); - } - - if (safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC - && safetySourceStatus == null) { - throw new IllegalArgumentException( - "Missing status for dynamic safety source: " + safetySourceId); - } - - if (safetySourceStatus != null) { - int sourceSeverityLevel = safetySourceStatus.getSeverityLevel(); - - if (externalSafetySource.hasEntryInRigidGroup() - && sourceSeverityLevel != SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED) { - throw new IllegalArgumentException( - "Safety source: " - + safetySourceId - + " is in a rigid group but specified a severity level: " - + sourceSeverityLevel); - } - - int maxSourceSeverityLevel = - Math.max( - SafetySourceData.SEVERITY_LEVEL_INFORMATION, - safetySource.getMaxSeverityLevel()); - - if (sourceSeverityLevel > maxSourceSeverityLevel) { - throw new IllegalArgumentException( - "Unexpected severity level: " - + sourceSeverityLevel - + ", for safety source: " - + safetySourceId); - } - } - - List<SafetySourceIssue> safetySourceIssues = safetySourceData.getIssues(); - - for (int i = 0; i < safetySourceIssues.size(); i++) { - SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i); - int issueSeverityLevel = safetySourceIssue.getSeverityLevel(); - if (issueSeverityLevel > safetySource.getMaxSeverityLevel()) { - throw new IllegalArgumentException( - "Unexpected severity level: " - + issueSeverityLevel - + ", for issue in safety source: " - + safetySourceId); - } - - int issueCategory = safetySourceIssue.getIssueCategory(); - if (!SafetyCenterFlags.isIssueCategoryAllowedForSource(issueCategory, safetySourceId)) { - throw new IllegalArgumentException( - "Unexpected issue category: " - + issueCategory - + ", for issue in safety source: " - + safetySourceId); - } - } - - return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId); - } - - private boolean processSafetyEvent( - @NonNull String safetySourceId, - @NonNull SafetyEvent safetyEvent, - @UserIdInt int userId, - boolean isError) { - int type = safetyEvent.getType(); - switch (type) { - case SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED: - String refreshBroadcastId = safetyEvent.getRefreshBroadcastId(); - if (refreshBroadcastId == null) { - Log.w( - TAG, - "Received safety event of type " - + safetyEvent.getType() - + " without a refresh broadcast id"); - return false; - } - return mSafetyCenterRefreshTracker.reportSourceRefreshCompleted( - refreshBroadcastId, safetySourceId, userId, !isError); - case SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: - case SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: - String safetySourceIssueId = safetyEvent.getSafetySourceIssueId(); - if (safetySourceIssueId == null) { - Log.w( - TAG, - "Received safety event of type " - + safetyEvent.getType() - + " without a safety source issue id"); - return false; - } - String safetySourceIssueActionId = safetyEvent.getSafetySourceIssueActionId(); - if (safetySourceIssueActionId == null) { - Log.w( - TAG, - "Received safety event of type " - + safetyEvent.getType() - + " without a safety source issue action id"); - return false; - } - SafetyCenterIssueKey safetyCenterIssueKey = - SafetyCenterIssueKey.newBuilder() - .setSafetySourceId(safetySourceId) - .setSafetySourceIssueId(safetySourceIssueId) - .setUserId(userId) - .build(); - SafetyCenterIssueActionId safetyCenterIssueActionId = - SafetyCenterIssueActionId.newBuilder() - .setSafetyCenterIssueKey(safetyCenterIssueKey) - .setSafetySourceIssueActionId(safetySourceIssueActionId) - .build(); - boolean success = type == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED; - int result = toSystemEventResult(success); - return unmarkSafetyCenterIssueActionInFlight(safetyCenterIssueActionId, result); - case SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: - case SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: - case SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED: - return false; - } - Log.w(TAG, "Unexpected SafetyEvent.Type: " + type); - return false; - } - @NonNull private SafetyCenterData getSafetyCenterData( @NonNull List<SafetySourcesGroup> safetySourcesGroups, @@ -918,7 +351,7 @@ final class SafetyCenterDataTracker { @NonNull SafetySource safetySource, @UserIdInt int userId) { SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId); - SafetySourceData safetySourceData = mSafetySourceDataForKey.get(key); + SafetySourceData safetySourceData = mSafetyCenterRepository.getSafetySourceData(key); if (safetySourceData == null) { return; @@ -1004,7 +437,7 @@ final class SafetyCenterDataTracker { safetySourceIssueAction.getLabel(), safetySourceIssueAction.getPendingIntent()) .setSuccessMessage(safetySourceIssueAction.getSuccessMessage()) - .setIsInFlight(isInFlight(safetyCenterIssueActionId)) + .setIsInFlight(mSafetyCenterRepository.actionIsInFlight(safetyCenterIssueActionId)) .setWillResolve(safetySourceIssueAction.willResolve()) .build(); } @@ -1127,7 +560,8 @@ final class SafetyCenterDataTracker { } SafetySourceKey key = toSafetySourceKey(entry.getId()); - SafetySourceData safetySourceData = mSafetySourceDataForKey.get(key); + SafetySourceData safetySourceData = + mSafetyCenterRepository.getSafetySourceData(key); boolean hasIssues = safetySourceData != null && !safetySourceData.getIssues().isEmpty(); @@ -1145,7 +579,7 @@ final class SafetyCenterDataTracker { SafetyCenterEntry entry = entries.get(i); SafetySourceKey key = toSafetySourceKey(entry.getId()); - if (mSafetySourceErrors.contains(key)) { + if (mSafetyCenterRepository.sourceHasError(key)) { errorEntries++; } } @@ -1230,7 +664,7 @@ final class SafetyCenterDataTracker { case SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC: SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId); SafetySourceStatus safetySourceStatus = - getSafetySourceStatus(mSafetySourceDataForKey.get(key)); + getSafetySourceStatus(mSafetyCenterRepository.getSafetySourceData(key)); boolean defaultEntryDueToQuietMode = isUserManaged && !isManagedUserRunning; if (safetySourceStatus != null && !defaultEntryDueToQuietMode) { PendingIntent pendingIntent = safetySourceStatus.getPendingIntent(); @@ -1334,7 +768,8 @@ final class SafetyCenterDataTracker { safetySource.getTitleForWorkResId()) : mSafetyCenterResourcesContext.getString(safetySource.getTitleResId()); CharSequence summary = - mSafetySourceErrors.contains(SafetySourceKey.of(safetySource.getId(), userId)) + mSafetyCenterRepository.sourceHasError( + SafetySourceKey.of(safetySource.getId(), userId)) ? getRefreshErrorString(1) : mSafetyCenterResourcesContext.getOptionalString( safetySource.getSummaryResId()); @@ -1420,7 +855,8 @@ final class SafetyCenterDataTracker { } boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning; boolean hasError = - mSafetySourceErrors.contains(SafetySourceKey.of(safetySource.getId(), userId)); + mSafetyCenterRepository.sourceHasError( + SafetySourceKey.of(safetySource.getId(), userId)); if (isQuietModeEnabled || hasError) { safetyCenterOverallState.addEntryOverallSeverityLevel( SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN); @@ -1441,7 +877,7 @@ final class SafetyCenterDataTracker { case SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC: SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId); SafetySourceStatus safetySourceStatus = - getSafetySourceStatus(mSafetySourceDataForKey.get(key)); + getSafetySourceStatus(mSafetyCenterRepository.getSafetySourceData(key)); boolean defaultEntryDueToQuietMode = isUserManaged && !isManagedUserRunning; if (safetySourceStatus != null && !defaultEntryDueToQuietMode) { PendingIntent pendingIntent = safetySourceStatus.getPendingIntent(); @@ -1501,7 +937,8 @@ final class SafetyCenterDataTracker { safetySource.getTitleForWorkResId()) : mSafetyCenterResourcesContext.getString(safetySource.getTitleResId()); CharSequence summary = - mSafetySourceErrors.contains(SafetySourceKey.of(safetySource.getId(), userId)) + mSafetyCenterRepository.sourceHasError( + SafetySourceKey.of(safetySource.getId(), userId)) ? getRefreshErrorString(1) : mSafetyCenterResourcesContext.getOptionalString( safetySource.getSummaryResId()); diff --git a/service/java/com/android/safetycenter/SafetyCenterRepository.java b/service/java/com/android/safetycenter/SafetyCenterRepository.java new file mode 100644 index 000000000..9821dc218 --- /dev/null +++ b/service/java/com/android/safetycenter/SafetyCenterRepository.java @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.safetycenter; + +import static android.os.Build.VERSION_CODES.TIRAMISU; + +import static com.android.safetycenter.WestworldLogger.toSystemEventResult; +import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.Context; +import android.os.SystemClock; +import android.safetycenter.SafetyCenterData; +import android.safetycenter.SafetyEvent; +import android.safetycenter.SafetySourceData; +import android.safetycenter.SafetySourceErrorDetails; +import android.safetycenter.SafetySourceIssue; +import android.safetycenter.SafetySourceStatus; +import android.safetycenter.config.SafetyCenterConfig; +import android.safetycenter.config.SafetySource; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import androidx.annotation.RequiresApi; + +import com.android.permission.util.UserUtils; +import com.android.safetycenter.internaldata.SafetyCenterIssueActionId; +import com.android.safetycenter.internaldata.SafetyCenterIssueKey; + +import java.io.PrintWriter; +import java.time.Duration; +import java.util.List; +import java.util.Objects; + +import javax.annotation.concurrent.NotThreadSafe; + +/** + * Repository for {@link SafetySourceData} and other data managed by Safety Center including {@link + * SafetySourceErrorDetails} and metadata about which issue actions are in-flight. + * + * <p>This class isn't thread safe. Thread safety must be handled by the caller. + */ +@RequiresApi(TIRAMISU) +@NotThreadSafe +final class SafetyCenterRepository { + + private static final String TAG = "SafetyCenterRepository"; + + private final ArrayMap<SafetySourceKey, SafetySourceData> mSafetySourceDataForKey = + new ArrayMap<>(); + + private final ArraySet<SafetySourceKey> mSafetySourceErrors = new ArraySet<>(); + + private final ArrayMap<SafetyCenterIssueActionId, Long> mSafetyCenterIssueActionsInFlight = + new ArrayMap<>(); + + @NonNull private final Context mContext; + @NonNull private final SafetyCenterConfigReader mSafetyCenterConfigReader; + @NonNull private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker; + @NonNull private final WestworldLogger mWestworldLogger; + @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache; + + SafetyCenterRepository( + @NonNull Context context, + @NonNull SafetyCenterConfigReader safetyCenterConfigReader, + @NonNull SafetyCenterRefreshTracker safetyCenterRefreshTracker, + @NonNull WestworldLogger westworldLogger, + @NonNull SafetyCenterIssueCache safetyCenterIssueCache) { + mContext = context; + mSafetyCenterConfigReader = safetyCenterConfigReader; + mSafetyCenterRefreshTracker = safetyCenterRefreshTracker; + mWestworldLogger = westworldLogger; + mSafetyCenterIssueCache = safetyCenterIssueCache; + } + + /** + * Sets the latest {@link SafetySourceData} for the given {@code safetySourceId}, {@link + * SafetyEvent}, {@code packageName} and {@code userId}, and returns whether there was a change + * to the underlying {@link SafetyCenterData}. + * + * <p>Throws if the request is invalid based on the {@link SafetyCenterConfig}: the given {@code + * safetySourceId}, {@code packageName} and/or {@code userId} are unexpected; or the {@link + * SafetySourceData} does not respect all constraints defined in the config. + * + * <p>Setting a {@code null} {@link SafetySourceData} evicts the current {@link + * SafetySourceData} entry and clears the Safety Center issue cache for the source. + * + * <p>This method may modify the {@link SafetyCenterIssueCache}. + */ + boolean setSafetySourceData( + @Nullable SafetySourceData safetySourceData, + @NonNull String safetySourceId, + @NonNull SafetyEvent safetyEvent, + @NonNull String packageName, + @UserIdInt int userId) { + if (!validateRequest(safetySourceData, safetySourceId, packageName, userId)) { + return false; + } + boolean safetyEventChangedSafetyCenterData = + processSafetyEvent(safetySourceId, safetyEvent, userId, false); + + SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId); + boolean removingSafetySourceErrorChangedSafetyCenterData = mSafetySourceErrors.remove(key); + SafetySourceData existingSafetySourceData = mSafetySourceDataForKey.get(key); + if (Objects.equals(safetySourceData, existingSafetySourceData)) { + return safetyEventChangedSafetyCenterData + || removingSafetySourceErrorChangedSafetyCenterData; + } + + ArraySet<String> issueIds = new ArraySet<>(); + if (safetySourceData == null) { + mSafetySourceDataForKey.remove(key); + } else { + mSafetySourceDataForKey.put(key, safetySourceData); + for (int i = 0; i < safetySourceData.getIssues().size(); i++) { + issueIds.add(safetySourceData.getIssues().get(i).getId()); + } + } + mSafetyCenterIssueCache.updateIssuesForSource(issueIds, safetySourceId, userId); + + return true; + } + + /** + * Returns the latest {@link SafetySourceData} that was set by {@link #setSafetySourceData} for + * the given {@code safetySourceId}, {@code packageName} and {@code userId}. + * + * <p>Throws if the request is invalid based on the {@link SafetyCenterConfig}: the given {@code + * safetySourceId}, {@code packageName} and/or {@code userId} are unexpected. + * + * <p>Returns {@code null} if it was never set since boot, or if the entry was evicted using + * {@link #setSafetySourceData} with a {@code null} value. + */ + @Nullable + SafetySourceData getSafetySourceData( + @NonNull String safetySourceId, @NonNull String packageName, @UserIdInt int userId) { + if (!validateRequest(null, safetySourceId, packageName, userId)) { + return null; + } + return getSafetySourceData(SafetySourceKey.of(safetySourceId, userId)); + } + + /** + * Returns the latest {@link SafetySourceData} that was set by {@link #setSafetySourceData} for + * the given {@link SafetySourceKey}. + * + * <p>This method does not perform any validation, {@link #getSafetySourceData(String, String, + * int)} should be called wherever validation is required. + */ + @Nullable + SafetySourceData getSafetySourceData(@NonNull SafetySourceKey safetySourceKey) { + return mSafetySourceDataForKey.get(safetySourceKey); + } + + /** + * Reports the given {@link SafetySourceErrorDetails} for the given {@code safetySourceId} and + * {@code userId}, and returns whether there was a change to the underlying {@link + * SafetyCenterData}. + * + * <p>Throws if the request is invalid based on the {@link SafetyCenterConfig}: the given {@code + * safetySourceId}, {@code packageName} and/or {@code userId} are unexpected. + */ + boolean reportSafetySourceError( + @NonNull SafetySourceErrorDetails safetySourceErrorDetails, + @NonNull String safetySourceId, + @NonNull String packageName, + @UserIdInt int userId) { + if (!validateRequest(null, safetySourceId, packageName, userId)) { + return false; + } + SafetyEvent safetyEvent = safetySourceErrorDetails.getSafetyEvent(); + Log.w(TAG, "Error reported from source: " + safetySourceId + ", for event: " + safetyEvent); + + boolean safetyEventChangedSafetyCenterData = + processSafetyEvent(safetySourceId, safetyEvent, userId, true); + int safetyEventType = safetyEvent.getType(); + if (safetyEventType == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED + || safetyEventType == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) { + return safetyEventChangedSafetyCenterData; + } + + SafetySourceKey key = SafetySourceKey.of(safetySourceId, userId); + boolean safetySourceErrorChangedSafetyCenterData = setSafetySourceError(key); + return safetyEventChangedSafetyCenterData || safetySourceErrorChangedSafetyCenterData; + } + + /** Marks the given {@link SafetySourceKey} as having errored-out. */ + boolean setSafetySourceError(@NonNull SafetySourceKey safetySourceKey) { + boolean removingSafetySourceDataChangedSafetyCenterData = + mSafetySourceDataForKey.remove(safetySourceKey) != null; + boolean addingSafetySourceErrorChangedSafetyCenterData = + mSafetySourceErrors.add(safetySourceKey); + return removingSafetySourceDataChangedSafetyCenterData + || addingSafetySourceErrorChangedSafetyCenterData; + } + + /** + * 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(@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); + } + } + } + + /** Marks the given {@link SafetyCenterIssueActionId} as in-flight. */ + void markSafetyCenterIssueActionInFlight( + @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId) { + mSafetyCenterIssueActionsInFlight.put( + safetyCenterIssueActionId, SystemClock.elapsedRealtime()); + } + + /** + * Unmarks the given {@link SafetyCenterIssueActionId} as in-flight, logs that event to + * Westworld with the given {@code result} value, and returns {@code true} if the underlying + * {@link SafetyCenterData} changed. + */ + boolean unmarkSafetyCenterIssueActionInFlight( + @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId, + @WestworldLogger.SystemEventResult int result) { + Long startElapsedMillis = + mSafetyCenterIssueActionsInFlight.remove(safetyCenterIssueActionId); + if (startElapsedMillis == null) { + Log.w( + TAG, + "Attempt to unmark unknown in-flight action: " + + toUserFriendlyString(safetyCenterIssueActionId)); + return false; + } + + SafetyCenterIssueKey issueKey = safetyCenterIssueActionId.getSafetyCenterIssueKey(); + SafetySourceIssue issue = getSafetySourceIssue(issueKey); + String issueTypeId = issue == null ? null : issue.getIssueTypeId(); + Duration duration = Duration.ofMillis(SystemClock.elapsedRealtime() - startElapsedMillis); + + mWestworldLogger.writeInlineActionSystemEvent( + issueKey.getSafetySourceId(), issueKey.getUserId(), issueTypeId, duration, result); + + if (issue == null || getSafetySourceIssueAction(safetyCenterIssueActionId) == null) { + Log.w( + TAG, + "Attempt to unmark in-flight action for a non-existent issue or action: " + + toUserFriendlyString(safetyCenterIssueActionId)); + return false; + } + + return true; + } + + /** + * Dismisses the given {@link SafetyCenterIssueKey}. + * + * <p>This method may modify the {@link SafetyCenterIssueCache}. + */ + void dismissSafetyCenterIssue(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) { + mSafetyCenterIssueCache.dismissIssue(safetyCenterIssueKey); + } + + /** + * Returns the {@link SafetySourceIssue} associated with the given {@link SafetyCenterIssueKey}. + * + * <p>Returns {@code null} if there is no such {@link SafetySourceIssue}, or if it's been + * dismissed. + */ + @Nullable + SafetySourceIssue getSafetySourceIssue(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) { + SafetySourceKey key = + SafetySourceKey.of( + safetyCenterIssueKey.getSafetySourceId(), safetyCenterIssueKey.getUserId()); + SafetySourceData safetySourceData = mSafetySourceDataForKey.get(key); + if (safetySourceData == null) { + return null; + } + List<SafetySourceIssue> safetySourceIssues = safetySourceData.getIssues(); + + SafetySourceIssue targetIssue = null; + for (int i = 0; i < safetySourceIssues.size(); i++) { + SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i); + + if (safetyCenterIssueKey.getSafetySourceIssueId().equals(safetySourceIssue.getId())) { + targetIssue = safetySourceIssue; + break; + } + } + if (targetIssue == null) { + return null; + } + + if (mSafetyCenterIssueCache.isIssueDismissed( + safetyCenterIssueKey, targetIssue.getSeverityLevel())) { + return null; + } + + return targetIssue; + } + + /** + * Returns the {@link SafetySourceIssue.Action} associated with the given {@link + * SafetyCenterIssueActionId}. + * + * <p>Returns {@code null} if there is no associated {@link SafetySourceIssue}, or if it's been + * dismissed. + * + * <p>Returns {@code null} if the {@link SafetySourceIssue.Action} is currently in flight. + */ + @Nullable + SafetySourceIssue.Action getSafetySourceIssueAction( + @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId) { + SafetySourceIssue safetySourceIssue = + getSafetySourceIssue(safetyCenterIssueActionId.getSafetyCenterIssueKey()); + + if (safetySourceIssue == null) { + return null; + } + + if (actionIsInFlight(safetyCenterIssueActionId)) { + return null; + } + + List<SafetySourceIssue.Action> safetySourceIssueActions = safetySourceIssue.getActions(); + for (int i = 0; i < safetySourceIssueActions.size(); i++) { + SafetySourceIssue.Action safetySourceIssueAction = safetySourceIssueActions.get(i); + + if (safetyCenterIssueActionId + .getSafetySourceIssueActionId() + .equals(safetySourceIssueAction.getId())) { + return safetySourceIssueAction; + } + } + + return null; + } + + /** Clears all {@link SafetySourceData}, errors, issues and in flight actions for all users. */ + void clear() { + mSafetySourceDataForKey.clear(); + mSafetySourceErrors.clear(); + mSafetyCenterIssueCache.clear(); + mSafetyCenterIssueActionsInFlight.clear(); + } + + /** + * Clears all {@link SafetySourceData}, errors, issues and in flight actions, for the given + * user. + */ + void clearForUser(@UserIdInt int userId) { + // Loop in reverse index order to be able to remove entries while iterating. + for (int i = mSafetySourceDataForKey.size() - 1; i >= 0; i--) { + SafetySourceKey sourceKey = mSafetySourceDataForKey.keyAt(i); + if (sourceKey.getUserId() == userId) { + mSafetySourceDataForKey.removeAt(i); + } + } + // 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); + } + } + // Issue cache implements this itself. + mSafetyCenterIssueCache.clearForUser(userId); + // Loop in reverse index order to be able to remove entries while iterating. + for (int i = mSafetyCenterIssueActionsInFlight.size() - 1; i >= 0; i--) { + SafetyCenterIssueActionId issueActionId = mSafetyCenterIssueActionsInFlight.keyAt(i); + if (issueActionId.getSafetyCenterIssueKey().getUserId() == userId) { + mSafetyCenterIssueActionsInFlight.removeAt(i); + } + } + } + + /** Dumps state for debugging purposes. */ + void dump(@NonNull PrintWriter fout) { + int dataCount = mSafetySourceDataForKey.size(); + fout.println("SOURCE DATA (" + dataCount + ")"); + for (int i = 0; i < dataCount; i++) { + SafetySourceKey key = mSafetySourceDataForKey.keyAt(i); + SafetySourceData data = mSafetySourceDataForKey.valueAt(i); + fout.println("\t[" + i + "] " + key + " -> " + data); + } + fout.println(); + + int errorCount = mSafetySourceErrors.size(); + fout.println("SOURCE ERRORS (" + errorCount + ")"); + for (int i = 0; i < errorCount; i++) { + SafetySourceKey key = mSafetySourceErrors.valueAt(i); + fout.println("\t[" + i + "] " + key); + } + fout.println(); + + int actionInFlightCount = mSafetyCenterIssueActionsInFlight.size(); + fout.println("ACTIONS IN FLIGHT (" + actionInFlightCount + ")"); + for (int i = 0; i < actionInFlightCount; i++) { + SafetyCenterIssueActionId id = mSafetyCenterIssueActionsInFlight.keyAt(i); + long startElapsedMillis = mSafetyCenterIssueActionsInFlight.valueAt(i); + long durationMillis = SystemClock.elapsedRealtime() - startElapsedMillis; + fout.println("\t[" + i + "] " + id + "(duration=" + durationMillis + "ms)"); + } + fout.println(); + } + + /** Returns {@code true} if the given issue action is in flight. */ + boolean actionIsInFlight(@NonNull SafetyCenterIssueActionId safetyCenterIssueActionId) { + return mSafetyCenterIssueActionsInFlight.containsKey(safetyCenterIssueActionId); + } + + /** Returns {@code true} if the given source has an error. */ + boolean sourceHasError(@NonNull SafetySourceKey safetySourceKey) { + return mSafetySourceErrors.contains(safetySourceKey); + } + + /** + * Checks if a request to the SafetyCenter is valid, and returns whether the request should be + * processed. + */ + private boolean validateRequest( + @Nullable SafetySourceData safetySourceData, + @NonNull String safetySourceId, + @NonNull String packageName, + @UserIdInt int userId) { + SafetyCenterConfigReader.ExternalSafetySource externalSafetySource = + mSafetyCenterConfigReader.getExternalSafetySource(safetySourceId); + if (externalSafetySource == null) { + throw new IllegalArgumentException("Unexpected safety source: " + safetySourceId); + } + + SafetySource safetySource = externalSafetySource.getSafetySource(); + + // TODO(b/222330089): Security: check certs? + if (!packageName.equals(safetySource.getPackageName())) { + throw new IllegalArgumentException( + "Unexpected package name: " + + packageName + + ", for safety source: " + + safetySourceId); + } + + // TODO(b/222327845): Security: check package is installed for user? + + if (UserUtils.isManagedProfile(userId, mContext) + && !SafetySources.supportsManagedProfiles(safetySource)) { + throw new IllegalArgumentException( + "Unexpected managed profile request for safety source: " + safetySourceId); + } + + boolean retrievingOrClearingData = safetySourceData == null; + if (retrievingOrClearingData) { + return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId); + } + + SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); + + if (safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY + && safetySourceStatus != null) { + throw new IllegalArgumentException( + "Unexpected status for issue only safety source: " + safetySourceId); + } + + if (safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC + && safetySourceStatus == null) { + throw new IllegalArgumentException( + "Missing status for dynamic safety source: " + safetySourceId); + } + + if (safetySourceStatus != null) { + int sourceSeverityLevel = safetySourceStatus.getSeverityLevel(); + + if (externalSafetySource.hasEntryInRigidGroup() + && sourceSeverityLevel != SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED) { + throw new IllegalArgumentException( + "Safety source: " + + safetySourceId + + " is in a rigid group but specified a severity level: " + + sourceSeverityLevel); + } + + int maxSourceSeverityLevel = + Math.max( + SafetySourceData.SEVERITY_LEVEL_INFORMATION, + safetySource.getMaxSeverityLevel()); + + if (sourceSeverityLevel > maxSourceSeverityLevel) { + throw new IllegalArgumentException( + "Unexpected severity level: " + + sourceSeverityLevel + + ", for safety source: " + + safetySourceId); + } + } + + List<SafetySourceIssue> safetySourceIssues = safetySourceData.getIssues(); + + for (int i = 0; i < safetySourceIssues.size(); i++) { + SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i); + int issueSeverityLevel = safetySourceIssue.getSeverityLevel(); + if (issueSeverityLevel > safetySource.getMaxSeverityLevel()) { + throw new IllegalArgumentException( + "Unexpected severity level: " + + issueSeverityLevel + + ", for issue in safety source: " + + safetySourceId); + } + + int issueCategory = safetySourceIssue.getIssueCategory(); + if (!SafetyCenterFlags.isIssueCategoryAllowedForSource(issueCategory, safetySourceId)) { + throw new IllegalArgumentException( + "Unexpected issue category: " + + issueCategory + + ", for issue in safety source: " + + safetySourceId); + } + } + + return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId); + } + + private boolean processSafetyEvent( + @NonNull String safetySourceId, + @NonNull SafetyEvent safetyEvent, + @UserIdInt int userId, + boolean isError) { + int type = safetyEvent.getType(); + switch (type) { + case SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED: + String refreshBroadcastId = safetyEvent.getRefreshBroadcastId(); + if (refreshBroadcastId == null) { + Log.w( + TAG, + "Received safety event of type " + + safetyEvent.getType() + + " without a refresh broadcast id"); + return false; + } + return mSafetyCenterRefreshTracker.reportSourceRefreshCompleted( + refreshBroadcastId, safetySourceId, userId, !isError); + case SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: + case SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: + String safetySourceIssueId = safetyEvent.getSafetySourceIssueId(); + if (safetySourceIssueId == null) { + Log.w( + TAG, + "Received safety event of type " + + safetyEvent.getType() + + " without a safety source issue id"); + return false; + } + String safetySourceIssueActionId = safetyEvent.getSafetySourceIssueActionId(); + if (safetySourceIssueActionId == null) { + Log.w( + TAG, + "Received safety event of type " + + safetyEvent.getType() + + " without a safety source issue action id"); + return false; + } + SafetyCenterIssueKey safetyCenterIssueKey = + SafetyCenterIssueKey.newBuilder() + .setSafetySourceId(safetySourceId) + .setSafetySourceIssueId(safetySourceIssueId) + .setUserId(userId) + .build(); + SafetyCenterIssueActionId safetyCenterIssueActionId = + SafetyCenterIssueActionId.newBuilder() + .setSafetyCenterIssueKey(safetyCenterIssueKey) + .setSafetySourceIssueActionId(safetySourceIssueActionId) + .build(); + boolean success = type == SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED; + int result = toSystemEventResult(success); + return unmarkSafetyCenterIssueActionInFlight(safetyCenterIssueActionId, result); + case SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: + case SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: + case SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED: + return false; + } + Log.w(TAG, "Unexpected SafetyEvent.Type: " + type); + return false; + } +} diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java index d5486d43a..99aab2ba5 100644 --- a/service/java/com/android/safetycenter/SafetyCenterService.java +++ b/service/java/com/android/safetycenter/SafetyCenterService.java @@ -144,9 +144,14 @@ public final class SafetyCenterService extends SystemService { @GuardedBy("mApiLock") @NonNull + private final SafetyCenterRepository mSafetyCenterRepository; + + @GuardedBy("mApiLock") + @NonNull private final SafetyCenterDataTracker mSafetyCenterDataTracker; @GuardedBy("mApiLock") + @NonNull private final SafetyCenterListeners mSafetyCenterListeners; @GuardedBy("mApiLock") @@ -154,6 +159,10 @@ public final class SafetyCenterService extends SystemService { @GuardedBy("mApiLock") @NonNull + private final SafetyCenterIssueCache mSafetyCenterIssueCache; + + @GuardedBy("mApiLock") + @NonNull private final SafetyCenterBroadcastDispatcher mSafetyCenterBroadcastDispatcher; @NonNull private final AppOpsManager mAppOpsManager; @@ -168,15 +177,23 @@ public final class SafetyCenterService extends SystemService { mSafetyCenterConfigReader = new SafetyCenterConfigReader(mSafetyCenterResourcesContext); WestworldLogger westworldLogger = new WestworldLogger(context, mSafetyCenterConfigReader); mSafetyCenterRefreshTracker = new SafetyCenterRefreshTracker(westworldLogger); + mSafetyCenterIssueCache = new SafetyCenterIssueCache(mSafetyCenterConfigReader); + mSafetyCenterRepository = + new SafetyCenterRepository( + context, + mSafetyCenterConfigReader, + mSafetyCenterRefreshTracker, + westworldLogger, + mSafetyCenterIssueCache); mSafetyCenterDataTracker = new SafetyCenterDataTracker( - context, mSafetyCenterResourcesContext, mSafetyCenterConfigReader, mSafetyCenterRefreshTracker, westworldLogger, new PendingIntentFactory(context), - new SafetyCenterIssueCache(mSafetyCenterConfigReader)); + mSafetyCenterIssueCache, + mSafetyCenterRepository); mSafetyCenterListeners = new SafetyCenterListeners(mSafetyCenterDataTracker); mSafetyCenterBroadcastDispatcher = new SafetyCenterBroadcastDispatcher( @@ -264,7 +281,7 @@ public final class SafetyCenterService extends SystemService { UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId); synchronized (mApiLock) { boolean hasUpdate = - mSafetyCenterDataTracker.setSafetySourceData( + mSafetyCenterRepository.setSafetySourceData( safetySourceData, safetySourceId, safetyEvent, packageName, userId); mSafetyCenterListeners.deliverUpdateForUserProfileGroup( userProfileGroup, hasUpdate, null); @@ -290,7 +307,7 @@ public final class SafetyCenterService extends SystemService { } synchronized (mApiLock) { - return mSafetyCenterDataTracker.getSafetySourceData( + return mSafetyCenterRepository.getSafetySourceData( safetySourceId, packageName, userId); } } @@ -316,7 +333,7 @@ public final class SafetyCenterService extends SystemService { UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId); synchronized (mApiLock) { boolean hasUpdate = - mSafetyCenterDataTracker.reportSafetySourceError( + mSafetyCenterRepository.reportSafetySourceError( errorDetails, safetySourceId, packageName, userId); SafetyCenterErrorDetails safetyCenterErrorDetails = null; if (hasUpdate @@ -447,7 +464,7 @@ public final class SafetyCenterService extends SystemService { "dismissSafetyCenterIssue", userProfileGroup, safetyCenterIssueKey.getUserId()); synchronized (mApiLock) { SafetySourceIssue safetySourceIssue = - mSafetyCenterDataTracker.getSafetySourceIssue(safetyCenterIssueKey); + mSafetyCenterRepository.getSafetySourceIssue(safetyCenterIssueKey); if (safetySourceIssue == null) { Log.w( TAG, @@ -457,7 +474,7 @@ public final class SafetyCenterService extends SystemService { // button multiple times in a row. return; } - mSafetyCenterDataTracker.dismissSafetyCenterIssue(safetyCenterIssueKey); + mSafetyCenterRepository.dismissSafetyCenterIssue(safetyCenterIssueKey); scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked(); PendingIntent onDismissPendingIntent = safetySourceIssue.getOnDismissPendingIntent(); @@ -510,7 +527,7 @@ public final class SafetyCenterService extends SystemService { safetyCenterIssueKey.getUserId()); synchronized (mApiLock) { SafetySourceIssue.Action safetySourceIssueAction = - mSafetyCenterDataTracker.getSafetySourceIssueAction( + mSafetyCenterRepository.getSafetySourceIssueAction( safetyCenterIssueActionId); if (safetySourceIssueAction == null) { Log.w( @@ -543,7 +560,7 @@ public final class SafetyCenterService extends SystemService { return; } if (safetySourceIssueAction.willResolve()) { - mSafetyCenterDataTracker.markSafetyCenterIssueActionInFlight( + mSafetyCenterRepository.markSafetyCenterIssueActionInFlight( safetyCenterIssueActionId); ResolvingActionTimeout resolvingActionTimeout = new ResolvingActionTimeout(safetyCenterIssueActionId, userProfileGroup); @@ -721,7 +738,8 @@ public final class SafetyCenterService extends SystemService { SafetyCenterService.this.dumpLocked(fd, fout); SafetyCenterFlags.dump(fout); mSafetyCenterConfigReader.dump(fout); - mSafetyCenterDataTracker.dump(fout); + mSafetyCenterRepository.dump(fout); + mSafetyCenterIssueCache.dump(fout); mSafetyCenterRefreshTracker.dump(fout); mSafetyCenterTimeouts.dump(fout); mSafetyCenterListeners.dump(fout); @@ -870,7 +888,7 @@ public final class SafetyCenterService extends SystemService { SafetyCenterFlags.getShowErrorEntriesOnTimeout(); if (showErrorEntriesOnTimeout) { for (int i = 0; i < stillInFlight.size(); i++) { - mSafetyCenterDataTracker.setSafetySourceError(stillInFlight.valueAt(i)); + mSafetyCenterRepository.setSafetySourceError(stillInFlight.valueAt(i)); } } mSafetyCenterListeners.deliverUpdateForUserProfileGroup( @@ -918,7 +936,7 @@ public final class SafetyCenterService extends SystemService { synchronized (mApiLock) { mSafetyCenterTimeouts.remove(this); boolean safetyCenterDataHasChanged = - mSafetyCenterDataTracker.unmarkSafetyCenterIssueActionInFlight( + mSafetyCenterRepository.unmarkSafetyCenterIssueActionInFlight( mSafetyCenterIssueActionId, SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT); if (!safetyCenterDataHasChanged) { @@ -1006,7 +1024,7 @@ public final class SafetyCenterService extends SystemService { UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId); synchronized (mApiLock) { if (clearDataPermanently) { - mSafetyCenterDataTracker.clearForUser(userId); + mSafetyCenterRepository.clearForUser(userId); } mSafetyCenterListeners.clearForUser(userId); mSafetyCenterRefreshTracker.clearRefreshForUser(userId); @@ -1019,7 +1037,7 @@ public final class SafetyCenterService extends SystemService { @RefreshReason int refreshReason, @UserIdInt int userId) { UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId); synchronized (mApiLock) { - mSafetyCenterDataTracker.clearSafetySourceErrors(userProfileGroup); + mSafetyCenterRepository.clearSafetySourceErrors(userProfileGroup); String refreshBroadcastId = mSafetyCenterBroadcastDispatcher.sendRefreshSafetySources( @@ -1040,7 +1058,7 @@ public final class SafetyCenterService extends SystemService { /** Schedule writing the cache to file. */ @GuardedBy("mApiLock") private void scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked() { - if (!mSafetyCenterDataTracker.isSafetyCenterIssueCacheDirty()) { + if (!mSafetyCenterIssueCache.isDirty()) { return; } if (!mSafetyCenterIssueCacheWriteScheduled) { @@ -1056,7 +1074,7 @@ public final class SafetyCenterService extends SystemService { synchronized (mApiLock) { mSafetyCenterIssueCacheWriteScheduled = false; - persistedSafetyCenterIssues = mSafetyCenterDataTracker.snapshotSafetyCenterIssueCache(); + persistedSafetyCenterIssues = mSafetyCenterIssueCache.snapshot(); // Since all write operations are scheduled in the same background thread, we can safely // release the lock after creating a snapshot and know that all snapshots will be // written in the correct order even if we are not holding the lock. @@ -1078,7 +1096,7 @@ public final class SafetyCenterService extends SystemService { Log.e(TAG, "Cannot read Safety Center persisted issues", e); } - mSafetyCenterDataTracker.loadSafetyCenterIssueCache(persistedSafetyCenterIssues); + mSafetyCenterIssueCache.load(persistedSafetyCenterIssues); scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked(); } @@ -1092,7 +1110,7 @@ public final class SafetyCenterService extends SystemService { @GuardedBy("mApiLock") private void clearDataLocked() { - mSafetyCenterDataTracker.clear(); + mSafetyCenterRepository.clear(); mSafetyCenterTimeouts.clear(); mSafetyCenterRefreshTracker.clearRefresh(); scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked(); |