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