diff options
author | 2022-02-21 09:32:10 +0000 | |
---|---|---|
committer | 2022-03-03 20:10:13 +0000 | |
commit | eb1ae9d4b72f214b58ddc31f333405e366d7f804 (patch) | |
tree | 6ed0eca7e0692576173c9c830754299789591868 | |
parent | 722815a7f37c04facc89908827affd782d0fdbca (diff) |
[SafetyCenter] Restructure SafetyCenterManager APIs in response to API Council feedback and Refactor test API to override SafetyCenterConfig entirely
* Rename sendSafetyCenterUpdate -> setSafetySourceData
* Rename getLastSafetyCenterUpdate -> getSafetySourceData
* Rename clearSafetyCenterData -> clearAllSafetySourceData
* Separate out safety source id from SafetySourceData, so it is
clear what is being "set" in setSafetySourceData and SafetySourceData
is now simply a data object that can belong to any source.
* Add an event object while setting data, so Safety Center knows why
data was set (this was touched upon in API council feedback meeting,
in particular with receiving data as a result of refresh broadcasts).
* Other APIs have javadoc changes and order rearrangement (to group
source and ui APIs) only, no logic changes.
Test: atest CtsSafetyCenterTestCases
Bug: 219195246
Bug: 219194447
Bug: 219078602
Bug: 218852160
Bug: 219194453
Bug: 217944317
Change-Id: Icdc839c7a35f0c10013a3b4af37db088995c0a40
Merged-In: Icdc839c7a35f0c10013a3b4af37db088995c0a40
10 files changed, 605 insertions, 331 deletions
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt index c150e1b29..7f301c1a4 100644 --- a/framework-s/api/system-current.txt +++ b/framework-s/api/system-current.txt @@ -200,22 +200,23 @@ package android.safetycenter { } public final class SafetyCenterManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void addAdditionalSafetySource(@NonNull String, @NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void addOnSafetyCenterDataChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener); - method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void clearAdditionalSafetySources(); - method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void clearSafetyCenterData(); + method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void clearAllSafetySourceData(); + method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void clearSafetyCenterConfigOverride(); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void dismissSafetyIssue(@NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void executeAction(@NonNull String, @NonNull String); - method @Nullable @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public android.safetycenter.SafetySourceData getLastSafetyCenterUpdate(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public android.safetycenter.SafetyCenterData getSafetyCenterData(); + method @Nullable @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public android.safetycenter.SafetySourceData getSafetySourceData(@NonNull String); method @RequiresPermission(anyOf={android.Manifest.permission.READ_SAFETY_CENTER_STATUS, android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE}) public boolean isSafetyCenterEnabled(); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void refreshSafetySources(int); method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void removeOnSafetyCenterDataChangedListener(@NonNull android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener); method @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public void reportSafetySourceError(@NonNull String, @NonNull android.safetycenter.SafetySourceError); - method @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public void sendSafetyCenterUpdate(@NonNull android.safetycenter.SafetySourceData); + method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void setSafetyCenterConfigOverride(@NonNull android.safetycenter.config.SafetyCenterConfig); + method @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public void setSafetySourceData(@NonNull String, @Nullable android.safetycenter.SafetySourceData, @NonNull android.safetycenter.SafetyEvent); field public static final String ACTION_REFRESH_SAFETY_SOURCES = "android.safetycenter.action.REFRESH_SAFETY_SOURCES"; field public static final int EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA = 0; // 0x0 field public static final int EXTRA_REFRESH_REQUEST_TYPE_GET_DATA = 1; // 0x1 + field public static final String EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID"; field public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE"; field public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS = "android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS"; field public static final int REFRESH_REASON_PAGE_OPEN = 100; // 0x64 @@ -273,9 +274,32 @@ package android.safetycenter { method @NonNull public android.safetycenter.SafetyCenterStatus.Builder setTitle(@NonNull CharSequence); } + public final class SafetyEvent implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getRefreshBroadcastId(); + method public int getSafetyEventType(); + method @Nullable public String getSafetySourceIssueActionId(); + method @Nullable public String getSafetySourceIssueId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetyEvent> CREATOR; + field public static final int SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED = 500; // 0x1f4 + field public static final int SAFETY_EVENT_TYPE_DEVICE_REBOOTED = 500; // 0x1f4 + field public static final int SAFETY_EVENT_TYPE_REFRESH_REQUESTED = 200; // 0xc8 + field public static final int SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED = 400; // 0x190 + field public static final int SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED = 300; // 0x12c + field public static final int SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED = 100; // 0x64 + } + + public static final class SafetyEvent.Builder { + ctor public SafetyEvent.Builder(int); + method @NonNull public android.safetycenter.SafetyEvent build(); + method @NonNull public android.safetycenter.SafetyEvent.Builder setRefreshBroadcastId(@Nullable String); + method @NonNull public android.safetycenter.SafetyEvent.Builder setSafetySourceIssueActionId(@Nullable String); + method @NonNull public android.safetycenter.SafetyEvent.Builder setSafetySourceIssueId(@Nullable String); + } + public final class SafetySourceData implements android.os.Parcelable { method public int describeContents(); - method @NonNull public String getId(); method @NonNull public java.util.List<android.safetycenter.SafetySourceIssue> getIssues(); method @Nullable public android.safetycenter.SafetySourceStatus getStatus(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -283,7 +307,7 @@ package android.safetycenter { } public static final class SafetySourceData.Builder { - ctor public SafetySourceData.Builder(@NonNull String); + ctor public SafetySourceData.Builder(); method @NonNull public android.safetycenter.SafetySourceData.Builder addIssue(@NonNull android.safetycenter.SafetySourceIssue); method @NonNull public android.safetycenter.SafetySourceData build(); method @NonNull public android.safetycenter.SafetySourceData.Builder clearIssues(); @@ -291,21 +315,11 @@ package android.safetycenter { } public final class SafetySourceError implements android.os.Parcelable { + ctor public SafetySourceError(@NonNull android.safetycenter.SafetyEvent); method public int describeContents(); - method @Nullable public String getActionId(); - method @Nullable public String getIssueId(); - method public int getType(); + method @NonNull public android.safetycenter.SafetyEvent getSafetyEvent(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetySourceError> CREATOR; - field public static final int SOURCE_ERROR_TYPE_ACTION_ERROR = 10; // 0xa - field public static final int SOURCE_ERROR_TYPE_UNKNOWN = 0; // 0x0 - } - - public static final class SafetySourceError.Builder { - ctor public SafetySourceError.Builder(int); - method @NonNull public android.safetycenter.SafetySourceError build(); - method @NonNull public android.safetycenter.SafetySourceError.Builder setActionId(@Nullable String); - method @NonNull public android.safetycenter.SafetySourceError.Builder setIssueId(@Nullable String); } public final class SafetySourceIssue implements android.os.Parcelable { diff --git a/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl b/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl index 7ab6cf20c..6c67c483b 100644 --- a/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl +++ b/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl @@ -19,8 +19,10 @@ package android.safetycenter; import android.safetycenter.IOnSafetyCenterDataChangedListener; import android.safetycenter.SafetyCenterData; import android.safetycenter.SafetyCenterError; +import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; import android.safetycenter.SafetySourceError; +import android.safetycenter.config.SafetyCenterConfig; /** * AIDL service for the safety center. @@ -38,28 +40,23 @@ interface ISafetyCenterManager { */ boolean isSafetyCenterEnabled(); - /** - * Called by a safety source to send a SafetySourceData update to the safety center. + /** + * Sets the latest SafetySourceData for the given safetySourceId and user to be displayed in + * SafetyCenter UI. */ - void sendSafetyCenterUpdate( + void setSafetySourceData( + String sourceId, in SafetySourceData safetySourceData, + in SafetyEvent safetyEvent, String packageName, int userId); - /** - * Returns the last SafetySourceData update received by the safety center for the given safety - * source id. - */ - SafetySourceData getLastSafetyCenterUpdate( + /** Returns the latest SafetySourceData set for the given safetySourceId and user. */ + SafetySourceData getSafetySourceData( String safetySourceId, String packageName, int userId); - /** - * Requests safety sources to send a SafetySourceData update to Safety Center. - */ - void refreshSafetySources(int refreshReason, int userId); - /** * Notifies the SafetyCenter of an error related to a given safety source. * @@ -71,24 +68,8 @@ interface ISafetyCenterManager { String packageName, int userId); - /** - * Add a safety source dynamically to be used in addition to the sources in the Safety Center - * xml configuration. - * - * <p>Note: This API serves to facilitate CTS testing and should not be used for other purposes. - */ - void addAdditionalSafetySource( - String sourceId, - String packageName, - String broadcastReceiverName); - - /** - * Clears additional safety sources added dynamically to be used in addition to the sources in - * the Safety Center xml configuration. - * - * <p>Note: This API serves to facilitate CTS testing and should not be used for other purposes. - */ - void clearAdditionalSafetySources(); + /** Requests safety sources to send their latest SafetySourceData to Safety Center. */ + void refreshSafetySources(int refreshReason, int userId); /** * Returns the current SafetyCenterData, assembled from the SafetySourceData from all sources. @@ -103,17 +84,40 @@ interface ISafetyCenterManager { IOnSafetyCenterDataChangedListener listener, int userId); + /** + * Dismisses the issue corresponding to the given issue ID. + */ + void dismissSafetyIssue(String issueId, int userId); + /** Executes the specified action on the specified issue. */ void executeAction(String safetyCenterIssueId, String safetyCenterActionId, int userId); /** - * Dismisses the issue corresponding to the given issue ID. + * Clears all SafetySourceData set by safety sources using setSafetySourceData. + * + * <p>Note: This API serves to facilitate CTS testing and should not be used for other purposes. */ - void dismissSafetyIssue(String issueId, int userId); + void clearAllSafetySourceData(); + + /** + * Sets an override of the SafetyCenterConfig set through XML. + * + * When set, the override SafetyCenterConfig will be used instead of the + * SafetyCenterConfig parsed from the XML file to read configured safety sources. + * + * <p>Note: This API serves to facilitate CTS testing and should not be used to configure safety + * sources dynamically for production. Once used for testing, the override should be cleared. + * + * See clearSafetyCenterConfigOverride. + */ + void setSafetyCenterConfigOverride(in SafetyCenterConfig safetyCenterConfig); /** - * Clears all SafetySourceData updates sent to the safety center using sendSafetyCenterUpdate, - * for all packages and users. + * Clears the override of the SafetyCenterConfig set through XML. + * + * <p>Note: This API serves to facilitate CTS testing and should not be used for other purposes. + * + * See setSafetyCenterConfigOverride(SafetyCenterConfig). */ - void clearSafetyCenterData(); + void clearSafetyCenterConfigOverride(); }
\ No newline at end of file diff --git a/framework-s/java/android/safetycenter/SafetyCenterManager.java b/framework-s/java/android/safetycenter/SafetyCenterManager.java index 8ba453f38..3795d485e 100644 --- a/framework-s/java/android/safetycenter/SafetyCenterManager.java +++ b/framework-s/java/android/safetycenter/SafetyCenterManager.java @@ -34,6 +34,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; +import android.safetycenter.config.SafetyCenterConfig; import android.util.ArrayMap; import androidx.annotation.RequiresApi; @@ -70,7 +71,7 @@ public final class SafetyCenterManager { * * <p>On receiving this broadcast, safety sources should determine their safety state according * to the parameters specified in the intent extras (see below) and send Safety Center data - * about their safety state using {@link #sendSafetyCenterUpdate(SafetySourceData)}. + * about their safety state using {@link #setSafetySourceData}. * * <p class="note">This is a protected intent that can only be sent by the system. * @@ -115,6 +116,14 @@ public final class SafetyCenterManager { "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE"; /** + * Used as an {@code String} extra field in {@link #ACTION_REFRESH_SAFETY_SOURCES} intents to + * specify a string identifier for the broadcast. + * + */ + public static final String EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID = + "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID"; + + /** * All possible types of data refresh requests in broadcasts with intent action * {@link #ACTION_REFRESH_SAFETY_SOURCES}. * @@ -210,20 +219,47 @@ public final class SafetyCenterManager { } /** - * Sends a {@link SafetySourceData} update to the safety center. + * Returns whether the SafetyCenter page is enabled. + */ + @RequiresPermission(anyOf = { + READ_SAFETY_CENTER_STATUS, + SEND_SAFETY_CENTER_UPDATE + }) + public boolean isSafetyCenterEnabled() { + try { + return mService.isSafetyCenterEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set the latest {@link SafetySourceData} for a safety source, to be displayed in + * SafetyCenter UI. * - * <p>Each {@link SafetySourceData#getId()} uniquely identifies the {@link SafetySourceData} for - * the current package and user. + * <p>Each {@code safetySourceId} uniquely identifies the {@link SafetySourceData} for the + * calling user. * - * <p>This call will override any existing {@link SafetySourceData} already present for the - * given {@link SafetySourceData#getId()} for the current package and user. + * <p>This call will rewrite any existing {@link SafetySourceData} already set for the given + * {@code safetySourceId} for the calling user. + * + * @param safetySourceId the unique identifier for a safety source in the calling user + * @param safetySourceData the latest safety data for the safety source in the calling user. If + * a safety source does not have any data to set, it can set its + * {@link SafetySourceData} to {@code null}, in which case Safety Center + * will fall back to any placeholder data specified in the safety source + * xml configuration. + * @param safetyEvent the event that triggered the safety source to set safety data */ @RequiresPermission(SEND_SAFETY_CENTER_UPDATE) - public void sendSafetyCenterUpdate(@NonNull SafetySourceData safetySourceData) { - requireNonNull(safetySourceData, "safetySourceData cannot be null"); + public void setSafetySourceData(@NonNull String safetySourceId, + @Nullable SafetySourceData safetySourceData, + @NonNull SafetyEvent safetyEvent) { try { - mService.sendSafetyCenterUpdate( + mService.setSafetySourceData( + safetySourceId, safetySourceData, + safetyEvent, mContext.getPackageName(), mContext.getUser().getIdentifier()); } catch (RemoteException e) { @@ -232,19 +268,18 @@ public final class SafetyCenterManager { } /** - * Returns the last {@link SafetySourceData} update received through {@link - * #sendSafetyCenterUpdate(SafetySourceData)} for the given - * {@code safetySourceId}, package and user. + * Returns the latest {@link SafetySourceData} set through {@link #setSafetySourceData} + * for the given {@code safetySourceId} and calling user. * - * <p>Returns {@code null} if there never was any update for the given {@code safetySourceId}, - * package and user. + * <p>Returns {@code null} if there never was any data sent for the given {@code safetySourceId} + * and user. */ @RequiresPermission(SEND_SAFETY_CENTER_UPDATE) @Nullable - public SafetySourceData getLastSafetyCenterUpdate(@NonNull String safetySourceId) { + public SafetySourceData getSafetySourceData(@NonNull String safetySourceId) { requireNonNull(safetySourceId, "safetySourceId cannot be null"); try { - return mService.getLastSafetyCenterUpdate( + return mService.getSafetySourceData( safetySourceId, mContext.getPackageName(), mContext.getUser().getIdentifier()); @@ -277,22 +312,7 @@ public final class SafetyCenterManager { } /** - * Returns whether the SafetyCenter page is enabled. - */ - @RequiresPermission(anyOf = { - READ_SAFETY_CENTER_STATUS, - SEND_SAFETY_CENTER_UPDATE - }) - public boolean isSafetyCenterEnabled() { - try { - return mService.isSafetyCenterEnabled(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Requests safety sources to send a {@link SafetySourceData} update to Safety Center. + * Requests safety sources to send their latest {@link SafetySourceData} to Safety Center. * * <p>This API sends a broadcast to all safety sources with action * {@link #ACTION_REFRESH_SAFETY_SOURCES}. @@ -391,19 +411,6 @@ public final class SafetyCenterManager { } /** - * Clears all {@link SafetySourceData} updates sent to the safety center using {@link - * #sendSafetyCenterUpdate(SafetySourceData)}, for all packages and users. - */ - @RequiresPermission(MANAGE_SAFETY_CENTER) - public void clearSafetyCenterData() { - try { - mService.clearSafetyCenterData(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Executes the specified action on the specified issue. * * @param safetyCenterIssueId the target issue ID returned by {@link SafetyCenterIssue#getId()} @@ -425,35 +432,50 @@ public final class SafetyCenterManager { } /** - * Add a safety source dynamically to be used in addition to the sources in the Safety Center - * xml configuration. + * Clears all {@link SafetySourceData} set by safety sources using {@link #setSafetySourceData}. * * <p>Note: This API serves to facilitate CTS testing and should not be used for other purposes. */ - // TODO(b/217944317): Modify the parameters to be a SafetySource or SafetyCenterConfig once - // these classes are Parcelable and part of the API surface. @RequiresPermission(MANAGE_SAFETY_CENTER) - public void addAdditionalSafetySource(@NonNull String sourceId, @NonNull String packageName, - @NonNull String broadcastReceiverName) { + public void clearAllSafetySourceData() { + try { + mService.clearAllSafetySourceData(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets an override of the {@link SafetyCenterConfig} set through XML. + * + * When set, the override {@link SafetyCenterConfig} will be used instead of the + * {@link SafetyCenterConfig} parsed from the XML file to read configured safety sources. + * + * <p>Note: This API serves to facilitate CTS testing and should not be used to configure safety + * sources dynamically for production. Once used for testing, the override should be cleared. + * + * @see #clearSafetyCenterConfigOverride() + */ + @RequiresPermission(MANAGE_SAFETY_CENTER) + public void setSafetyCenterConfigOverride(@NonNull SafetyCenterConfig safetyCenterConfig) { try { - mService.addAdditionalSafetySource(sourceId, packageName, broadcastReceiverName); + mService.setSafetyCenterConfigOverride(safetyCenterConfig); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Clears additional safety sources added dynamically to be used in addition to the sources in - * the Safety Center xml configuration. + * Clears the override of the {@link SafetyCenterConfig} set through XML. * * <p>Note: This API serves to facilitate CTS testing and should not be used for other purposes. + * + * @see #setSafetyCenterConfigOverride(SafetyCenterConfig) */ - // TODO(b/217944317): Modify the parameters to be a SafetySource or SafetyCenterConfig once - // these classes are Parcelable and part of the API surface. @RequiresPermission(MANAGE_SAFETY_CENTER) - public void clearAdditionalSafetySources() { + public void clearSafetyCenterConfigOverride() { try { - mService.clearAdditionalSafetySources(); + mService.clearSafetyCenterConfigOverride(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/framework-s/java/android/safetycenter/SafetyEvent.aidl b/framework-s/java/android/safetycenter/SafetyEvent.aidl new file mode 100644 index 000000000..d54115a48 --- /dev/null +++ b/framework-s/java/android/safetycenter/SafetyEvent.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 android.safetycenter; + +/** + * Parcelable AIDL SafetyEvent. + * + * @hide + */ +parcelable SafetyEvent;
\ No newline at end of file diff --git a/framework-s/java/android/safetycenter/SafetyEvent.java b/framework-s/java/android/safetycenter/SafetyEvent.java new file mode 100644 index 000000000..135c002e6 --- /dev/null +++ b/framework-s/java/android/safetycenter/SafetyEvent.java @@ -0,0 +1,297 @@ +/* + * 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 android.safetycenter; + +import static android.os.Build.VERSION_CODES.TIRAMISU; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.RequiresApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * A safety event that may trigger a safety source to set its {@link SafetySourceData}. + * + * @hide + */ +@SystemApi +@RequiresApi(TIRAMISU) +public final class SafetyEvent implements Parcelable { + /** + * Types of safety events that may trigger a set of a safety source's {@link SafetySourceData}. + * + * @hide + */ + @IntDef(prefix = {"SAFETY_EVENT_TYPE_"}, value = { + SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED, + SAFETY_EVENT_TYPE_REFRESH_REQUESTED, + SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED, + SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED, + SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED, + SAFETY_EVENT_TYPE_DEVICE_REBOOTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SafetyEventType { + } + /** + * Indicates that there has been a change of state for safety source, which may be independent + * of Safety Center interactions. + */ + public static final int SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED = 100; + + /** + * Indicates that the safety source performed a data refresh in response to a request from + * Safety Center. + */ + public static final int SAFETY_EVENT_TYPE_REFRESH_REQUESTED = 200; + + /** + * Indicates that the safety source successfully completed a resolving + * {@link SafetySourceIssue.Action}. + */ + public static final int SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED = 300; + + /** + * Indicates that the safety source failed to complete a resolving + * {@link SafetySourceIssue.Action}. + */ + public static final int SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED = 400; + + /** + * Indicates that the device's locale changed. + */ + public static final int SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED = 500; + + /** + * Indicates that the device was rebooted. + */ + public static final int SAFETY_EVENT_TYPE_DEVICE_REBOOTED = 500; + + @NonNull + public static final Creator<SafetyEvent> CREATOR = + new Creator<SafetyEvent>() { + @Override + public SafetyEvent createFromParcel(Parcel in) { + return new SafetyEvent(in.readInt(), in.readString(), + in.readString(), in.readString()); + } + + @Override + public SafetyEvent[] newArray(int size) { + return new SafetyEvent[size]; + } + }; + + @SafetyEventType + private final int mSafetyEventType; + @Nullable + private final String mRefreshBroadcastId; + @Nullable + private final String mSafetySourceIssueId; + @Nullable + private final String mSafetySourceIssueActionId; + + private SafetyEvent(@SafetyEventType int safetyEvent, + @Nullable String refreshBroadcastId, + @Nullable String safetySourceIssueId, + @Nullable String safetySourceIssueActionId) { + mSafetyEventType = safetyEvent; + mRefreshBroadcastId = refreshBroadcastId; + mSafetySourceIssueId = safetySourceIssueId; + mSafetySourceIssueActionId = safetySourceIssueActionId; + } + + /** Returns the type of the safety event. */ + @SafetyEventType + public int getSafetyEventType() { + return mSafetyEventType; + } + + /** + * Returns an optional broadcast id provided by Safety Center when requesting a refresh, through + * {@link SafetyCenterManager#EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID}. + * + * <p>This will only be relevant for events of type + * {@link #SAFETY_EVENT_TYPE_REFRESH_REQUESTED}. + * + * @see #getSafetyEventType() + */ + @Nullable + public String getRefreshBroadcastId() { + return mRefreshBroadcastId; + } + + /** + * Returns the id of the {@link SafetySourceIssue} this event is associated with (if any). + * + * <p>This will only be relevant for events of type + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. + * + * @see #getSafetyEventType() + * @see SafetySourceIssue#getId() + */ + @Nullable + public String getSafetySourceIssueId() { + return mSafetySourceIssueId; + } + + /** + * Returns the id of the {@link SafetySourceIssue.Action} this event is associated with (if + * any). + * + * <p>This will only be relevant for events of type + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. + * + * @see #getSafetyEventType() + * @see SafetySourceIssue.Action#getId() + */ + @Nullable + public String getSafetySourceIssueActionId() { + return mSafetySourceIssueActionId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSafetyEventType); + dest.writeString(mRefreshBroadcastId); + dest.writeString(mSafetySourceIssueId); + dest.writeString(mSafetySourceIssueActionId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SafetyEvent)) return false; + SafetyEvent that = (SafetyEvent) o; + return mSafetyEventType == that.mSafetyEventType + && Objects.equals(mRefreshBroadcastId, that.mRefreshBroadcastId) + && Objects.equals(mSafetySourceIssueId, that.mSafetySourceIssueId) + && Objects.equals(mSafetySourceIssueActionId, that.mSafetySourceIssueActionId); + } + + @Override + public int hashCode() { + return Objects.hash(mSafetyEventType, mRefreshBroadcastId, mSafetySourceIssueId, + mSafetySourceIssueActionId); + } + + @Override + public String toString() { + return "SafetySourceDataRewriteReason{" + + "mSafetyEventType=" + + mSafetyEventType + + ", mRefreshBroadcastId='" + + mRefreshBroadcastId + + '\'' + + ", mSafetySourceIssueId='" + + mSafetySourceIssueId + + '\'' + + ", mSafetySourceIssueActionId='" + + mSafetySourceIssueActionId + + '\'' + + '}'; + } + + /** Builder class for {@link SafetyEvent}. */ + public static final class Builder { + @SafetyEventType + private final int mSafetyEventType; + @Nullable + private String mRefreshBroadcastId; + @Nullable + private String mSafetySourceIssueId; + @Nullable + private String mSafetySourceIssueActionId; + + /** Creates a {@link Builder} for {@link SafetyEvent}. */ + public Builder(@SafetyEventType int safetyEventType) { + mSafetyEventType = safetyEventType; + } + + /** + * Sets an optional broadcast id provided by Safety Center when requesting a refresh, + * through {@link SafetyCenterManager#EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID}. + * + * <p>This will only be relevant for events of type + * {@link #SAFETY_EVENT_TYPE_REFRESH_REQUESTED}. + * + * @see #getSafetyEventType() + */ + @NonNull + public Builder setRefreshBroadcastId(@Nullable String refreshBroadcastId) { + mRefreshBroadcastId = refreshBroadcastId; + return this; + } + + /** + * Sets the id of the {@link SafetySourceIssue} this event is associated with (if any). + * + * <p>This will only be relevant for events of type + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. + * + * @see #getSafetyEventType() + * @see SafetySourceIssue#getId() + */ + @NonNull + public Builder setSafetySourceIssueId(@Nullable String safetySourceIssueId) { + mSafetySourceIssueId = safetySourceIssueId; + return this; + } + + /** + * Sets the id of the {@link SafetySourceIssue.Action} this event is associated with (if + * any). + * + * <p>This will only be relevant for events of type + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or + * {@link #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. + * + * @see #getSafetyEventType() + * @see SafetySourceIssue.Action#getId() + */ + @NonNull + public Builder setSafetySourceIssueActionId(@Nullable String safetySourceIssueActionId) { + mSafetySourceIssueActionId = safetySourceIssueActionId; + return this; + } + + /** + * Creates the {@link SafetyEvent} represented by this {@link Builder}. + */ + @NonNull + public SafetyEvent build() { + return new SafetyEvent(mSafetyEventType, mRefreshBroadcastId, mSafetySourceIssueId, + mSafetySourceIssueActionId); + } + } +} diff --git a/framework-s/java/android/safetycenter/SafetySourceData.java b/framework-s/java/android/safetycenter/SafetySourceData.java index cee62af06..6e4fe11c5 100644 --- a/framework-s/java/android/safetycenter/SafetySourceData.java +++ b/framework-s/java/android/safetycenter/SafetySourceData.java @@ -48,13 +48,12 @@ public final class SafetySourceData implements Parcelable { new Parcelable.Creator<SafetySourceData>() { @Override public SafetySourceData createFromParcel(Parcel in) { - String id = requireNonNull(in.readString()); SafetySourceStatus status = in.readParcelable(SafetySourceStatus.class.getClassLoader(), SafetySourceStatus.class); List<SafetySourceIssue> issues = new ArrayList<>(); in.readParcelableList(issues, SafetySourceIssue.class.getClassLoader()); - return new SafetySourceData(id, status, issues); + return new SafetySourceData(status, issues); } @Override @@ -63,31 +62,17 @@ public final class SafetySourceData implements Parcelable { } }; - @NonNull - private final String mId; @Nullable private final SafetySourceStatus mStatus; @NonNull private final List<SafetySourceIssue> mIssues; - private SafetySourceData(@NonNull String id, @Nullable SafetySourceStatus status, + private SafetySourceData(@Nullable SafetySourceStatus status, @NonNull List<SafetySourceIssue> issues) { - this.mId = id; this.mStatus = status; this.mIssues = new ArrayList<>(issues); } - /** - * Returns the id of the associated safety source. - * - * <p>The id uniquely identifies a safety source within the scope of the application that is - * creating the source. - */ - @NonNull - public String getId() { - return mId; - } - /** Returns the data for the safety source status to be shown in UI. */ @Nullable public SafetySourceStatus getStatus() { @@ -107,7 +92,6 @@ public final class SafetySourceData implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(mId); dest.writeParcelable(mStatus, flags); dest.writeParcelableList(mIssues, flags); } @@ -117,21 +101,18 @@ public final class SafetySourceData implements Parcelable { if (this == o) return true; if (!(o instanceof SafetySourceData)) return false; SafetySourceData that = (SafetySourceData) o; - return mId.equals(that.mId) && Objects.equals(mStatus, that.mStatus) + return Objects.equals(mStatus, that.mStatus) && mIssues.equals(that.mIssues); } @Override public int hashCode() { - return Objects.hash(mId, mStatus, mIssues); + return Objects.hash(mStatus, mIssues); } @Override public String toString() { return "SafetySourceData{" - + "mId='" - + mId - + '\'' + ", mStatus=" + mStatus + ", mIssues=" @@ -142,22 +123,10 @@ public final class SafetySourceData implements Parcelable { /** Builder class for {@link SafetySourceData}. */ public static final class Builder { @NonNull - private final String mId; - @NonNull private final List<SafetySourceIssue> mIssues = new ArrayList<>(); @Nullable private SafetySourceStatus mStatus; - /** - * Creates a {@link Builder} for a {@link SafetySourceData}. - * - * @param id uniquely identifies the associated safety source, scoped within the application - * that is creating the associated safety source. - */ - public Builder(@NonNull String id) { - this.mId = requireNonNull(id); - } - /** Sets data for the safety source status to be shown in UI. */ @NonNull public Builder setStatus(@Nullable SafetySourceStatus status) { @@ -186,7 +155,7 @@ public final class SafetySourceData implements Parcelable { public SafetySourceData build() { // TODO(b/207329841): Validate data matches validation in S, for eg that the status // and severity levels of the settings and issues are compatible. - return new SafetySourceData(mId, mStatus, mIssues); + return new SafetySourceData(mStatus, mIssues); } } } diff --git a/framework-s/java/android/safetycenter/SafetySourceError.java b/framework-s/java/android/safetycenter/SafetySourceError.java index 4b07495c3..afc33eaff 100644 --- a/framework-s/java/android/safetycenter/SafetySourceError.java +++ b/framework-s/java/android/safetycenter/SafetySourceError.java @@ -18,17 +18,13 @@ package android.safetycenter; import static android.os.Build.VERSION_CODES.TIRAMISU; -import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.RequiresApi; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -39,87 +35,38 @@ import java.util.Objects; @SystemApi @RequiresApi(TIRAMISU) public final class SafetySourceError implements Parcelable { + @NonNull + private final SafetyEvent mSafetyEvent; - /** - * All possible types for a {@link SafetySourceError}. - * - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "SOURCE_ERROR_TYPE_", value = { - SOURCE_ERROR_TYPE_UNKNOWN, - SOURCE_ERROR_TYPE_ACTION_ERROR, - }) - public @interface SourceErrorType {} - - /** Indicates this error is of an unknown type. */ - public static final int SOURCE_ERROR_TYPE_UNKNOWN = 0; - - /** Indicates this error reports a problem while executing an action on an issue. */ - public static final int SOURCE_ERROR_TYPE_ACTION_ERROR = 10; - - @SourceErrorType - private final int mType; - @Nullable - private final String mIssueId; - @Nullable - private final String mActionId; - - private SafetySourceError( - @SourceErrorType int type, @Nullable String issueId, @Nullable String actionId) { - mType = type; - mIssueId = issueId; - mActionId = actionId; + public SafetySourceError(@NonNull SafetyEvent safetyEvent) { + mSafetyEvent = safetyEvent; } - /** Returns the {@link SourceErrorType} of this error. */ - @SourceErrorType - public int getType() { - return mType; + /** Returns the safety event associated with this error. */ + @NonNull + public SafetyEvent getSafetyEvent() { + return mSafetyEvent; } - /** - * Returns the id of the {@link SafetySourceIssue} this error is associated with (if any). - * - * @see SafetySourceIssue#getId() - */ - @Nullable - public String getIssueId() { - return mIssueId; - } - - /** - * Returns the id of the {@link SafetySourceIssue.Action} this error is associated with (if - * any). - * - * @see SafetySourceIssue.Action#getId() - */ - @Nullable - public String getActionId() { - return mActionId; - } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SafetySourceError that = (SafetySourceError) o; - return mType == that.mType - && Objects.equals(mIssueId, that.mIssueId) - && Objects.equals(mActionId, that.mActionId); + return mSafetyEvent.equals(that.mSafetyEvent); } @Override public int hashCode() { - return Objects.hash(mType, mIssueId, mActionId); + return Objects.hash(mSafetyEvent); } @Override public String toString() { return "SafetySourceError{" - + "mType=" + mType - + ", mIssueId='" + mIssueId + '\'' - + ", mActionId='" + mActionId + '\'' + + "mSafetyEvent=" + + mSafetyEvent + '}'; } @@ -130,19 +77,15 @@ public final class SafetySourceError implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mType); - dest.writeString(mIssueId); - dest.writeString(mActionId); + dest.writeParcelable(mSafetyEvent, flags); } @NonNull public static final Creator<SafetySourceError> CREATOR = new Creator<SafetySourceError>() { @Override public SafetySourceError createFromParcel(Parcel in) { - return new SafetySourceError( - in.readInt(), - in.readString(), - in.readString()); + return new SafetySourceError(in.readParcelable(SafetyEvent.class.getClassLoader(), + SafetyEvent.class)); } @Override @@ -150,52 +93,4 @@ public final class SafetySourceError implements Parcelable { return new SafetySourceError[0]; } }; - - /** Builder class for a {@link SafetySourceError}. */ - public static final class Builder { - @SourceErrorType - private final int mType; - @Nullable - private String mIssueId; - @Nullable - private String mActionId; - - /** Creates a {@link Builder} for a {@link SafetySourceError}. */ - public Builder(@SourceErrorType int type) { - mType = type; - } - - /** - * Sets the id of the {@link SafetySourceIssue} this error is associated with (if any). - * - * <p>Typically this would only be used for issues containing actions marked {@link - * SafetySourceIssue.Action#isResolving()}, since Safety Center is waiting for a response - * and will return an error if none is given in a certain amount of time. - */ - @NonNull - public Builder setIssueId(@Nullable String issueId) { - mIssueId = issueId; - return this; - } - - /** - * Sets the id of the {@link SafetySourceIssue.Action} this error is associated with (if - * any). - * - * <p>Typically this would only be used for actions marked {@link - * SafetySourceIssue.Action#isResolving()}, since Safety Center is waiting for a response - * and will return an error if none is given in a certain amount of time. - */ - @NonNull - public Builder setActionId(@Nullable String actionId) { - mActionId = actionId; - return this; - } - - /** Creates the {@link SafetySourceError} defined by this {@link Builder}. */ - @NonNull - public SafetySourceError build() { - return new SafetySourceError(mType, mIssueId, mActionId); - } - } } diff --git a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.aidl b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.aidl new file mode 100644 index 000000000..ef2493894 --- /dev/null +++ b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.aidl @@ -0,0 +1,24 @@ +/* + * 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 android.safetycenter.config; + +/** + * Parcelable AIDL SafetyCenterConfig. + * + * @hide + */ +parcelable SafetyCenterConfig;
\ No newline at end of file diff --git a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java index f54cc2b4e..e119ce6bd 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java @@ -53,7 +53,7 @@ import java.util.Map; import java.util.Objects; /** - * A class that keeps track of all the {@link SafetySourceData} updates received by safety center, + * 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 permission controller. * * <p>This class isn't thread safe. Thread safety must be handled by the caller. @@ -82,23 +82,24 @@ final class SafetyCenterDataTracker { } /** - * Adds a {@link SafetySourceData} update for the given {@code packageName} and {@code userId}, - * and returns the updated {@link SafetyCenterData} of the {@code userId}. + * Sets the latest {@link SafetySourceData} for the given {@code safetySourceId} and + * {@code userId}, and returns the updated {@link SafetyCenterData} of the {@code userId}. * * <p>Returns {@code null} if there was no update to the underlying {@link SafetyCenterData}, or * if the {@link SafetyCenterConfig} is not available. */ @Nullable - SafetyCenterData addSafetySourceData( + SafetyCenterData setSafetySourceData( + @NonNull String safetySourceId, @NonNull SafetySourceData safetySourceData, @NonNull String packageName, @UserIdInt int userId) { - if (!configContains(safetySourceData.getId(), packageName)) { + if (!configContains(safetySourceId, packageName)) { // TODO(b/218801292): Should this be hard error for the caller? return null; } - Key key = Key.of(safetySourceData.getId(), packageName, userId); + Key key = Key.of(safetySourceId, packageName, userId); SafetySourceData existingSafetySourceData = mSafetySourceDataForKey.get(key); if (safetySourceData.equals(existingSafetySourceData)) { return null; @@ -109,10 +110,10 @@ final class SafetyCenterDataTracker { } /** - * Returns the latest {@link SafetySourceData} update for the given {@code safetySourceId}, - * {@code packageName} and {@code userId}. + * Returns the latest {@link SafetySourceData} for the given {@code safetySourceId} and + * {@code userId}. * - * <p>Returns {@code null} if there was no update. + * <p>Returns {@code null} if there was no data set. */ @Nullable SafetySourceData getSafetySourceData( @@ -127,14 +128,14 @@ final class SafetyCenterDataTracker { return mSafetySourceDataForKey.get(Key.of(safetySourceId, packageName, userId)); } - /** Clears all the {@link SafetySourceData} updates received so far, for all users. */ + /** Clears all the {@link SafetySourceData} set received so far, for all users. */ void clear() { mSafetySourceDataForKey.clear(); } /** * Returns the current {@link SafetyCenterData} for the given {@code userId}, aggregated from - * all the {@link SafetySourceData} updates received so far. + * all the {@link SafetySourceData} set so far. * * <p>Returns an arbitrary default value if no data has been received for the user so far, or if * the {@link SafetyCenterConfig} is not available. @@ -616,7 +617,7 @@ final class SafetyCenterDataTracker { } /** - * A key for {@link SafetySourceData} updates; based on the {@code safetySourceId}, {@code + * A key for {@link SafetySourceData}; based on the {@code safetySourceId}, {@code * packageName} and {@code userId}. */ // TODO(b/219697341): Look into using AutoValue for this data class. diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java index 1a06b8fb0..98edc8a62 100644 --- a/service/java/com/android/safetycenter/SafetyCenterService.java +++ b/service/java/com/android/safetycenter/SafetyCenterService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS; import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE; import static android.os.Build.VERSION_CODES.TIRAMISU; import static android.safetycenter.SafetyCenterManager.RefreshReason; +import static android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC; import static java.util.Objects.requireNonNull; @@ -38,8 +39,12 @@ import android.provider.DeviceConfig; import android.safetycenter.IOnSafetyCenterDataChangedListener; import android.safetycenter.ISafetyCenterManager; import android.safetycenter.SafetyCenterData; +import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; import android.safetycenter.SafetySourceError; +import android.safetycenter.config.SafetyCenterConfig; +import android.safetycenter.config.SafetySource; +import android.safetycenter.config.SafetySourcesGroup; import androidx.annotation.Keep; import androidx.annotation.RequiresApi; @@ -99,24 +104,50 @@ public final class SafetyCenterService extends SystemService { /** Service implementation of {@link ISafetyCenterManager.Stub}. */ private final class Stub extends ISafetyCenterManager.Stub { @Override - public void sendSafetyCenterUpdate( - @NonNull SafetySourceData safetySourceData, + public boolean isSafetyCenterEnabled() { + enforceAnyCallingOrSelfPermissions("isSafetyCenterEnabled", + READ_SAFETY_CENTER_STATUS, + SEND_SAFETY_CENTER_UPDATE); + // TODO(b/214568975): Decide if we should disable safety center if there is a problem + // reading the config. + + // We don't require the caller to have READ_DEVICE_CONFIG permission. + final long callingId = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_SAFETY_CENTER_ENABLED, + /* defaultValue = */ false) + && getSafetyCenterConfigValue(); + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @Override + public void setSafetySourceData( + @NonNull String safetySourceId, + @Nullable SafetySourceData safetySourceData, + @NonNull SafetyEvent safetyEvent, @NonNull String packageName, @UserIdInt int userId) { mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName); // TODO(b/217235899): Finalize cross-user behavior. PermissionUtils.enforceCrossUserPermission( - userId, false, "sendSafetyCenterUpdate", getContext()); + userId, false, "setSafetySourceData", getContext()); // TODO(b/205706756): Security: check certs? getContext().enforceCallingOrSelfPermission(SEND_SAFETY_CENTER_UPDATE, - "sendSafetyCenterUpdate"); + "setSafetySourceData"); // TODO(b/218812582): Validate the SafetySourceData. SafetyCenterData safetyCenterData; RemoteCallbackList<IOnSafetyCenterDataChangedListener> listeners; synchronized (mApiLock) { - safetyCenterData = mSafetyCenterDataTracker.addSafetySourceData(safetySourceData, - packageName, userId); + safetyCenterData = mSafetyCenterDataTracker.setSafetySourceData( + safetySourceId, + safetySourceData, + packageName, + userId); listeners = mSafetyCenterListeners.getListeners(userId); } // This doesn't need to be done while holding the lock, as RemoteCallbackList already @@ -125,7 +156,7 @@ public final class SafetyCenterService extends SystemService { // doing this while holding the lock could also potentially lead to deadlocks. if (listeners != null && safetyCenterData != null) { // TODO(b/218811189): This should be called on all listeners associated with the - // userId, i.e. if #sendSafetyCenterUpdate is called with a work profile userId, + // userId, i.e. if #setSafetySourceData is called with a work profile userId, // we should also let the personal profile listeners know about the update. SafetyCenterListeners.deliverUpdate(listeners, safetyCenterData); } @@ -133,17 +164,17 @@ public final class SafetyCenterService extends SystemService { @Override @Nullable - public SafetySourceData getLastSafetyCenterUpdate( + public SafetySourceData getSafetySourceData( @NonNull String safetySourceId, @NonNull String packageName, @UserIdInt int userId) { mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName); // TODO(b/217235899): Finalize cross-user behavior. PermissionUtils.enforceCrossUserPermission( - userId, false, "getLastSafetyCenterUpdate", getContext()); + userId, false, "getSafetySourceData", getContext()); // TODO(b/205706756): Security: check certs? getContext().enforceCallingOrSelfPermission( - SEND_SAFETY_CENTER_UPDATE, "getLastSafetyCenterUpdate"); + SEND_SAFETY_CENTER_UPDATE, "getSafetySourceData"); synchronized (mApiLock) { return mSafetyCenterDataTracker.getSafetySourceData(safetySourceId, packageName, @@ -167,27 +198,6 @@ public final class SafetyCenterService extends SystemService { } @Override - public boolean isSafetyCenterEnabled() { - enforceAnyCallingOrSelfPermissions("isSafetyCenterEnabled", - READ_SAFETY_CENTER_STATUS, - SEND_SAFETY_CENTER_UPDATE); - // TODO(b/214568975): Decide if we should disable safety center if there is a problem - // reading the config. - - // We don't require the caller to have READ_DEVICE_CONFIG permission. - final long callingId = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_CENTER_ENABLED, - /* defaultValue = */ false) - && getSafetyCenterConfigValue(); - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - - @Override public void refreshSafetySources( @RefreshReason int refreshReason, @UserIdInt int userId) { @@ -271,16 +281,6 @@ public final class SafetyCenterService extends SystemService { } @Override - public void clearSafetyCenterData() { - getContext().enforceCallingOrSelfPermission( - MANAGE_SAFETY_CENTER, "clearSafetyCenterData"); - - synchronized (mApiLock) { - mSafetyCenterDataTracker.clear(); - } - } - - @Override public void executeAction( @NonNull String safetyCenterIssueId, @NonNull String safetyCenterActionId, @@ -294,23 +294,47 @@ public final class SafetyCenterService extends SystemService { } @Override - public void addAdditionalSafetySource( - @NonNull String sourceId, - @NonNull String packageName, - @NonNull String broadcastReceiverName) { + public void clearAllSafetySourceData() { + getContext().enforceCallingOrSelfPermission( + MANAGE_SAFETY_CENTER, "clearAllSafetySourceData"); + + synchronized (mApiLock) { + mSafetyCenterDataTracker.clear(); + } + } + + @Override + public void setSafetyCenterConfigOverride( + @NonNull SafetyCenterConfig safetyCenterConfig) { getContext().enforceCallingOrSelfPermission(MANAGE_SAFETY_CENTER, - "addAdditionalSafetySource"); + "setSafetyCenterConfigOverride"); synchronized (mRefreshLock) { - mSafetyCenterRefreshManager.addAdditionalSafetySourceBroadcastReceiverComponent( - new ComponentName(packageName, broadcastReceiverName)); + // TODO(b/217944317): Implement properly by overriding config in + // SafetyCenterConfigReader instead. This placeholder impl serves to allow this + // API to be merged in tm-dev, and final impl will be in tm-mainline-prod. + for (int i = 0; i < safetyCenterConfig.getSafetySourcesGroups().size(); i++) { + SafetySourcesGroup group = safetyCenterConfig.getSafetySourcesGroups().get(i); + for (int j = 0; j < group.getSafetySources().size(); j++) { + SafetySource safetySource = group.getSafetySources().get(j); + if (safetySource.getType() != SAFETY_SOURCE_TYPE_STATIC + && safetySource.getBroadcastReceiverClassName() != null) { + mSafetyCenterRefreshManager + .addAdditionalSafetySourceBroadcastReceiverComponent( + new ComponentName( + safetySource.getPackageName(), + safetySource.getBroadcastReceiverClassName() + )); + } + } + } } } @Override - public void clearAdditionalSafetySources() { + public void clearSafetyCenterConfigOverride() { getContext().enforceCallingOrSelfPermission( - MANAGE_SAFETY_CENTER, "clearAdditionalSafetySources"); + MANAGE_SAFETY_CENTER, "clearSafetyCenterConfigOverride"); synchronized (mRefreshLock) { mSafetyCenterRefreshManager |