1 /*
2  * Copyright (C) 2020 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.testapi;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.car.user.CarUserManager;
23 import android.car.user.CarUserManager.UserLifecycleEvent;
24 import android.car.user.CarUserManager.UserLifecycleListener;
25 import android.util.Log;
26 
27 import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.Preconditions;
30 
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.TimeUnit;
36 import java.util.stream.Collectors;
37 
38 /**
39  * {@link UserLifecycleListener} that blocks until the proper events are received.
40  *
41  * <p>It can be used in 2 "modes":
42  *
43  * <ul>
44  *   <li>{@link #forAnyEvent()}: it blocks (through the {@link #waitForAnyEvent()} call) until any
45  *   any event is received. It doesn't allow any customization (other than
46  *   {@link Builder#setTimeout(long)}).
47  *   <li>{@link #forSpecificEvents()}: it blocks (through the {@link #waitForEvents()} call) until
48  *   all events specified by the {@link Builder} are received.
49  * </ul>
50  */
51 public final class BlockingUserLifecycleListener implements UserLifecycleListener {
52 
53     private static final String TAG = BlockingUserLifecycleListener.class.getSimpleName();
54 
55     private static final long DEFAULT_TIMEOUT_MS = 2_000;
56 
57     private static int sNextId;
58 
59     private final Object mLock = new Object();
60 
61     private final CountDownLatch mLatch = new CountDownLatch(1);
62 
63     @GuardedBy("mLock")
64     private final List<UserLifecycleEvent> mAllReceivedEvents = new ArrayList<>();
65     @GuardedBy("mLock")
66     private final List<UserLifecycleEvent> mExpectedEventsReceived = new ArrayList<>();
67 
68     @UserLifecycleEventType
69     private final List<Integer> mExpectedEventTypes;
70 
71     @UserLifecycleEventType
72     private final List<Integer> mExpectedEventTypesLeft;
73 
74     @UserIdInt
75     @Nullable
76     private final Integer mForUserId;
77 
78     @UserIdInt
79     @Nullable
80     private final Integer mForPreviousUserId;
81 
82     private final long mTimeoutMs;
83 
84     private final int mId = ++sNextId;
85 
BlockingUserLifecycleListener(Builder builder)86     private BlockingUserLifecycleListener(Builder builder) {
87         mExpectedEventTypes = Collections
88                 .unmodifiableList(new ArrayList<>(builder.mExpectedEventTypes));
89         mExpectedEventTypesLeft = builder.mExpectedEventTypes;
90         mTimeoutMs = builder.mTimeoutMs;
91         mForUserId = builder.mForUserId;
92         mForPreviousUserId = builder.mForPreviousUserId;
93         Log.d(TAG, "constructor: " + this);
94     }
95 
96     /**
97      * Creates a builder for tests that need to wait for an arbitrary event.
98      */
99     @NonNull
forAnyEvent()100     public static Builder forAnyEvent() {
101         return new Builder(/* forAnyEvent= */ true);
102     }
103 
104     /**
105      * Creates a builder for tests that need to wait for specific events.
106      */
107     @NonNull
forSpecificEvents()108     public static Builder forSpecificEvents() {
109         return new Builder(/* forAnyEvent= */ false);
110     }
111 
112     /**
113      * Builder for a customized {@link BlockingUserLifecycleListener} instance.
114      */
115     public static final class Builder {
116         private long mTimeoutMs = DEFAULT_TIMEOUT_MS;
117         private final boolean mForAnyEvent;
118 
Builder(boolean forAnyEvent)119         private Builder(boolean forAnyEvent) {
120             mForAnyEvent = forAnyEvent;
121         }
122 
123         @UserLifecycleEventType
124         private final List<Integer> mExpectedEventTypes = new ArrayList<>();
125 
126         @UserIdInt
127         @Nullable
128         private Integer mForUserId;
129 
130         @UserIdInt
131         @Nullable
132         private Integer mForPreviousUserId;
133 
134         /**
135          * Sets the timeout.
136          */
setTimeout(long timeoutMs)137         public Builder setTimeout(long timeoutMs) {
138             mTimeoutMs = timeoutMs;
139             return this;
140         }
141 
142         /**
143          * Sets the expected type - once the given event is received, the listener will unblock.
144          *
145          * @throws IllegalStateException if builder is {@link #forAnyEvent}.
146          * @throws IllegalArgumentException if the expected type was already added.
147          */
addExpectedEvent(@serLifecycleEventType int eventType)148         public Builder addExpectedEvent(@UserLifecycleEventType int eventType) {
149             assertNotForAnyEvent();
150             mExpectedEventTypes.add(eventType);
151             return this;
152         }
153 
154         /**
155          * Filters received events just for the given user.
156          */
forUser(@serIdInt int userId)157         public Builder forUser(@UserIdInt int userId) {
158             assertNotForAnyEvent();
159             mForUserId = userId;
160             return this;
161         }
162 
163         /**
164          * Filters received events just for the given previous user.
165          */
forPreviousUser(@serIdInt int userId)166         public Builder forPreviousUser(@UserIdInt int userId) {
167             assertNotForAnyEvent();
168             mForPreviousUserId = userId;
169             return this;
170         }
171 
172         /**
173          * Builds a new instance.
174          */
175         @NonNull
build()176         public BlockingUserLifecycleListener build() {
177             return new BlockingUserLifecycleListener(Builder.this);
178         }
179 
assertNotForAnyEvent()180         private void assertNotForAnyEvent() {
181             Preconditions.checkState(!mForAnyEvent, "not allowed forAnyEvent()");
182         }
183     }
184 
185     @Override
onEvent(UserLifecycleEvent event)186     public void onEvent(UserLifecycleEvent event) {
187         synchronized (mLock) {
188             Log.d(TAG, "onEvent(): expecting=" + mExpectedEventTypesLeft + ", received=" + event);
189 
190             mAllReceivedEvents.add(event);
191 
192             if (expectingSpecificUser() && event.getUserId() != mForUserId) {
193                 Log.w(TAG, "ignoring event for different user (expecting " + mForUserId + ")");
194                 return;
195             }
196 
197             if (expectingSpecificPreviousUser()
198                     && event.getPreviousUserId() != mForPreviousUserId) {
199                 Log.w(TAG, "ignoring event for different previous user (expecting "
200                         + mForPreviousUserId + ")");
201                 return;
202             }
203 
204             Integer actualType = event.getEventType();
205             boolean removed = mExpectedEventTypesLeft.remove(actualType);
206             if (removed) {
207                 Log.v(TAG, "event removed; still expecting for "
208                         + toString(mExpectedEventTypesLeft));
209                 mExpectedEventsReceived.add(event);
210             } else {
211                 Log.v(TAG, "event not removed");
212             }
213 
214             if (mExpectedEventTypesLeft.isEmpty() && mLatch.getCount() == 1) {
215                 Log.d(TAG, "all expected events received, counting down " + mLatch);
216                 mLatch.countDown();
217             }
218         }
219     }
220 
221     /**
222      * Blocks until any event is received, and returns it.
223      *
224      * @throws IllegalStateException if listener was built using {@link #forSpecificEvents()}.
225      * @throws IllegalStateException if it times out before any event is received.
226      * @throws InterruptedException if interrupted before any event is received.
227      */
228     @Nullable
waitForAnyEvent()229     public UserLifecycleEvent waitForAnyEvent() throws InterruptedException {
230         Preconditions.checkState(isForAnyEvent(),
231                 "cannot call waitForEvent() when built with expected events");
232         waitForExpectedEvents();
233 
234         UserLifecycleEvent event;
235         synchronized (mLock) {
236             event = mAllReceivedEvents.isEmpty() ? null : mAllReceivedEvents.get(0);
237             Log.v(TAG, "waitForAnyEvent(): returning " + event);
238         }
239         return event;
240     }
241 
242     /**
243      * Blocks until the events specified in the {@link Builder} are received, and returns them.
244      *
245      * @throws IllegalStateException if listener was built without any call to
246      * {@link Builder#addExpectedEvent(int)} or using {@link #forAnyEvent().
247      * @throws IllegalStateException if it times out before all specified events are received.
248      * @throws InterruptedException if interrupted before all specified events are received.
249      */
250     @NonNull
waitForEvents()251     public List<UserLifecycleEvent> waitForEvents() throws InterruptedException {
252         Preconditions.checkState(!isForAnyEvent(),
253                 "cannot call waitForEvents() when built without specific expected events");
254         waitForExpectedEvents();
255         List<UserLifecycleEvent> events;
256         synchronized (mLock) {
257             events = mExpectedEventsReceived;
258         }
259         Log.v(TAG, "waitForEvents(): returning " + events);
260         return events;
261     }
262 
263     /**
264      * Gets a list with all received events until now.
265      */
266     @NonNull
getAllReceivedEvents()267     public List<UserLifecycleEvent> getAllReceivedEvents() {
268         Preconditions.checkState(!isForAnyEvent(),
269                 "cannot call getAllReceivedEvents() when built without specific expected events");
270         synchronized (mLock) {
271             return Collections.unmodifiableList(new ArrayList<>(mAllReceivedEvents));
272         }
273     }
274 
275     @Override
toString()276     public String toString() {
277         return "[" + getClass().getSimpleName() + ": " + stateToString() + "]";
278     }
279 
280     @NonNull
stateToString()281     private String stateToString() {
282         synchronized (mLock) {
283             return "id=" + mId + ",timeout=" + mTimeoutMs + "ms"
284                     + ",expectedEventTypes=" + toString(mExpectedEventTypes)
285                     + ",expectedEventTypesLeft=" + toString(mExpectedEventTypesLeft)
286                     + (expectingSpecificUser() ? ",forUser=" + mForUserId : "")
287                     + (expectingSpecificPreviousUser() ? ",forPrevUser=" + mForPreviousUserId : "")
288                     + ",received=" + mAllReceivedEvents
289                     + ",waiting=" + mExpectedEventTypesLeft;
290         }
291     }
292 
waitForExpectedEvents()293     private void waitForExpectedEvents() throws InterruptedException {
294         if (!mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS)) {
295             String errorMessage = "did not receive all expected events (" + stateToString() + ")";
296             Log.e(TAG, errorMessage);
297             throw new IllegalStateException(errorMessage);
298         }
299     }
300 
301     @NonNull
toString(@onNull List<Integer> eventTypes)302     private static String toString(@NonNull List<Integer> eventTypes) {
303         return eventTypes.stream()
304                 .map((i) -> CarUserManager.lifecycleEventTypeToString(i))
305                 .collect(Collectors.toList())
306                 .toString();
307     }
308 
isForAnyEvent()309     private boolean isForAnyEvent() {
310         return mExpectedEventTypes.isEmpty();
311     }
312 
expectingSpecificUser()313     private boolean expectingSpecificUser() {
314         return mForUserId != null;
315     }
316 
expectingSpecificPreviousUser()317     private boolean expectingSpecificPreviousUser() {
318         return mForPreviousUserId != null;
319     }
320 }
321