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