• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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