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; 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.Objects.requireNonNull; 23 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.SystemApi; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 31 import androidx.annotation.RequiresApi; 32 33 import com.android.modules.utils.build.SdkLevel; 34 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.util.Objects; 38 39 /** 40 * A safety event that may trigger a safety source to set its {@link SafetySourceData}. 41 * 42 * @hide 43 */ 44 @SystemApi 45 @RequiresApi(TIRAMISU) 46 public final class SafetyEvent implements Parcelable { 47 48 /** 49 * Indicates that there has been a change of state for safety source, which may be independent 50 * of Safety Center interactions. 51 */ 52 public static final int SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED = 100; 53 54 /** 55 * Indicates that the safety source performed a data refresh in response to a request from 56 * Safety Center. 57 */ 58 public static final int SAFETY_EVENT_TYPE_REFRESH_REQUESTED = 200; 59 60 /** 61 * Indicates that the safety source successfully completed a resolving {@link 62 * SafetySourceIssue.Action}. 63 */ 64 public static final int SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED = 300; 65 66 /** 67 * Indicates that the safety source failed to complete a resolving {@link 68 * SafetySourceIssue.Action}. 69 */ 70 public static final int SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED = 400; 71 72 /** Indicates that the device's locale changed. */ 73 public static final int SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED = 500; 74 75 /** Indicates that the device was rebooted. */ 76 public static final int SAFETY_EVENT_TYPE_DEVICE_REBOOTED = 600; 77 78 /** 79 * Types of safety events that may trigger a set of a safety source's {@link SafetySourceData}. 80 * 81 * @hide 82 */ 83 @IntDef( 84 prefix = {"SAFETY_EVENT_TYPE_"}, 85 value = { 86 SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED, 87 SAFETY_EVENT_TYPE_REFRESH_REQUESTED, 88 SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED, 89 SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED, 90 SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED, 91 SAFETY_EVENT_TYPE_DEVICE_REBOOTED 92 }) 93 @Retention(RetentionPolicy.SOURCE) 94 public @interface Type {} 95 96 @NonNull 97 public static final Creator<SafetyEvent> CREATOR = 98 new Creator<SafetyEvent>() { 99 @Override 100 public SafetyEvent createFromParcel(Parcel in) { 101 int type = in.readInt(); 102 return new SafetyEvent.Builder(type) 103 .setRefreshBroadcastId(in.readString()) 104 .setSafetySourceIssueId(in.readString()) 105 .setSafetySourceIssueActionId(in.readString()) 106 .build(); 107 } 108 109 @Override 110 public SafetyEvent[] newArray(int size) { 111 return new SafetyEvent[size]; 112 } 113 }; 114 115 @Type private final int mType; 116 @Nullable private final String mRefreshBroadcastId; 117 @Nullable private final String mSafetySourceIssueId; 118 @Nullable private final String mSafetySourceIssueActionId; 119 SafetyEvent( @ype int type, @Nullable String refreshBroadcastId, @Nullable String safetySourceIssueId, @Nullable String safetySourceIssueActionId)120 private SafetyEvent( 121 @Type int type, 122 @Nullable String refreshBroadcastId, 123 @Nullable String safetySourceIssueId, 124 @Nullable String safetySourceIssueActionId) { 125 mType = type; 126 mRefreshBroadcastId = refreshBroadcastId; 127 mSafetySourceIssueId = safetySourceIssueId; 128 mSafetySourceIssueActionId = safetySourceIssueActionId; 129 } 130 131 /** Returns the type of the safety event. */ 132 @Type getType()133 public int getType() { 134 return mType; 135 } 136 137 /** 138 * Returns an optional id provided by Safety Center when requesting a refresh, through {@link 139 * SafetyCenterManager#EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID}. 140 * 141 * <p>This will only be relevant for events of type {@link 142 * #SAFETY_EVENT_TYPE_REFRESH_REQUESTED}. 143 * 144 * @see #getType() 145 */ 146 @Nullable getRefreshBroadcastId()147 public String getRefreshBroadcastId() { 148 return mRefreshBroadcastId; 149 } 150 151 /** 152 * Returns the id of the {@link SafetySourceIssue} this event is associated with (if any). 153 * 154 * <p>This will only be relevant for events of type {@link 155 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or {@link 156 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. 157 * 158 * @see #getType() 159 * @see SafetySourceIssue#getId() 160 */ 161 @Nullable getSafetySourceIssueId()162 public String getSafetySourceIssueId() { 163 return mSafetySourceIssueId; 164 } 165 166 /** 167 * Returns the id of the {@link SafetySourceIssue.Action} this event is associated with (if 168 * any). 169 * 170 * <p>This will only be relevant for events of type {@link 171 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or {@link 172 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. 173 * 174 * @see #getType() 175 * @see SafetySourceIssue.Action#getId() 176 */ 177 @Nullable getSafetySourceIssueActionId()178 public String getSafetySourceIssueActionId() { 179 return mSafetySourceIssueActionId; 180 } 181 182 @Override describeContents()183 public int describeContents() { 184 return 0; 185 } 186 187 @Override writeToParcel(@onNull Parcel dest, int flags)188 public void writeToParcel(@NonNull Parcel dest, int flags) { 189 dest.writeInt(mType); 190 dest.writeString(mRefreshBroadcastId); 191 dest.writeString(mSafetySourceIssueId); 192 dest.writeString(mSafetySourceIssueActionId); 193 } 194 195 @Override equals(Object o)196 public boolean equals(Object o) { 197 if (this == o) return true; 198 if (!(o instanceof SafetyEvent)) return false; 199 SafetyEvent that = (SafetyEvent) o; 200 return mType == that.mType 201 && Objects.equals(mRefreshBroadcastId, that.mRefreshBroadcastId) 202 && Objects.equals(mSafetySourceIssueId, that.mSafetySourceIssueId) 203 && Objects.equals(mSafetySourceIssueActionId, that.mSafetySourceIssueActionId); 204 } 205 206 @Override hashCode()207 public int hashCode() { 208 return Objects.hash( 209 mType, mRefreshBroadcastId, mSafetySourceIssueId, mSafetySourceIssueActionId); 210 } 211 212 @Override toString()213 public String toString() { 214 return "SafetyEvent{" 215 + "mType=" 216 + mType 217 + ", mRefreshBroadcastId=" 218 + mRefreshBroadcastId 219 + ", mSafetySourceIssueId=" 220 + mSafetySourceIssueId 221 + ", mSafetySourceIssueActionId=" 222 + mSafetySourceIssueActionId 223 + '}'; 224 } 225 226 /** Builder class for {@link SafetyEvent}. */ 227 public static final class Builder { 228 229 @Type private final int mType; 230 @Nullable private String mRefreshBroadcastId; 231 @Nullable private String mSafetySourceIssueId; 232 @Nullable private String mSafetySourceIssueActionId; 233 234 /** Creates a {@link Builder} for {@link SafetyEvent}. */ Builder(@ype int type)235 public Builder(@Type int type) { 236 mType = validateType(type); 237 } 238 239 /** Creates a {@link Builder} with the values from the given {@link SafetyEvent}. */ 240 @RequiresApi(UPSIDE_DOWN_CAKE) Builder(@onNull SafetyEvent safetyEvent)241 public Builder(@NonNull SafetyEvent safetyEvent) { 242 if (!SdkLevel.isAtLeastU()) { 243 throw new UnsupportedOperationException( 244 "Method not supported on versions lower than UPSIDE_DOWN_CAKE"); 245 } 246 requireNonNull(safetyEvent); 247 mType = safetyEvent.mType; 248 mRefreshBroadcastId = safetyEvent.mRefreshBroadcastId; 249 mSafetySourceIssueId = safetyEvent.mSafetySourceIssueId; 250 mSafetySourceIssueActionId = safetyEvent.mSafetySourceIssueActionId; 251 } 252 253 /** 254 * Sets an optional broadcast id provided by Safety Center when requesting a refresh, 255 * through {@link SafetyCenterManager#EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID}. 256 * 257 * <p>This will only be relevant for events of type {@link 258 * #SAFETY_EVENT_TYPE_REFRESH_REQUESTED}. 259 * 260 * @see #getType() 261 */ 262 @NonNull setRefreshBroadcastId(@ullable String refreshBroadcastId)263 public Builder setRefreshBroadcastId(@Nullable String refreshBroadcastId) { 264 mRefreshBroadcastId = refreshBroadcastId; 265 return this; 266 } 267 268 /** 269 * Sets the id of the {@link SafetySourceIssue} this event is associated with (if any). 270 * 271 * <p>This will only be relevant for events of type {@link 272 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or {@link 273 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. 274 * 275 * @see #getType() 276 * @see SafetySourceIssue#getId() 277 */ 278 @NonNull setSafetySourceIssueId(@ullable String safetySourceIssueId)279 public Builder setSafetySourceIssueId(@Nullable String safetySourceIssueId) { 280 mSafetySourceIssueId = safetySourceIssueId; 281 return this; 282 } 283 284 /** 285 * Sets the id of the {@link SafetySourceIssue.Action} this event is associated with (if 286 * any). 287 * 288 * <p>This will only be relevant for events of type {@link 289 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED} or {@link 290 * #SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED}. 291 * 292 * @see #getType() 293 * @see SafetySourceIssue.Action#getId() 294 */ 295 @NonNull setSafetySourceIssueActionId(@ullable String safetySourceIssueActionId)296 public Builder setSafetySourceIssueActionId(@Nullable String safetySourceIssueActionId) { 297 mSafetySourceIssueActionId = safetySourceIssueActionId; 298 return this; 299 } 300 301 /** Creates the {@link SafetyEvent} represented by this {@link Builder}. */ 302 @NonNull build()303 public SafetyEvent build() { 304 switch (mType) { 305 case SAFETY_EVENT_TYPE_REFRESH_REQUESTED: 306 if (mRefreshBroadcastId == null) { 307 throw new IllegalArgumentException( 308 "Missing refresh broadcast id for refresh requested safety event"); 309 } 310 break; 311 case SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: 312 case SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: 313 if (mSafetySourceIssueId == null) { 314 throw new IllegalArgumentException( 315 "Missing issue id for resolving action safety event: " + mType); 316 } 317 if (mSafetySourceIssueActionId == null) { 318 throw new IllegalArgumentException( 319 "Missing issue action id for resolving action safety event: " 320 + mType); 321 } 322 break; 323 case SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: 324 case SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: 325 case SAFETY_EVENT_TYPE_DEVICE_REBOOTED: 326 default: 327 } 328 return new SafetyEvent( 329 mType, mRefreshBroadcastId, mSafetySourceIssueId, mSafetySourceIssueActionId); 330 } 331 } 332 333 @Type validateType(int value)334 private static int validateType(int value) { 335 switch (value) { 336 case SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: 337 case SAFETY_EVENT_TYPE_REFRESH_REQUESTED: 338 case SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: 339 case SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: 340 case SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: 341 case SAFETY_EVENT_TYPE_DEVICE_REBOOTED: 342 return value; 343 default: 344 } 345 throw new IllegalArgumentException("Unexpected Type for SafetyEvent: " + value); 346 } 347 } 348