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.car.user; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEBUGGING_CODE; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.app.ActivityManager; 25 import android.car.annotation.AddedInOrBefore; 26 import android.car.annotation.ApiRequirements; 27 import android.car.user.CarUserManager.UserLifecycleEvent; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.UserHandle; 31 import android.util.ArraySet; 32 33 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 34 import com.android.car.internal.common.CommonConstants; 35 import com.android.car.internal.util.ArrayUtils; 36 import com.android.car.internal.util.DataClass; 37 import com.android.internal.annotations.VisibleForTesting; 38 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Objects; 42 43 /** 44 * Filter for user lifecycle event receivers to selectively receive events. 45 * 46 * @hide 47 */ 48 @DataClass( 49 genParcelable = true, 50 genGetters = false, 51 genConstructor = false, 52 genEqualsHashCode = true) 53 @SystemApi 54 public final class UserLifecycleEventFilter implements Parcelable { 55 56 private static final int USER_CURRENT = UserHandle.CURRENT.getIdentifier(); 57 58 private final @Nullable int[] mEventTypes; 59 private final @Nullable int[] mUserIds; 60 61 // TODO(b/216850516): Manually implemented these getters, as codegen does not auto-generate 62 // the @VisibleForTesting annotation. 63 /** @hide */ 64 @VisibleForTesting 65 @AddedInOrBefore(majorVersion = 33) getEventTypes()66 public @Nullable int[] getEventTypes() { 67 return mEventTypes; 68 } 69 70 /** @hide */ 71 @VisibleForTesting 72 @AddedInOrBefore(majorVersion = 33) getUserIds()73 public @Nullable int[] getUserIds() { 74 return mUserIds; 75 } 76 77 /** 78 * Checks if the {@code event} passes this filter. 79 * 80 * @param event user lifecycle event to check. 81 * @return {@code true} if the event passes this filter. 82 */ 83 @AddedInOrBefore(majorVersion = 33) apply(@onNull UserLifecycleEvent event)84 public boolean apply(@NonNull UserLifecycleEvent event) { 85 Objects.requireNonNull(event, "event cannot be null"); 86 87 return matchUserId(event) && matchEventType(event); 88 } 89 matchUserId(UserLifecycleEvent event)90 private boolean matchUserId(UserLifecycleEvent event) { 91 // Filter by the user id. 92 if (mUserIds == null) { 93 return true; 94 } 95 for (int userId : mUserIds) { 96 if (userId == USER_CURRENT) { 97 userId = ActivityManager.getCurrentUser(); 98 } 99 if (userId == event.getUserId() || userId == event.getPreviousUserId()) { 100 return true; 101 } 102 } 103 return false; 104 } 105 matchEventType(UserLifecycleEvent event)106 private boolean matchEventType(UserLifecycleEvent event) { 107 // Filter by the event type. 108 if (mEventTypes == null) { 109 return true; 110 } 111 for (int eventType : mEventTypes) { 112 if (eventType == event.getEventType()) { 113 return true; 114 } 115 } 116 return false; 117 } 118 119 @Override 120 @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE) toString()121 public String toString() { 122 StringBuilder builder = new StringBuilder("UserLifecycleEventFilter{eventTypes="); 123 if (mEventTypes == null) { 124 builder.append("ANY"); 125 } else { 126 builder.append('['); 127 for (int eventType : mEventTypes) { 128 builder.append(CarUserManager.lifecycleEventTypeToString(eventType)); 129 builder.append(','); 130 } 131 builder.setCharAt(builder.length() - 1, ']'); 132 } 133 builder.append(",userIds="); 134 if (mUserIds == null) { 135 builder.append("ANY"); 136 } else { 137 builder.append('['); 138 for (int userId : mUserIds) { 139 if (userId == USER_CURRENT) { 140 builder.append("CURRENT"); 141 } else { 142 builder.append(userId); 143 } 144 builder.append(','); 145 } 146 builder.setCharAt(builder.length() - 1, ']'); 147 } 148 149 return builder.append('}').toString(); 150 } 151 152 /** 153 * Builder for {@link UserLifecycleEventFilter}. 154 * 155 */ 156 public static final class Builder { 157 private final ArraySet<Integer> mEventTypes = new ArraySet<>(); 158 private final ArraySet<Integer> mUserIds = new ArraySet<>(); 159 160 /** Adds an event type that this filter passes. */ 161 @SuppressWarnings("[MissingGetterMatchingBuilder]") 162 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 163 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 164 @NonNull addEventType(@ommonConstants.UserLifecycleEventType int eventType)165 public Builder addEventType(@CommonConstants.UserLifecycleEventType int eventType) { 166 mEventTypes.add(eventType); 167 return this; 168 } 169 170 /** 171 * Adds a user that this filter passes. 172 * * 173 * @param userHandle a user handle. {@code UserHandle.CURRENT} is supported but no other 174 * special value is supported. When {@code UserHandle.CURRENT} is used, 175 * the filter will use the current user at the time of the event, not the 176 * current user at the time of the filter creation. 177 * @throws IllegalArgumentException if the specified userHandle is not supported. 178 */ 179 @SuppressWarnings({"[MissingGetterMatchingBuilder]", "[UserHandleName]"}) 180 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 181 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 182 @NonNull addUser(@onNull UserHandle userHandle)183 public Builder addUser(@NonNull UserHandle userHandle) { 184 int userId = userHandle.getIdentifier(); 185 if (userId < 0 && userId != USER_CURRENT) { 186 throw new IllegalArgumentException("Unsupported user handle: " + userHandle); 187 } 188 mUserIds.add(userHandle.getIdentifier()); 189 return this; 190 } 191 192 /** Builds and returns a {@link UserLifecycleEventFilter}. */ 193 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 194 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 195 @NonNull build()196 public UserLifecycleEventFilter build() { 197 if (mEventTypes.isEmpty() && mUserIds.isEmpty()) { 198 throw new IllegalStateException("Cannot build an empty filter."); 199 } 200 return new UserLifecycleEventFilter(this); 201 } 202 } 203 UserLifecycleEventFilter(Builder builder)204 private UserLifecycleEventFilter(Builder builder) { 205 // Keep the arrays in sorted order so that equals() and hashCode() work as intended. 206 mEventTypes = toSortedArray(builder.mEventTypes); 207 mUserIds = toSortedArray(builder.mUserIds); 208 } 209 toSortedArray(ArraySet<Integer> arraySet)210 private @Nullable int[] toSortedArray(ArraySet<Integer> arraySet) { 211 if (arraySet.isEmpty()) { 212 return null; 213 } 214 int[] result = ArrayUtils.convertToIntArray(new ArrayList<>(arraySet)); 215 Arrays.sort(result); 216 return result; 217 } 218 219 220 221 // Code below generated by codegen v1.0.23. 222 // 223 // DO NOT MODIFY! 224 // CHECKSTYLE:OFF Generated code 225 // 226 // To regenerate run: 227 // $ codegen $ANDROID_BUILD_TOP/packages/services/Car/car-lib/src/android/car/user/UserLifecycleEventFilter.java 228 // Added AddedInOrBefore or ApiRequirement Annotation manually 229 // 230 // To exclude the generated code from IntelliJ auto-formatting enable (one-time): 231 // Settings > Editor > Code Style > Formatter Control 232 //@formatter:off 233 234 235 @Override 236 @DataClass.Generated.Member equals(@ullable Object o)237 public boolean equals(@Nullable Object o) { 238 // You can override field equality logic by defining either of the methods like: 239 // boolean fieldNameEquals(UserLifecycleEventFilter other) { ... } 240 // boolean fieldNameEquals(FieldType otherValue) { ... } 241 242 if (this == o) return true; 243 if (o == null || getClass() != o.getClass()) return false; 244 @SuppressWarnings("unchecked") 245 UserLifecycleEventFilter that = (UserLifecycleEventFilter) o; 246 //noinspection PointlessBooleanExpression 247 return true 248 && Arrays.equals(mEventTypes, that.mEventTypes) 249 && Arrays.equals(mUserIds, that.mUserIds); 250 } 251 252 @Override 253 @DataClass.Generated.Member hashCode()254 public int hashCode() { 255 // You can override field hashCode logic by defining methods like: 256 // int fieldNameHashCode() { ... } 257 258 int _hash = 1; 259 _hash = 31 * _hash + Arrays.hashCode(mEventTypes); 260 _hash = 31 * _hash + Arrays.hashCode(mUserIds); 261 return _hash; 262 } 263 264 @Override 265 @DataClass.Generated.Member 266 @AddedInOrBefore(majorVersion = 33) writeToParcel(@onNull Parcel dest, int flags)267 public void writeToParcel(@NonNull Parcel dest, int flags) { 268 // You can override field parcelling by defining methods like: 269 // void parcelFieldName(Parcel dest, int flags) { ... } 270 271 byte flg = 0; 272 if (mEventTypes != null) flg |= 0x1; 273 if (mUserIds != null) flg |= 0x2; 274 dest.writeByte(flg); 275 if (mEventTypes != null) dest.writeIntArray(mEventTypes); 276 if (mUserIds != null) dest.writeIntArray(mUserIds); 277 } 278 279 @Override 280 @DataClass.Generated.Member 281 @AddedInOrBefore(majorVersion = 33) describeContents()282 public int describeContents() { return 0; } 283 284 /** @hide */ 285 @SuppressWarnings({"unchecked", "RedundantCast"}) 286 @DataClass.Generated.Member UserLifecycleEventFilter(@onNull Parcel in)287 /* package-private */ UserLifecycleEventFilter(@NonNull Parcel in) { 288 // You can override field unparcelling by defining methods like: 289 // static FieldType unparcelFieldName(Parcel in) { ... } 290 291 byte flg = in.readByte(); 292 int[] eventTypes = (flg & 0x1) == 0 ? null : in.createIntArray(); 293 int[] userIds = (flg & 0x2) == 0 ? null : in.createIntArray(); 294 295 this.mEventTypes = eventTypes; 296 this.mUserIds = userIds; 297 298 // onConstructed(); // You can define this method to get a callback 299 } 300 301 @DataClass.Generated.Member 302 @AddedInOrBefore(majorVersion = 33) 303 public static final @NonNull Parcelable.Creator<UserLifecycleEventFilter> CREATOR 304 = new Parcelable.Creator<UserLifecycleEventFilter>() { 305 @Override 306 public UserLifecycleEventFilter[] newArray(int size) { 307 return new UserLifecycleEventFilter[size]; 308 } 309 310 @Override 311 public UserLifecycleEventFilter createFromParcel(@NonNull Parcel in) { 312 return new UserLifecycleEventFilter(in); 313 } 314 }; 315 316 @DataClass.Generated( 317 time = 1643409624655L, 318 codegenVersion = "1.0.23", 319 sourceFile = "packages/services/Car/car-lib/src/android/car/user/UserLifecycleEventFilter.java", 320 inputSignatures = "private static final int USER_CURRENT\nprivate final @android.annotation.Nullable int[] mEventTypes\nprivate final @android.annotation.Nullable int[] mUserIds\npublic @com.android.internal.annotations.VisibleForTesting @android.annotation.Nullable int[] getEventTypes()\npublic @com.android.internal.annotations.VisibleForTesting @android.annotation.Nullable int[] getUserIds()\npublic boolean apply(android.car.user.CarUserManager.UserLifecycleEvent)\nprivate boolean matchUserId(android.car.user.CarUserManager.UserLifecycleEvent)\nprivate boolean matchEventType(android.car.user.CarUserManager.UserLifecycleEvent)\npublic @java.lang.Override @com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport java.lang.String toString()\nprivate @android.annotation.Nullable int[] toSortedArray(android.util.ArraySet<java.lang.Integer>)\nclass UserLifecycleEventFilter extends java.lang.Object implements [android.os.Parcelable]\nprivate final android.util.ArraySet<java.lang.Integer> mEventTypes\nprivate final android.util.ArraySet<java.lang.Integer> mUserIds\npublic android.car.user.UserLifecycleEventFilter.Builder addEventType(int)\npublic android.car.user.UserLifecycleEventFilter.Builder addUser(android.os.UserHandle)\npublic android.car.user.UserLifecycleEventFilter build()\nclass Builder extends java.lang.Object implements []\n@com.android.car.internal.util.DataClass(genParcelable=true, genGetters=false, genConstructor=false, genEqualsHashCode=true)") 321 @Deprecated __metadata()322 private void __metadata() {} 323 324 325 //@formatter:on 326 // End of generated code 327 328 } 329