1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.safetycenter.config;
18 
19 import static android.os.Build.VERSION_CODES.TIRAMISU;
20 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
21 
22 import static java.util.Collections.unmodifiableList;
23 import static java.util.Objects.requireNonNull;
24 
25 import android.annotation.NonNull;
26 import android.annotation.SystemApi;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 
30 import androidx.annotation.RequiresApi;
31 
32 import com.android.modules.utils.build.SdkLevel;
33 
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Objects;
38 import java.util.Set;
39 
40 /**
41  * Data class used to represent the initial configuration of the Safety Center.
42  *
43  * @hide
44  */
45 @SystemApi
46 @RequiresApi(TIRAMISU)
47 public final class SafetyCenterConfig implements Parcelable {
48 
49     @NonNull
50     public static final Creator<SafetyCenterConfig> CREATOR =
51             new Creator<SafetyCenterConfig>() {
52                 @Override
53                 public SafetyCenterConfig createFromParcel(Parcel in) {
54                     List<SafetySourcesGroup> safetySourcesGroups =
55                             requireNonNull(in.createTypedArrayList(SafetySourcesGroup.CREATOR));
56                     Builder builder = new Builder();
57                     for (int i = 0; i < safetySourcesGroups.size(); i++) {
58                         builder.addSafetySourcesGroup(safetySourcesGroups.get(i));
59                     }
60                     return builder.build();
61                 }
62 
63                 @Override
64                 public SafetyCenterConfig[] newArray(int size) {
65                     return new SafetyCenterConfig[size];
66                 }
67             };
68 
69     @NonNull private final List<SafetySourcesGroup> mSafetySourcesGroups;
70 
SafetyCenterConfig(@onNull List<SafetySourcesGroup> safetySourcesGroups)71     private SafetyCenterConfig(@NonNull List<SafetySourcesGroup> safetySourcesGroups) {
72         mSafetySourcesGroups = safetySourcesGroups;
73     }
74 
75     /**
76      * Returns the list of {@link SafetySourcesGroup}s in the Safety Center configuration.
77      *
78      * <p>A Safety Center configuration contains at least one {@link SafetySourcesGroup}.
79      */
80     @NonNull
getSafetySourcesGroups()81     public List<SafetySourcesGroup> getSafetySourcesGroups() {
82         return mSafetySourcesGroups;
83     }
84 
85     @Override
equals(Object o)86     public boolean equals(Object o) {
87         if (this == o) return true;
88         if (!(o instanceof SafetyCenterConfig)) return false;
89         SafetyCenterConfig that = (SafetyCenterConfig) o;
90         return Objects.equals(mSafetySourcesGroups, that.mSafetySourcesGroups);
91     }
92 
93     @Override
hashCode()94     public int hashCode() {
95         return Objects.hash(mSafetySourcesGroups);
96     }
97 
98     @Override
toString()99     public String toString() {
100         return "SafetyCenterConfig{" + "mSafetySourcesGroups=" + mSafetySourcesGroups + '}';
101     }
102 
103     @Override
describeContents()104     public int describeContents() {
105         return 0;
106     }
107 
108     @Override
writeToParcel(@onNull Parcel dest, int flags)109     public void writeToParcel(@NonNull Parcel dest, int flags) {
110         dest.writeTypedList(mSafetySourcesGroups);
111     }
112 
113     /** Builder class for {@link SafetyCenterConfig}. */
114     public static final class Builder {
115 
116         private final List<SafetySourcesGroup> mSafetySourcesGroups = new ArrayList<>();
117 
118         /** Creates a {@link Builder} for a {@link SafetyCenterConfig}. */
Builder()119         public Builder() {}
120 
121         /** Creates a {@link Builder} with the values from the given {@link SafetyCenterConfig}. */
122         @RequiresApi(UPSIDE_DOWN_CAKE)
Builder(@onNull SafetyCenterConfig safetyCenterConfig)123         public Builder(@NonNull SafetyCenterConfig safetyCenterConfig) {
124             if (!SdkLevel.isAtLeastU()) {
125                 throw new UnsupportedOperationException(
126                         "Method not supported on versions lower than UPSIDE_DOWN_CAKE");
127             }
128             requireNonNull(safetyCenterConfig);
129             mSafetySourcesGroups.addAll(safetyCenterConfig.mSafetySourcesGroups);
130         }
131 
132         /**
133          * Adds a {@link SafetySourcesGroup} to the Safety Center configuration.
134          *
135          * <p>A Safety Center configuration must contain at least one {@link SafetySourcesGroup}.
136          */
137         @NonNull
addSafetySourcesGroup(@onNull SafetySourcesGroup safetySourcesGroup)138         public Builder addSafetySourcesGroup(@NonNull SafetySourcesGroup safetySourcesGroup) {
139             mSafetySourcesGroups.add(requireNonNull(safetySourcesGroup));
140             return this;
141         }
142 
143         /**
144          * Creates the {@link SafetyCenterConfig} defined by this {@link Builder}.
145          *
146          * @throws IllegalStateException if any constraint on the Safety Center configuration is
147          *     violated
148          */
149         @NonNull
build()150         public SafetyCenterConfig build() {
151             List<SafetySourcesGroup> safetySourcesGroups =
152                     unmodifiableList(new ArrayList<>(mSafetySourcesGroups));
153             if (safetySourcesGroups.isEmpty()) {
154                 throw new IllegalStateException("No safety sources groups present");
155             }
156             Set<String> safetySourceIds = new HashSet<>();
157             Set<String> safetySourcesGroupsIds = new HashSet<>();
158             int safetySourcesGroupsSize = safetySourcesGroups.size();
159             for (int i = 0; i < safetySourcesGroupsSize; i++) {
160                 SafetySourcesGroup safetySourcesGroup = safetySourcesGroups.get(i);
161                 String groupId = safetySourcesGroup.getId();
162                 if (safetySourcesGroupsIds.contains(groupId)) {
163                     throw new IllegalStateException(
164                             "Duplicate id " + groupId + " among safety sources groups");
165                 }
166                 safetySourcesGroupsIds.add(groupId);
167                 List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
168                 int safetySourcesSize = safetySources.size();
169                 for (int j = 0; j < safetySourcesSize; j++) {
170                     SafetySource staticSafetySource = safetySources.get(j);
171                     String sourceId = staticSafetySource.getId();
172                     if (safetySourceIds.contains(sourceId)) {
173                         throw new IllegalStateException(
174                                 "Duplicate id " + sourceId + " among safety sources");
175                     }
176                     safetySourceIds.add(sourceId);
177                 }
178             }
179             return new SafetyCenterConfig(safetySourcesGroups);
180         }
181     }
182 }
183