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