[Angel] Add support for PendingIntents to SafetySourceData and address comments from ag/16182563

* Adds SafetySourceIssue.Action representing button actions to safety issue, and add PendingIntents for safety preferences.
* Rename
    SafetyPreferenceData -> SafetySourceStatus, SafetyIssueData ->
    SafetySourceIssue, SafetyPreferenceData.SeverityLevel ->  SafetySourceStatus.StatusLevel.I
chose to keep the levels separate as their javadocs/guidelines for
choosing a level will be different.
* I haven't folded SafetyPreferenceData into SafetySourceData as the
SafetyPreferenceData will be @Nullable. UX has confirmed we want this
behaviour for sources - sources can provide issues without the
preference, but in this case the warnings will not be allowed to affect
the overall status.
* Refine javadocs for API
* Delete aidl files other than SafetySourceData as that's the only class
to be used as arguments in IPC

Test: Will add CTS tests when API is no longer hidden.
Bug: 206752239

Change-Id: I868bb324f14e2f4262b8796bccdde34f6aa9ba68
diff --git a/framework-s/java/android/safetycenter/SafetyIssueData.aidl b/framework-s/java/android/safetycenter/SafetyIssueData.aidl
deleted file mode 100644
index 9a33ded..0000000
--- a/framework-s/java/android/safetycenter/SafetyIssueData.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 SafetyIssueData.
- *
- * @hide
- */
-parcelable SafetyIssueData;
diff --git a/framework-s/java/android/safetycenter/SafetyIssueData.java b/framework-s/java/android/safetycenter/SafetyIssueData.java
deleted file mode 100644
index c115231..0000000
--- a/framework-s/java/android/safetycenter/SafetyIssueData.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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;
-
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import androidx.annotation.RequiresApi;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Data for a safety issue in the Safety Center page. A safety issue represents an actionable issue
- *  relating to a particular safety source.
- *
- * @hide
- */
-// @SystemApi -- Add this line back when ready for API council review.
-// TODO(b/205551986): Move this class into `framework-s`, add NonNull annotations, replace usages of
-//  `androidx.annotation.IntDef` with `android.annotation.IntDef` and add prefixes to IntDefs.
-@RequiresApi(TIRAMISU)
-public final class SafetyIssueData implements Parcelable {
-
-    /**
-     * All possible severity levels for the safety issue.
-     *
-     * @hide
-     */
-    @IntDef(prefix = { "SEVERITY_LEVEL_" }, value = {
-            SEVERITY_LEVEL_INFORMATION,
-            SEVERITY_LEVEL_RECOMMENDATION,
-            SEVERITY_LEVEL_CRITICAL_WARNING
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SeverityLevel {}
-
-    /**
-     * Indicates an informational message. This severity will be reflected in the UI through a
-     * green icon.
-     */
-    public static final int SEVERITY_LEVEL_INFORMATION = 200;
-
-    /**
-     * Indicates a medium-severity issue which the user is encouraged to act on. This severity will
-     * be reflected in the UI through a yellow icon.
-     */
-    public static final int SEVERITY_LEVEL_RECOMMENDATION = 300;
-
-    /**
-     * Indicates a critical or urgent security issue that should be addressed by the user. This
-     * severity will be reflected in the UI through a red icon.
-     */
-    public static final int SEVERITY_LEVEL_CRITICAL_WARNING = 400;
-
-    @NonNull
-    public static final Parcelable.Creator<SafetyIssueData> CREATOR =
-            new Parcelable.Creator<SafetyIssueData>() {
-                @Override
-                public SafetyIssueData createFromParcel(Parcel in) {
-                    CharSequence title =
-                            requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
-                    CharSequence summary =
-                            requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
-                    int severityLevel = in.readInt();
-                    return new SafetyIssueData(title, summary, severityLevel);
-                }
-
-                @Override
-                public SafetyIssueData[] newArray(int size) {
-                    return new SafetyIssueData[size];
-                }
-            };
-
-    @NonNull
-    private final CharSequence mTitle;
-
-    @NonNull
-    private final CharSequence mSummary;
-
-    private final @SeverityLevel int mSeverityLevel;
-
-    private SafetyIssueData(@NonNull CharSequence title, @NonNull CharSequence summary,
-            @SeverityLevel int severityLevel) {
-        this.mTitle = title;
-        this.mSummary = summary;
-        this.mSeverityLevel = severityLevel;
-    }
-
-    /** Returns the localized title of the safety issue to be displayed in the UI. */
-    @NonNull
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /** Returns the localized summary of the safety issue to be displayed in the UI. */
-    @NonNull
-    public CharSequence getSummary() {
-        return mSummary;
-    }
-
-    /** Returns the {@link SeverityLevel} of the safety issue. */
-    @SeverityLevel
-    public int getSeverityLevel() {
-        return mSeverityLevel;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        TextUtils.writeToParcel(mTitle, dest, flags);
-        TextUtils.writeToParcel(mSummary, dest, flags);
-        dest.writeInt(mSeverityLevel);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof SafetyIssueData)) return false;
-        SafetyIssueData that = (SafetyIssueData) o;
-        return mSeverityLevel == that.mSeverityLevel && mTitle.equals(that.mTitle)
-                && mSummary.equals(
-                that.mSummary);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mTitle, mSummary, mSeverityLevel);
-    }
-
-    @Override
-    public String toString() {
-        return "SafetyIssueData{"
-                + "mTitle='"
-                + mTitle
-                + '\''
-                + ", mSummary='"
-                + mSummary
-                + '\''
-                + ", mSeverityLevel="
-                + mSeverityLevel
-                + '}';
-    }
-
-    /** Builder class for {@link SafetyIssueData}. */
-    public static final class Builder {
-        @NonNull
-        private final CharSequence mTitle;
-
-        @NonNull
-        private final CharSequence mSummary;
-
-        private @SeverityLevel final int mSeverityLevel;
-
-        /** Creates a {@link Builder} for a {@link SafetyIssueData}. */
-        public Builder(@NonNull CharSequence title, @NonNull CharSequence summary,
-                @SeverityLevel int severityLevel) {
-            this.mTitle = requireNonNull(title);
-            this.mSummary = requireNonNull(summary);
-            this.mSeverityLevel = severityLevel;
-        }
-
-        /** Creates the {@link SafetyIssueData} defined by this {@link Builder}. */
-        @NonNull
-        public SafetyIssueData build() {
-            return new SafetyIssueData(mTitle, mSummary, mSeverityLevel);
-        }
-    }
-}
diff --git a/framework-s/java/android/safetycenter/SafetyPreferenceData.aidl b/framework-s/java/android/safetycenter/SafetyPreferenceData.aidl
deleted file mode 100644
index 36bcc19..0000000
--- a/framework-s/java/android/safetycenter/SafetyPreferenceData.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 SafetyPreferenceData.
- *
- * @hide
- */
-parcelable SafetyPreferenceData;
diff --git a/framework-s/java/android/safetycenter/SafetyPreferenceData.java b/framework-s/java/android/safetycenter/SafetyPreferenceData.java
deleted file mode 100644
index c97d3ca..0000000
--- a/framework-s/java/android/safetycenter/SafetyPreferenceData.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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;
-
-import static android.os.Build.VERSION_CODES.TIRAMISU;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import androidx.annotation.RequiresApi;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Data for a safety preference in the Safety Center page. A safety preference represents the
- * overall safety state of a safety source.
- *
- * @hide
- */
-// @SystemApi -- Add this line back when ready for API council review.
-// TODO(b/205551986): Move this class into `framework-s`, add NonNull annotations, replace usages of
-//  `androidx.annotation.IntDef` with `android.annotation.IntDef` and add prefixes to IntDefs.
-@RequiresApi(TIRAMISU)
-public final class SafetyPreferenceData implements Parcelable {
-
-    /**
-     * All possible severity levels for the safety source's safety state.
-     *
-     * @hide
-     */
-    // TODO(b/205806500): Determine full list of severity levels. We may add a new one to signify
-    //  that there was an error retrieving data.
-    @IntDef(prefix = { "SEVERITY_LEVEL_" }, value = {
-            SEVERITY_LEVEL_NONE,
-            SEVERITY_LEVEL_NO_ISSUES,
-            SEVERITY_LEVEL_RECOMMENDATION,
-            SEVERITY_LEVEL_CRITICAL_WARNING
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SeverityLevel {}
-
-    /**
-     * Indicates that no severity is associated with the message. This severity will be reflected in
-     * the UI through the absence of an icon.
-     */
-    public static final int SEVERITY_LEVEL_NONE = 100;
-
-    /**
-     * Indicates that no issues were detected. This severity will be reflected in the UI through a
-     * green icon.
-     */
-    public static final int SEVERITY_LEVEL_NO_ISSUES = 200;
-
-    /**
-     * Indicates the presence of a medium-severity issue which the user is encouraged to act on.
-     * This severity will be reflected in the UI through a yellow icon.
-     */
-    public static final int SEVERITY_LEVEL_RECOMMENDATION = 300;
-
-    /**
-     * Indicates the presence of a critical or urgent security issue that should be addressed by the
-     * user. This severity will be reflected in the UI through a red icon.
-     */
-    public static final int SEVERITY_LEVEL_CRITICAL_WARNING = 400;
-
-    @NonNull
-    public static final Parcelable.Creator<SafetyPreferenceData> CREATOR =
-            new Parcelable.Creator<SafetyPreferenceData>() {
-                @Override
-                public SafetyPreferenceData createFromParcel(Parcel in) {
-                    CharSequence title =
-                            requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
-                    CharSequence summary =
-                            requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
-                    @SeverityLevel int severityLevel = in.readInt();
-                    return new SafetyPreferenceData(title, summary, severityLevel);
-                }
-
-                @Override
-                public SafetyPreferenceData[] newArray(int size) {
-                    return new SafetyPreferenceData[size];
-                }
-            };
-
-    @NonNull
-    private final CharSequence mTitle;
-
-    @NonNull
-    private final CharSequence mSummary;
-
-    private final @SeverityLevel int mSeverityLevel;
-
-    private SafetyPreferenceData(@NonNull CharSequence title, @NonNull CharSequence summary,
-            @SeverityLevel int severityLevel) {
-        this.mTitle = title;
-        this.mSummary = summary;
-        this.mSeverityLevel = severityLevel;
-    }
-
-    /** Returns the localized title of the safety preference to be displayed in the UI. */
-    @NonNull
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /** Returns the localized summary of the safety preference to be displayed in the UI. */
-    @NonNull
-    public CharSequence getSummary() {
-        return mSummary;
-    }
-
-    /** Returns the {@link SeverityLevel} of the safety preference. */
-    @SeverityLevel
-    public int getSeverityLevel() {
-        return mSeverityLevel;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        TextUtils.writeToParcel(mTitle, dest, flags);
-        TextUtils.writeToParcel(mSummary, dest, flags);
-        dest.writeInt(mSeverityLevel);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof SafetyPreferenceData)) return false;
-        SafetyPreferenceData that = (SafetyPreferenceData) o;
-        return mSeverityLevel == that.mSeverityLevel && mTitle.equals(that.mTitle)
-                && mSummary.equals(
-                that.mSummary);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mTitle, mSummary, mSeverityLevel);
-    }
-
-    @Override
-    public String toString() {
-        return "SafetyPreferenceData{"
-                + "mTitle='"
-                + mTitle
-                + '\''
-                + ", mSummary='"
-                + mSummary
-                + '\''
-                + ", mSeverityLevel="
-                + mSeverityLevel
-                + '}';
-    }
-
-    /** Builder class for {@link SafetyPreferenceData}. */
-    public static final class Builder {
-        @NonNull
-        private final CharSequence mTitle;
-
-        @NonNull
-        private final CharSequence mSummary;
-
-        private @SeverityLevel final int mSeverityLevel;
-
-        /** Creates a {@link Builder} for a {@link SafetyPreferenceData}. */
-        public Builder(@NonNull CharSequence title, @NonNull CharSequence summary,
-                @SeverityLevel int severityLevel) {
-            this.mTitle = requireNonNull(title);
-            this.mSummary = requireNonNull(summary);
-            this.mSeverityLevel = severityLevel;
-        }
-
-        /** Creates the {@link SafetyPreferenceData} defined by this {@link Builder}. */
-        @NonNull
-        public SafetyPreferenceData build() {
-            return new SafetyPreferenceData(mTitle, mSummary, mSeverityLevel);
-        }
-    }
-}
diff --git a/framework-s/java/android/safetycenter/SafetySourceData.java b/framework-s/java/android/safetycenter/SafetySourceData.java
index 7e1984a..b237330 100644
--- a/framework-s/java/android/safetycenter/SafetySourceData.java
+++ b/framework-s/java/android/safetycenter/SafetySourceData.java
@@ -32,16 +32,14 @@
 import java.util.Objects;
 
 /**
- * Data class used by safety sources to propagate safety information such as a generic status,
- * potential warnings and other related metadata.
+ * Data class used by safety sources to propagate safety information such as their safety status and
+ * safety issues.
  *
  * @hide
  */
 // @SystemApi -- Add this line back when ready for API council review.
+// TODO(b/207399899): Add timestamp field(s) to data model classes.
 @RequiresApi(TIRAMISU)
-// TODO(b/205551986): Move this to the right place and add back the NonNull annotations once
-//  b/205289292 is fixed.
-// TODO(b/206089303): Add Builders as more fields are added to this class.
 public final class SafetySourceData implements Parcelable {
 
     @NonNull
@@ -49,14 +47,13 @@
             new Parcelable.Creator<SafetySourceData>() {
                 @Override
                 public SafetySourceData createFromParcel(Parcel in) {
-                    String safetySourceId = requireNonNull(in.readString());
-                    SafetyPreferenceData safetyPreferenceData =
-                            requireNonNull(
-                                    in.readParcelable(SafetyPreferenceData.class.getClassLoader()));
-                    List<SafetyIssueData> safetyIssuesData = new ArrayList<>();
-                    in.readParcelableList(safetyIssuesData, SafetyIssueData.class.getClassLoader());
-                    return new SafetySourceData(safetySourceId,
-                            safetyPreferenceData, safetyIssuesData);
+                    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);
                 }
                 @Override
                 public SafetySourceData[] newArray(int size) {
@@ -65,38 +62,42 @@
             };
 
     @NonNull
-    private final String mSafetySourceId;
+    private final String mId;
 
     @Nullable
-    private final SafetyPreferenceData mSafetyPreferenceData;
+    private final SafetySourceStatus mStatus;
 
     @NonNull
-    private final List<SafetyIssueData> mSafetyIssuesData;
+    private final List<SafetySourceIssue> mIssues;
 
-    private SafetySourceData(@NonNull String safetySourceId,
-            @Nullable SafetyPreferenceData safetyPreferenceData,
-            @NonNull List<SafetyIssueData> safetyIssuesData) {
-        this.mSafetySourceId = safetySourceId;
-        this.mSafetyPreferenceData = safetyPreferenceData;
-        this.mSafetyIssuesData = new ArrayList<>(safetyIssuesData);
+    private SafetySourceData(@NonNull String id, @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. */
+    /**
+     * 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 getSafetySourceId() {
-        return mSafetySourceId;
+    public String getId() {
+        return mId;
     }
 
-    /** Returns the data for the safety preference to be shown in UI. */
+    /** Returns the data for the safety source status to be shown in UI. */
     @Nullable
-    public SafetyPreferenceData getSafetyPreferenceData() {
-        return mSafetyPreferenceData;
+    public SafetySourceStatus getStatus() {
+        return mStatus;
     }
 
-    /** Returns the data for the list of safety issues to be shown in UI. */
+    /** Returns the data for the list of safety source issues to be shown in UI. */
     @NonNull
-    public List<SafetyIssueData> getSafetyIssuesData() {
-        return new ArrayList<>(mSafetyIssuesData);
+    public List<SafetySourceIssue> getIssues() {
+        return new ArrayList<>(mIssues);
     }
 
     @Override
@@ -106,9 +107,9 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(mSafetySourceId);
-        dest.writeParcelable(mSafetyPreferenceData, flags);
-        dest.writeParcelableList(mSafetyIssuesData, flags);
+        dest.writeString(mId);
+        dest.writeParcelable(mStatus, flags);
+        dest.writeParcelableList(mIssues, flags);
     }
 
     @Override
@@ -116,26 +117,25 @@
         if (this == o) return true;
         if (!(o instanceof SafetySourceData)) return false;
         SafetySourceData that = (SafetySourceData) o;
-        return mSafetySourceId.equals(that.mSafetySourceId)
-                && mSafetyPreferenceData.equals(that.mSafetyPreferenceData)
-                && mSafetyIssuesData.equals(that.mSafetyIssuesData);
+        return mId.equals(that.mId) && Objects.equals(mStatus, that.mStatus)
+                && mIssues.equals(that.mIssues);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSafetySourceId, mSafetyPreferenceData, mSafetyIssuesData);
+        return Objects.hash(mId, mStatus, mIssues);
     }
 
     @Override
     public String toString() {
         return "SafetySourceData{"
-                + "mSafetySourceId='"
-                + mSafetySourceId
+                + "mId='"
+                + mId
                 + '\''
-                + ", mSafetyPreferenceData="
-                + mSafetyPreferenceData
-                + ", mSafetyIssuesData="
-                + mSafetyIssuesData
+                + ", mStatus="
+                + mStatus
+                + ", mIssues="
+                + mIssues
                 + '}';
     }
 
@@ -145,49 +145,49 @@
         private final String mId;
 
         @Nullable
-        private SafetyPreferenceData mSafetyPreferenceData;
+        private SafetySourceStatus mStatus;
 
         @NonNull
-        private List<SafetyIssueData> mSafetyIssuesData = new ArrayList<>();
+        private final List<SafetySourceIssue> mIssues = new ArrayList<>();
 
-        /**
-         * Creates a {@link Builder} for a {@link SafetySourceData} using the id of the associated
-         * safety source.
+        /** Creates a {@link Builder} for a {@link SafetySourceData}.
+         *
+         *  @param id uniquely identifies this safety source, scoped within the
+         *            application that is creating the safety source.
          */
         public Builder(@NonNull String id) {
             this.mId = requireNonNull(id);
         }
 
-        /** Sets data for the safety preference to be shown in UI. */
+        /** Sets data for the safety source status to be shown in UI. */
         @NonNull
-        public Builder setSafetyPreferenceData(
-                @Nullable SafetyPreferenceData safetyPreferenceData) {
-            mSafetyPreferenceData = safetyPreferenceData;
+        public Builder setStatus(@Nullable SafetySourceStatus status) {
+            mStatus = status;
             return this;
         }
 
-        /** Adds data for a safety issue to be shown in UI. */
+        /** Adds data for a safety source issue to be shown in UI. */
         @NonNull
-        // @SuppressWarnings("MissingGetterMatchingBuilder")
-        //  The MissingGetterMatchingBuilder warning has been suppressed as it expects the
-        //  corresponding getter to be named `getSafetyIssueDatas()` which would be grammatically
-        //  incorrect.
-        public Builder addSafetyIssueData(@NonNull SafetyIssueData safetyIssueData) {
-            mSafetyIssuesData.add(requireNonNull(safetyIssueData));
+        public Builder addIssue(@NonNull SafetySourceIssue safetySourceIssue) {
+            mIssues.add(requireNonNull(safetySourceIssue));
             return this;
         }
 
-        /** Clears data for all the safety issues that were added to this {@link Builder}. */
+        /**
+         * Clears data for all the safety source issues that were added to this {@link Builder}.
+         */
         @NonNull
-        public Builder clearSafetyIssuesData() {
-            mSafetyIssuesData.clear();
+        public Builder clearIssues() {
+            mIssues.clear();
             return this;
         }
 
         /** Creates the {@link SafetySourceData} defined by this {@link Builder}. */
         @NonNull
         public SafetySourceData build() {
-            return new SafetySourceData(mId, mSafetyPreferenceData, mSafetyIssuesData);
+            // 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);
         }
     }
 }
diff --git a/framework-s/java/android/safetycenter/SafetySourceIssue.java b/framework-s/java/android/safetycenter/SafetySourceIssue.java
new file mode 100644
index 0000000..1800efc
--- /dev/null
+++ b/framework-s/java/android/safetycenter/SafetySourceIssue.java
@@ -0,0 +1,410 @@
+/*
+ * 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;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.RequiresApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Data for a safety source issue in the Safety Center page.
+ *
+ * <p>An issue represents an actionable matter relating to a particular safety source.
+ *
+ * <p>The safety issue will contain localized messages to be shown in UI explaining the potential
+ * threat or warning and suggested fixes, as well as actions a user is allowed to take from the UI
+ * to resolve the issue.
+ *
+ * @hide
+ */
+// @SystemApi -- Add this line back when ready for API council review.
+@RequiresApi(TIRAMISU)
+public final class SafetySourceIssue implements Parcelable {
+
+    /**
+     * All possible severity levels for the safety source issue.
+     *
+     * <p>The severity level is meant to convey the severity of the individual issue.
+     *
+     * <p>The higher the severity level, the worse the safety level of the source and the higher
+     * the threat to the user.
+     *
+     * <p>The numerical values of the levels are not used directly, rather they are used to build
+     * a continuum of levels which support relative comparison.
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "SEVERITY_LEVEL_" }, value = {
+            SEVERITY_LEVEL_INFORMATION,
+            SEVERITY_LEVEL_RECOMMENDATION,
+            SEVERITY_LEVEL_CRITICAL_WARNING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SeverityLevel {}
+
+    /**
+     * Indicates an informational message. This severity will be reflected in the UI through a
+     * green icon.
+     */
+    public static final int SEVERITY_LEVEL_INFORMATION = 200;
+
+    /**
+     * Indicates a medium-severity issue which the user is encouraged to act on. This severity will
+     * be reflected in the UI through a yellow icon.
+     */
+    public static final int SEVERITY_LEVEL_RECOMMENDATION = 300;
+
+    /**
+     * Indicates a critical or urgent safety issue that should be addressed by the user. This
+     * severity will be reflected in the UI through a red icon.
+     */
+    public static final int SEVERITY_LEVEL_CRITICAL_WARNING = 400;
+
+    /**
+     * Data for an action supported from a safety issue {@link SafetySourceIssue} in the Safety
+     * Center page.
+     *
+     * <p>The purpose of the action is to allow the user to address the safety issue, either by
+     * performing a fix suggested in the issue, or by navigating the user to the source of the issue
+     * where they can be exposed to details about the issue and further suggestions to resolve it.
+     *
+     * <p>The user will be allowed to invoke the action from the UI by clicking on a UI element and
+     * consequently resolve the issue.
+     *
+     * @hide
+     */
+    // @SystemApi -- Add this line back when ready for API council review.
+    public static final class Action implements Parcelable {
+
+        @NonNull
+        public static final Parcelable.Creator<Action> CREATOR =
+                new Parcelable.Creator<Action>() {
+                    @Override
+                    public Action createFromParcel(Parcel in) {
+                        CharSequence label =
+                                requireNonNull(
+                                        TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
+                        PendingIntent clickPendingIntent =
+                                requireNonNull(PendingIntent.readPendingIntentOrNullFromParcel(in));
+                        boolean resolving = in.readBoolean();
+                        return new Action(label, clickPendingIntent, resolving);
+                    }
+
+                    @Override
+                    public Action[] newArray(int size) {
+                        return new Action[size];
+                    }
+                };
+
+        @NonNull
+        private final CharSequence mLabel;
+
+        @NonNull
+        private final PendingIntent mClickPendingIntent;
+
+        private final boolean mResolving;
+
+        private Action(@NonNull CharSequence label, @NonNull PendingIntent clickPendingIntent,
+                boolean resolving) {
+            this.mLabel = label;
+            this.mClickPendingIntent = clickPendingIntent;
+            this.mResolving = resolving;
+        }
+
+        /**
+         * Returns the localized label of the action to be displayed in the UI.
+         *
+         * <p>The label should indicate what action will be performed if when invoked.
+         */
+        @NonNull
+        public CharSequence getLabel() {
+            return mLabel;
+        }
+
+        /**
+         * Returns a {@link PendingIntent} to be fired when the action is clicked on.
+         *
+         * <p>The {@link PendingIntent} should perform the action referred to by
+         * {@link #getLabel()}.
+         */
+        @NonNull
+        public PendingIntent getClickPendingIntent() {
+            return mClickPendingIntent;
+        }
+
+        /**
+         * Returns whether invoking this action will fix or address the issue sufficiently for it
+         * to be considered resolved i.e. the issue will no longer need to be conveyed to the user
+         * in the UI.
+         */
+        public boolean isResolving() {
+            return mResolving;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            TextUtils.writeToParcel(mLabel, dest, flags);
+            mClickPendingIntent.writeToParcel(dest, flags);
+            dest.writeBoolean(mResolving);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Action)) return false;
+            Action that = (Action) o;
+            return mResolving == that.mResolving
+                    && TextUtils.equals(mLabel, that.mLabel)
+                    && mClickPendingIntent.equals(that.mClickPendingIntent);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mLabel, mClickPendingIntent, mResolving);
+        }
+
+        @Override
+        public String toString() {
+            return "Action{"
+                    + "mLabel="
+                    + mLabel
+                    + ", mClickPendingIntent="
+                    + mClickPendingIntent
+                    + ", mResolving="
+                    + mResolving
+                    + '}';
+        }
+
+        /** Builder class for {@link Action}. */
+        public static final class Builder {
+            @NonNull
+            private final CharSequence mLabel;
+
+            @NonNull
+            private final PendingIntent mClickPendingIntent;
+
+            private boolean mResolving = false;
+
+            /** Creates a {@link Builder} for an {@link Action}. */
+            public Builder(@NonNull CharSequence label, @NonNull PendingIntent clickPendingIntent) {
+                this.mLabel = requireNonNull(label);
+                this.mClickPendingIntent = requireNonNull(clickPendingIntent);
+            }
+
+            /**
+             * Sets whether the action will resolve the safety issue.
+             *
+             * @see #isResolving()
+             */
+            @NonNull
+            public Builder setResolving(boolean resolving) {
+                this.mResolving = resolving;
+                return this;
+            }
+
+            /** Creates the {@link Action} defined by this {@link Builder}. */
+            @NonNull
+            public Action build() {
+                return new Action(mLabel, mClickPendingIntent, mResolving);
+            }
+        }
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SafetySourceIssue> CREATOR =
+            new Parcelable.Creator<SafetySourceIssue>() {
+                @Override
+                public SafetySourceIssue createFromParcel(Parcel in) {
+                    CharSequence title =
+                            requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
+                    CharSequence summary =
+                            requireNonNull(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
+                    int severityLevel = in.readInt();
+                    List<Action> actions = new ArrayList<>();
+                    in.readParcelableList(actions, Action.class.getClassLoader());
+                    return new SafetySourceIssue(title, summary, severityLevel, actions);
+                }
+
+                @Override
+                public SafetySourceIssue[] newArray(int size) {
+                    return new SafetySourceIssue[size];
+                }
+            };
+
+    @NonNull
+    private final CharSequence mTitle;
+
+    @NonNull
+    private final CharSequence mSummary;
+
+    private final @SeverityLevel int mSeverityLevel;
+
+    @NonNull
+    private final List<Action> mActions;
+
+    private SafetySourceIssue(@NonNull CharSequence title, @NonNull CharSequence summary,
+            @SeverityLevel int severityLevel, @NonNull List<Action> actions) {
+        this.mTitle = title;
+        this.mSummary = summary;
+        this.mSeverityLevel = severityLevel;
+        this.mActions = actions;
+    }
+
+    /** Returns the localized title of the issue to be displayed in the UI. */
+    @NonNull
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /** Returns the localized summary of the issue to be displayed in the UI. */
+    @NonNull
+    public CharSequence getSummary() {
+        return mSummary;
+    }
+
+    /** Returns the {@link SeverityLevel} of the issue. */
+    @SeverityLevel
+    public int getSeverityLevel() {
+        return mSeverityLevel;
+    }
+
+    /**
+     * Returns a list of {@link Action} instances 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.
+     *
+     * <p>In Android {@link android.os.Build.VERSION_CODES#TIRAMISU}, each issue can contain at most
+     * two actions supported from the UI.
+     */
+    @NonNull
+    public List<Action> getActions() {
+        return new ArrayList<>(mActions);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        TextUtils.writeToParcel(mTitle, dest, flags);
+        TextUtils.writeToParcel(mSummary, dest, flags);
+        dest.writeInt(mSeverityLevel);
+        dest.writeParcelableList(mActions, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SafetySourceIssue)) return false;
+        SafetySourceIssue that = (SafetySourceIssue) o;
+        return mSeverityLevel == that.mSeverityLevel
+                && TextUtils.equals(mTitle, that.mTitle)
+                && TextUtils.equals(mSummary, that.mSummary)
+                && mActions.equals(that.mActions);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTitle, mSummary, mSeverityLevel, mActions);
+    }
+
+    @Override
+    public String toString() {
+        return "SafetySourceIssue{"
+                + "mTitle="
+                + mTitle
+                + ", mSummary="
+                + mSummary
+                + ", mSeverityLevel="
+                + mSeverityLevel
+                + ", mActions="
+                + mActions
+                + '}';
+    }
+
+    /** Builder class for {@link SafetySourceIssue}. */
+    public static final class Builder {
+        @NonNull
+        private final CharSequence mTitle;
+
+        @NonNull
+        private final CharSequence mSummary;
+
+        private @SeverityLevel final int mSeverityLevel;
+
+        @NonNull
+        private final List<Action> mActions = new ArrayList<>();
+
+        /** Creates a {@link Builder} for a {@link SafetySourceIssue}. */
+        public Builder(@NonNull CharSequence title, @NonNull CharSequence summary,
+                @SeverityLevel int severityLevel) {
+            this.mTitle = requireNonNull(title);
+            this.mSummary = requireNonNull(summary);
+            this.mSeverityLevel = severityLevel;
+        }
+
+        /** Adds data for an 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}. */
+        @NonNull
+        public Builder clearActions() {
+            mActions.clear();
+            return this;
+        }
+
+        /** Creates the {@link SafetySourceIssue} defined by this {@link Builder}. */
+        @NonNull
+        public SafetySourceIssue build() {
+            // TODO(b/207402324): Check with UX whether issues without any actions are permitted.
+            checkArgument(!mActions.isEmpty(),
+                    "Safety source issue must contain at least 1 action");
+            checkArgument(mActions.size() <= 2,
+                    "Safety source issue must not contain more than 2 actions");
+            return new SafetySourceIssue(mTitle, mSummary, mSeverityLevel, mActions);
+        }
+    }
+}
diff --git a/framework-s/java/android/safetycenter/SafetySourceStatus.java b/framework-s/java/android/safetycenter/SafetySourceStatus.java
new file mode 100644
index 0000000..687f518
--- /dev/null
+++ b/framework-s/java/android/safetycenter/SafetySourceStatus.java
@@ -0,0 +1,233 @@
+/*
+ * 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;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.RequiresApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Data for a safety source status in the Safety Center page, which conveys the overall state of
+ * the safety source and allows a user to navigate to the source.
+ *
+ * @hide
+ */
+// @SystemApi -- Add this line back when ready for API council review.
+@RequiresApi(TIRAMISU)
+public final class SafetySourceStatus implements Parcelable {
+
+    /**
+     * All possible status levels for the safety source status.
+     *
+     * <p>The status level is meant to convey the overall state of the safety source and contributes
+     * to the top-level safety status of the user. Choose the status level to represent the most
+     * severe of all the safety source's issues.
+     *
+     * <p>The numerical values of the levels are not used directly, rather they are used to build
+     * a continuum of levels which support relative comparison.
+     *
+     * <p>The higher the status level, the worse the safety level of the source and the higher
+     * the threat to the user.
+     *
+     * @hide
+     */
+    // TODO(b/205806500): Determine full list of status levels. We may add a new one to signify
+    //  that there was an error retrieving data.
+    @IntDef(prefix = { "STATUS_LEVEL_" }, value = {
+            STATUS_LEVEL_NONE,
+            STATUS_LEVEL_NO_ISSUES,
+            STATUS_LEVEL_RECOMMENDATION,
+            STATUS_LEVEL_CRITICAL_WARNING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusLevel {}
+
+    /**
+     * Indicates that no status is associated with the information. This status will be reflected in
+     * the UI through the absence of an icon.
+     */
+    public static final int STATUS_LEVEL_NONE = 100;
+
+    /**
+     * Indicates that no issues were detected. This status will be reflected in the UI through a
+     * green icon.
+     */
+    public static final int STATUS_LEVEL_NO_ISSUES = 200;
+
+    /**
+     * Indicates the presence of a medium-status issue which the user is encouraged to act on.
+     * This status will be reflected in the UI through a yellow icon.
+     */
+    public static final int STATUS_LEVEL_RECOMMENDATION = 300;
+
+    /**
+     * Indicates the presence of a critical or urgent safety issue that should be addressed by the
+     * user. This status will be reflected in the UI through a red icon.
+     */
+    public static final int STATUS_LEVEL_CRITICAL_WARNING = 400;
+
+    @NonNull
+    public static final Parcelable.Creator<SafetySourceStatus> CREATOR =
+            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 clickPendingIntent =
+                            requireNonNull(PendingIntent.readPendingIntentOrNullFromParcel(in));
+                    return new SafetySourceStatus(title, summary, statusLevel, clickPendingIntent);
+                }
+
+                @Override
+                public SafetySourceStatus[] newArray(int size) {
+                    return new SafetySourceStatus[size];
+                }
+            };
+
+    @NonNull
+    private final CharSequence mTitle;
+
+    @NonNull
+    private final CharSequence mSummary;
+
+    private final @StatusLevel int mStatusLevel;
+
+    @NonNull final PendingIntent mClickPendingIntent;
+
+    private SafetySourceStatus(@NonNull CharSequence title, @NonNull CharSequence summary,
+            @StatusLevel int statusLevel, @NonNull PendingIntent clickPendingIntent) {
+        this.mTitle = title;
+        this.mSummary = summary;
+        this.mStatusLevel = statusLevel;
+        this.mClickPendingIntent = clickPendingIntent;
+    }
+
+    /** Returns the localized title of the safety source status to be displayed in the UI. */
+    @NonNull
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /** Returns the localized summary of the safety source status to be displayed in the UI. */
+    @NonNull
+    public CharSequence getSummary() {
+        return mSummary;
+    }
+
+    /** Returns the {@link StatusLevel} of the status. */
+    @StatusLevel
+    public int getStatusLevel() {
+        return mStatusLevel;
+    }
+
+    /**
+     * Returns the {@link PendingIntent} that will be invoked when the safety source status UI is
+     * clicked on.
+     */
+    @NonNull
+    public PendingIntent getClickPendingIntent() {
+        return mClickPendingIntent;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        TextUtils.writeToParcel(mTitle, dest, flags);
+        TextUtils.writeToParcel(mSummary, dest, flags);
+        dest.writeInt(mStatusLevel);
+        mClickPendingIntent.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SafetySourceStatus)) return false;
+        SafetySourceStatus that = (SafetySourceStatus) o;
+        return mStatusLevel == that.mStatusLevel
+                && TextUtils.equals(mTitle, that.mTitle)
+                && TextUtils.equals(mSummary, that.mSummary)
+                && mClickPendingIntent.equals(that.mClickPendingIntent);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTitle, mSummary, mStatusLevel, mClickPendingIntent);
+    }
+
+    @Override
+    public String toString() {
+        return "SafetySourceStatus{"
+                + "mTitle="
+                + mTitle
+                + ", mSummary="
+                + mSummary
+                + ", mStatusLevel="
+                + mStatusLevel
+                + ", mClickPendingIntent="
+                + mClickPendingIntent
+                + '}';
+    }
+
+    /** Builder class for {@link SafetySourceStatus}. */
+    public static final class Builder {
+        @NonNull
+        private final CharSequence mTitle;
+
+        @NonNull
+        private final CharSequence mSummary;
+
+        private @StatusLevel final int mStatusLevel;
+
+        @NonNull
+        private final PendingIntent mClickPendingIntent;
+
+        /** Creates a {@link Builder} for a {@link SafetySourceStatus}. */
+        public Builder(@NonNull CharSequence title, @NonNull CharSequence summary,
+                @StatusLevel int statusLevel, @NonNull PendingIntent clickPendingIntent) {
+            this.mTitle = requireNonNull(title);
+            this.mSummary = requireNonNull(summary);
+            this.mStatusLevel = statusLevel;
+            this.mClickPendingIntent = requireNonNull(clickPendingIntent);
+        }
+
+        /** Creates the {@link SafetySourceStatus} defined by this {@link Builder}. */
+        @NonNull
+        public SafetySourceStatus build() {
+            return new SafetySourceStatus(mTitle, mSummary, mStatusLevel, mClickPendingIntent);
+        }
+    }
+}