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