Prepare to validate incoming parcels using builders
* Change source and config parcels to be parsed using builders
* Change config parcels to use typed lists
Test: atest CtsSafetyCenterTestCases
Bug: 207329841
Bug: 222683944
Change-Id: I51e712141b07698bbbf85cf71e21f9d01c7db944
diff --git a/framework-s/java/android/safetycenter/SafetySourceData.java b/framework-s/java/android/safetycenter/SafetySourceData.java
index 954b279..70c0a81 100644
--- a/framework-s/java/android/safetycenter/SafetySourceData.java
+++ b/framework-s/java/android/safetycenter/SafetySourceData.java
@@ -29,6 +29,7 @@
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -50,7 +51,12 @@
SafetySourceStatus status = in.readTypedObject(SafetySourceStatus.CREATOR);
List<SafetySourceIssue> issues =
in.createTypedArrayList(SafetySourceIssue.CREATOR);
- return new SafetySourceData(status, issues);
+ Builder builder = new Builder().setStatus(status);
+ // TODO(b/224513050): Consider simplifying by adding a new API to the builder.
+ for (int i = 0; i < issues.size(); i++) {
+ builder.addIssue(issues.get(i));
+ }
+ return builder.build();
}
@Override
@@ -70,13 +76,13 @@
this.mIssues = new ArrayList<>(issues);
}
- /** Returns the data for the safety source status to be shown in UI. */
+ /** Returns the data for the {@link SafetySourceStatus} to be shown in UI. */
@Nullable
public SafetySourceStatus getStatus() {
return mStatus;
}
- /** Returns the data for the list of safety source issues to be shown in UI. */
+ /** Returns the data for the list of {@link SafetySourceIssue}s to be shown in UI. */
@NonNull
public List<SafetySourceIssue> getIssues() {
return new ArrayList<>(mIssues);
@@ -124,14 +130,14 @@
@Nullable
private SafetySourceStatus mStatus;
- /** Sets data for the safety source status to be shown in UI. */
+ /** Sets data for the {@link SafetySourceStatus} to be shown in UI. */
@NonNull
public Builder setStatus(@Nullable SafetySourceStatus status) {
mStatus = status;
return this;
}
- /** Adds data for a safety source issue to be shown in UI. */
+ /** Adds data for a {@link SafetySourceIssue} to be shown in UI. */
@NonNull
public Builder addIssue(@NonNull SafetySourceIssue safetySourceIssue) {
mIssues.add(requireNonNull(safetySourceIssue));
@@ -139,7 +145,8 @@
}
/**
- * Clears data for all the safety source issues that were added to this {@link Builder}.
+ * Clears data for all the {@link SafetySourceIssue}s that were added to this
+ * {@link Builder}.
*/
@NonNull
public Builder clearIssues() {
@@ -152,7 +159,7 @@
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(mStatus, mIssues);
+ return new SafetySourceData(mStatus, Collections.unmodifiableList(mIssues));
}
}
}
diff --git a/framework-s/java/android/safetycenter/SafetySourceIssue.java b/framework-s/java/android/safetycenter/SafetySourceIssue.java
index 950fc35..dc7d912 100644
--- a/framework-s/java/android/safetycenter/SafetySourceIssue.java
+++ b/framework-s/java/android/safetycenter/SafetySourceIssue.java
@@ -37,6 +37,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -112,8 +113,15 @@
PendingIntent onDismissPendingIntent =
PendingIntent.readPendingIntentOrNullFromParcel(in);
String issueTypeId = requireNonNull(in.readString());
- return new SafetySourceIssue(id, title, subtitle, summary, severityLevel,
- issueCategory, actions, onDismissPendingIntent, issueTypeId);
+ Builder builder = new Builder(id, title, summary, severityLevel, issueTypeId)
+ .setSubtitle(subtitle)
+ .setIssueCategory(issueCategory)
+ .setOnDismissPendingIntent(onDismissPendingIntent);
+ // TODO(b/224513050): Consider simplifying by adding a new API to the builder.
+ for (int i = 0; i < actions.size(); i++) {
+ builder.addAction(actions.get(i));
+ }
+ return builder.build();
}
@Override
@@ -209,8 +217,7 @@
}
/**
- * Returns a list of {@link Action} instances representing actions supported in the UI for this
- * issue.
+ * Returns a list of {@link Action}s representing actions supported in the UI for this issue.
*
* <p>Each issue must contain at least one action, in order to help the user resolve the issue.
*
@@ -383,16 +390,14 @@
new Parcelable.Creator<Action>() {
@Override
public Action createFromParcel(Parcel in) {
- String id = requireNonNull(in.readString());
- CharSequence label =
- requireNonNull(
- TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
- PendingIntent pendingIntent =
- requireNonNull(PendingIntent.readPendingIntentOrNullFromParcel(in));
- boolean willResolve = in.readBoolean();
- CharSequence successMessage =
- TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- return new Action(id, label, pendingIntent, willResolve, successMessage);
+ return new Builder(
+ in.readString(),
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in),
+ PendingIntent.readPendingIntentOrNullFromParcel(in))
+ .setWillResolve(in.readBoolean())
+ .setSuccessMessage(
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in))
+ .build();
}
@Override
@@ -618,14 +623,14 @@
return this;
}
- /** Adds data for an action to be shown in UI. */
+ /** Adds data for an {@link Action} to be shown in UI. */
@NonNull
public Builder addAction(@NonNull Action actionData) {
mActions.add(requireNonNull(actionData));
return this;
}
- /** Clears data for all the actions that were added to this {@link Builder}. */
+ /** Clears data for all the {@link Action}s that were added to this {@link Builder}. */
@NonNull
public Builder clearActions() {
mActions.clear();
@@ -656,7 +661,8 @@
checkArgument(mActions.size() <= 2,
"Safety source issue must not contain more than 2 actions");
return new SafetySourceIssue(mId, mTitle, mSubtitle, mSummary, mSeverityLevel,
- mIssueCategory, mActions, mOnDismissPendingIntent, mIssueTypeId);
+ mIssueCategory, Collections.unmodifiableList(mActions), mOnDismissPendingIntent,
+ mIssueTypeId);
}
}
}
diff --git a/framework-s/java/android/safetycenter/SafetySourceStatus.java b/framework-s/java/android/safetycenter/SafetySourceStatus.java
index 83a5410..9868064 100644
--- a/framework-s/java/android/safetycenter/SafetySourceStatus.java
+++ b/framework-s/java/android/safetycenter/SafetySourceStatus.java
@@ -80,17 +80,14 @@
new Parcelable.Creator<SafetySourceStatus>() {
@Override
public SafetySourceStatus createFromParcel(Parcel in) {
- CharSequence title =
- requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
- CharSequence summary =
- requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
- int statusLevel = in.readInt();
- PendingIntent pendingIntent =
- requireNonNull(PendingIntent.readPendingIntentOrNullFromParcel(in));
- IconAction iconAction = in.readTypedObject(IconAction.CREATOR);
- boolean enabled = in.readBoolean();
- return new SafetySourceStatus(title, summary, statusLevel, pendingIntent,
- iconAction, enabled);
+ return new Builder(
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in),
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in),
+ in.readInt(),
+ PendingIntent.readPendingIntentOrNullFromParcel(in))
+ .setIconAction(in.readTypedObject(IconAction.CREATOR))
+ .setEnabled(in.readBoolean())
+ .build();
}
@Override
@@ -150,7 +147,7 @@
}
/**
- * Returns an optional icon action to be displayed in the safety source status UI.
+ * Returns an optional {@link IconAction} to be displayed in the safety source status UI.
*
* <p>The icon action will be a clickable icon which performs an action as indicated by the
* icon.
@@ -387,7 +384,7 @@
}
/**
- * Sets an optional icon action for the safety source status.
+ * Sets an optional {@link IconAction} for the safety source status.
*
* @see #getIconAction()
*/
diff --git a/framework-s/java/android/safetycenter/config/BuilderUtils.java b/framework-s/java/android/safetycenter/config/BuilderUtils.java
index b94f9fe..a01eb65 100644
--- a/framework-s/java/android/safetycenter/config/BuilderUtils.java
+++ b/framework-s/java/android/safetycenter/config/BuilderUtils.java
@@ -16,38 +16,50 @@
package android.safetycenter.config;
-import android.annotation.IdRes;
+import android.annotation.AnyRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
+import java.util.Objects;
+
final class BuilderUtils {
private BuilderUtils() {
}
- static void validateAttribute(@Nullable Object attribute, @NonNull String name,
- boolean required, boolean prohibited) {
+ private static void validateAttribute(@Nullable Object attribute, @NonNull String name,
+ boolean required, boolean prohibited, @Nullable Object defaultValue) {
if (attribute == null && required) {
throw new IllegalStateException(String.format("Required attribute %s missing", name));
}
- if (attribute != null && prohibited) {
+ boolean nonDefaultValueProvided = !Objects.equals(attribute, defaultValue);
+ boolean checkProhibited = prohibited && nonDefaultValueProvided;
+ if (attribute != null && checkProhibited) {
throw new IllegalStateException(String.format("Prohibited attribute %s present", name));
}
}
- @IdRes
- static int validateResId(@Nullable @IdRes Integer value, @NonNull String name, boolean required,
- boolean prohibited) {
- validateAttribute(value, name, required, prohibited);
+ static void validateAttribute(@Nullable Object attribute, @NonNull String name,
+ boolean required, boolean prohibited) {
+ validateAttribute(attribute, name, required, prohibited, null);
+ }
+
+ @AnyRes
+ static int validateResId(@Nullable @AnyRes Integer value, @NonNull String name,
+ boolean required, boolean prohibited) {
+ validateAttribute(value, name, required, prohibited, Integer.valueOf(Resources.ID_NULL));
if (value == null) {
return Resources.ID_NULL;
}
+ if (required && value == Resources.ID_NULL) {
+ throw new IllegalStateException(String.format("Required attribute %s invalid", name));
+ }
return value;
}
static int validateIntDef(@Nullable Integer value, @NonNull String name,
boolean required, boolean prohibited, int defaultValue, int... validValues) {
- validateAttribute(value, name, required, prohibited);
+ validateAttribute(value, name, required, prohibited, Integer.valueOf(defaultValue));
if (value == null) {
return defaultValue;
}
@@ -64,7 +76,7 @@
static int validateInteger(@Nullable Integer value, @NonNull String name,
boolean required, boolean prohibited, int defaultValue) {
- validateAttribute(value, name, required, prohibited);
+ validateAttribute(value, name, required, prohibited, Integer.valueOf(defaultValue));
if (value == null) {
return defaultValue;
}
@@ -73,7 +85,7 @@
static boolean validateBoolean(@Nullable Boolean value, @NonNull String name,
boolean required, boolean prohibited, boolean defaultValue) {
- validateAttribute(value, name, required, prohibited);
+ validateAttribute(value, name, required, prohibited, Boolean.valueOf(defaultValue));
if (value == null) {
return defaultValue;
}
diff --git a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
index 9f74be0..6f2bb8d 100644
--- a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
+++ b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
@@ -50,7 +50,7 @@
mSafetySourcesGroups = safetySourcesGroups;
}
- /** Returns the list of safety sources groups in the configuration. */
+ /** Returns the list of {@link SafetySourcesGroup}s in the configuration. */
@NonNull
public List<SafetySourcesGroup> getSafetySourcesGroups() {
return mSafetySourcesGroups;
@@ -83,7 +83,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelableList(mSafetySourcesGroups, flags);
+ dest.writeTypedList(mSafetySourcesGroups);
}
@NonNull
@@ -91,11 +91,14 @@
new Parcelable.Creator<SafetyCenterConfig>() {
@Override
public SafetyCenterConfig createFromParcel(Parcel in) {
- List<SafetySourcesGroup> safetySourcesGroups = new ArrayList<>();
- in.readParcelableList(safetySourcesGroups,
- SafetySourcesGroup.class.getClassLoader());
- return new SafetyCenterConfig(
- Collections.unmodifiableList(safetySourcesGroups));
+ List<SafetySourcesGroup> safetySourcesGroups =
+ in.createTypedArrayList(SafetySourcesGroup.CREATOR);
+ Builder builder = new Builder();
+ // TODO(b/224513050): Consider simplifying by adding a new API to the builder.
+ for (int i = 0; i < safetySourcesGroups.size(); i++) {
+ builder.addSafetySourcesGroup(safetySourcesGroups.get(i));
+ }
+ return builder.build();
}
@Override
@@ -113,7 +116,7 @@
public Builder() {
}
- /** Adds a safety source group to the configuration. */
+ /** Adds a {@link SafetySourcesGroup} to the configuration. */
@NonNull
public Builder addSafetySourcesGroup(@NonNull SafetySourcesGroup safetySourcesGroup) {
mSafetySourcesGroups.add(requireNonNull(safetySourcesGroup));
diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java
index 2ec6cfd..2ed7463 100644
--- a/framework-s/java/android/safetycenter/config/SafetySource.java
+++ b/framework-s/java/android/safetycenter/config/SafetySource.java
@@ -387,24 +387,21 @@
new Parcelable.Creator<SafetySource>() {
@Override
public SafetySource createFromParcel(Parcel in) {
- int type = in.readInt();
- String id = in.readString();
- String packageName = in.readString();
- int titleResId = in.readInt();
- int titleForWorkResId = in.readInt();
- int summaryResId = in.readInt();
- String intentAction = in.readString();
- int profile = in.readInt();
- int initialDisplayState = in.readInt();
- int maxSeverityLevel = in.readInt();
- int searchTermsResId = in.readInt();
- String broadcastReceiverClassName = in.readString();
- boolean loggingAllowed = in.readBoolean();
- boolean refreshOnPageOpenAllowed = in.readBoolean();
- return new SafetySource(type, id, packageName, titleResId, titleForWorkResId,
- summaryResId, intentAction, profile, initialDisplayState,
- maxSeverityLevel, searchTermsResId, broadcastReceiverClassName,
- loggingAllowed, refreshOnPageOpenAllowed);
+ return new Builder(in.readInt())
+ .setId(in.readString())
+ .setPackageName(in.readString())
+ .setTitleResId(in.readInt())
+ .setTitleForWorkResId(in.readInt())
+ .setSummaryResId(in.readInt())
+ .setIntentAction(in.readString())
+ .setProfile(in.readInt())
+ .setInitialDisplayState(in.readInt())
+ .setMaxSeverityLevel(in.readInt())
+ .setSearchTermsResId(in.readInt())
+ .setBroadcastReceiverClassName(in.readString())
+ .setLoggingAllowed(in.readBoolean())
+ .setRefreshOnPageOpenAllowed(in.readBoolean())
+ .build();
}
@Override
diff --git a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
index 84ce677..e76d9a6 100644
--- a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
+++ b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
@@ -163,7 +163,7 @@
return mStatelessIconType;
}
- /** Returns the list of safety sources in this safety sources group. */
+ /** Returns the list of {@link SafetySource}s in this safety sources group. */
@NonNull
public List<SafetySource> getSafetySources() {
return mSafetySources;
@@ -208,7 +208,7 @@
dest.writeInt(mTitleResId);
dest.writeInt(mSummaryResId);
dest.writeInt(mStatelessIconType);
- dest.writeParcelableList(mSafetySources, flags);
+ dest.writeTypedList(mSafetySources);
}
@NonNull
@@ -216,14 +216,18 @@
new Parcelable.Creator<SafetySourcesGroup>() {
@Override
public SafetySourcesGroup createFromParcel(Parcel in) {
- String id = in.readString();
- int titleResId = in.readInt();
- int summaryResId = in.readInt();
- int statelessIconType = in.readInt();
- List<SafetySource> safetySources = new ArrayList<>();
- in.readParcelableList(safetySources, SafetySource.class.getClassLoader());
- return new SafetySourcesGroup(id, titleResId, summaryResId, statelessIconType,
- Collections.unmodifiableList(safetySources));
+ Builder builder = new Builder()
+ .setId(in.readString())
+ .setTitleResId(in.readInt())
+ .setSummaryResId(in.readInt())
+ .setStatelessIconType(in.readInt());
+ List<SafetySource> safetySources =
+ in.createTypedArrayList(SafetySource.CREATOR);
+ // TODO(b/224513050): Consider simplifying by adding a new API to the builder.
+ for (int i = 0; i < safetySources.size(); i++) {
+ builder.addSafetySource(safetySources.get(i));
+ }
+ return builder.build();
}
@Override
@@ -280,7 +284,7 @@
return this;
}
- /** Adds a safety source to this safety sources group. */
+ /** Adds a {@link SafetySource} to this safety sources group. */
@NonNull
public Builder addSafetySource(@NonNull SafetySource safetySource) {
mSafetySources.add(requireNonNull(safetySource));