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 package android.car.test.mocks; 17 18 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 19 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.mockito.Matchers.anyString; 23 import static org.mockito.Mockito.when; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.UserIdInt; 28 import android.app.Activity; 29 import android.app.ActivityManager; 30 import android.car.test.util.UserTestingHelper; 31 import android.car.test.util.UserTestingHelper.UserInfoBuilder; 32 import android.car.test.util.Visitor; 33 import android.content.Context; 34 import android.content.pm.PackageManager; 35 import android.content.pm.UserInfo; 36 import android.content.pm.UserInfo.UserInfoFlag; 37 import android.os.Binder; 38 import android.os.Handler; 39 import android.os.IBinder; 40 import android.os.IInterface; 41 import android.os.Looper; 42 import android.os.ServiceManager; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.os.UserManager.RemoveResult; 46 import android.util.Log; 47 48 import java.util.List; 49 import java.util.Objects; 50 import java.util.concurrent.Callable; 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.atomic.AtomicReference; 53 import java.util.stream.Collectors; 54 55 /** 56 * Provides common Mockito calls for core Android classes. 57 */ 58 public final class AndroidMockitoHelper { 59 60 private static final String TAG = AndroidMockitoHelper.class.getSimpleName(); 61 62 /** 63 * Mocks a call to {@link ActivityManager#getCurrentUser()}. 64 * 65 * <p><b>Note: </b>it must be made inside a 66 * {@link com.android.dx.mockito.inline.extended.StaticMockitoSession} built with 67 * {@code spyStatic(ActivityManager.class)}. 68 * 69 * @param userId result of such call 70 */ mockAmGetCurrentUser(@serIdInt int userId)71 public static void mockAmGetCurrentUser(@UserIdInt int userId) { 72 doReturn(userId).when(() -> ActivityManager.getCurrentUser()); 73 } 74 75 /** 76 * Mocks a call to {@link UserManager#isHeadlessSystemUserMode()}. 77 * 78 * <p><b>Note: </b>it must be made inside a 79 * {@linkcom.android.dx.mockito.inline.extended.StaticMockitoSession} built with 80 * {@code spyStatic(UserManager.class)}. 81 * 82 * @param mode result of such call 83 */ mockUmIsHeadlessSystemUserMode(boolean mode)84 public static void mockUmIsHeadlessSystemUserMode(boolean mode) { 85 doReturn(mode).when(() -> UserManager.isHeadlessSystemUserMode()); 86 } 87 88 /** 89 * Mocks {@code UserManager#getUserInfo(userId)} to return a {@link UserInfo} with the given 90 * {@code flags}. 91 */ 92 @NonNull mockUmGetUserInfo(@onNull UserManager um, @UserIdInt int userId, @UserInfoFlag int flags)93 public static UserInfo mockUmGetUserInfo(@NonNull UserManager um, @UserIdInt int userId, 94 @UserInfoFlag int flags) { 95 Objects.requireNonNull(um); 96 UserInfo user = new UserTestingHelper.UserInfoBuilder(userId).setFlags(flags).build(); 97 mockUmGetUserInfo(um, user); 98 return user; 99 } 100 101 /** 102 * Mocks {@code UserManager.getUserInfo(userId)} to return the given {@link UserInfo}. 103 */ 104 @NonNull mockUmGetUserInfo(@onNull UserManager um, @NonNull UserInfo user)105 public static void mockUmGetUserInfo(@NonNull UserManager um, @NonNull UserInfo user) { 106 when(um.getUserInfo(user.id)).thenReturn(user); 107 } 108 109 /** 110 * Mocks {@code UserManager#getUserInfo(userId)} when the {@code userId} is the system user's. 111 */ 112 @NonNull mockUmGetSystemUser(@onNull UserManager um)113 public static void mockUmGetSystemUser(@NonNull UserManager um) { 114 UserInfo user = new UserTestingHelper.UserInfoBuilder(UserHandle.USER_SYSTEM) 115 .setFlags(UserInfo.FLAG_SYSTEM).build(); 116 when(um.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(user); 117 } 118 119 /** 120 * Mocks {@code UserManager#getAliveUsers()} to return the given users. 121 */ mockUmGetAliveUsers(@onNull UserManager um, @NonNull UserInfo... users)122 public static void mockUmGetAliveUsers(@NonNull UserManager um, @NonNull UserInfo... users) { 123 Objects.requireNonNull(um); 124 when(um.getAliveUsers()).thenReturn(UserTestingHelper.toList(users)); 125 } 126 127 /** 128 * Mocks {@code UserManager#getAliveUsers()} to return the simple users with the given ids. 129 */ mockUmGetAliveUsers(@onNull UserManager um, @NonNull @UserIdInt int... userIds)130 public static void mockUmGetAliveUsers(@NonNull UserManager um, 131 @NonNull @UserIdInt int... userIds) { 132 List<UserInfo> users = UserTestingHelper.newUsers(userIds); 133 when(um.getAliveUsers()).thenReturn(users); 134 } 135 136 /** 137 * Mocks {@code UserManager#getUserHandles()} to return the simple users with the given ids. 138 */ mockUmGetUserHandles(@onNull UserManager um, boolean excludeDying, @NonNull @UserIdInt int... userIds)139 public static void mockUmGetUserHandles(@NonNull UserManager um, boolean excludeDying, 140 @NonNull @UserIdInt int... userIds) { 141 List<UserHandle> result = UserTestingHelper.newUsers(userIds).stream().map( 142 UserInfo::getUserHandle).collect(Collectors.toList()); 143 when(um.getUserHandles(excludeDying)).thenReturn(result); 144 } 145 146 /** 147 * Mocks {@code UserManager#getUsers(excludePartial, excludeDying, excludeDying)} to return the 148 * given users. 149 */ mockUmGetUsers(@onNull UserManager um, boolean excludePartial, boolean excludeDying, boolean excludePreCreated, @NonNull List<UserInfo> users)150 public static void mockUmGetUsers(@NonNull UserManager um, boolean excludePartial, 151 boolean excludeDying, boolean excludePreCreated, @NonNull List<UserInfo> users) { 152 Objects.requireNonNull(um); 153 when(um.getUsers(excludePartial, excludeDying, excludePreCreated)).thenReturn(users); 154 } 155 156 /** 157 * Mocks a call to {@code UserManager#getUsers()}, which includes dying users. 158 */ mockUmGetAllUsers(@onNull UserManager um, @NonNull UserInfo... userInfos)159 public static void mockUmGetAllUsers(@NonNull UserManager um, @NonNull UserInfo... userInfos) { 160 when(um.getUsers()).thenReturn(UserTestingHelper.toList(userInfos)); 161 } 162 163 /** 164 * Mocks a call to {@code UserManager#isUserRunning(userId)}. 165 */ mockUmIsUserRunning(@onNull UserManager um, @UserIdInt int userId, boolean isRunning)166 public static void mockUmIsUserRunning(@NonNull UserManager um, @UserIdInt int userId, 167 boolean isRunning) { 168 when(um.isUserRunning(userId)).thenReturn(isRunning); 169 } 170 171 /** 172 * Mocks a successful call to {@code UserManager#createUser(String, String, int)}, returning 173 * a user with the passed arguments. 174 */ 175 @NonNull mockUmCreateUser(@onNull UserManager um, @Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId)176 public static UserInfo mockUmCreateUser(@NonNull UserManager um, @Nullable String name, 177 @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId) { 178 UserInfo userInfo = new UserInfoBuilder(userId) 179 .setName(name) 180 .setType(userType) 181 .setFlags(flags) 182 .build(); 183 when(um.createUser(name, userType, flags)).thenReturn(userInfo); 184 return userInfo; 185 } 186 187 /** 188 * Mocks a call to {@code UserManager#createUser(String, String, int)} that throws the given 189 * runtime exception. 190 */ 191 @NonNull mockUmCreateUser(@onNull UserManager um, @Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @NonNull RuntimeException e)192 public static void mockUmCreateUser(@NonNull UserManager um, @Nullable String name, 193 @NonNull String userType, @UserInfoFlag int flags, @NonNull RuntimeException e) { 194 when(um.createUser(name, userType, flags)).thenThrow(e); 195 } 196 197 /** 198 * Mocks a successful call to {@code UserManager#removeUserOrSetEphemeral(int)}, and notifies 199 * {@code listener} when it's called. 200 */ mockUmRemoveUserOrSetEphemeral(@onNull UserManager um, @NonNull UserInfo user, boolean evenWhenDisallowed, @RemoveResult int result, @Nullable Visitor<UserInfo> listener)201 public static void mockUmRemoveUserOrSetEphemeral(@NonNull UserManager um, 202 @NonNull UserInfo user, boolean evenWhenDisallowed, @RemoveResult int result, 203 @Nullable Visitor<UserInfo> listener) { 204 int userId = user.id; 205 when(um.removeUserOrSetEphemeral(userId, evenWhenDisallowed)).thenAnswer((inv) -> { 206 if (listener != null) { 207 Log.v(TAG, "mockUmRemoveUserOrSetEphemeral(" + user + "): notifying " + listener); 208 listener.visit(user); 209 } 210 return result; 211 }); 212 } 213 214 /** 215 * Mocks a call to {@link ServiceManager#getService(name)}. 216 * 217 * <p><b>Note: </b>it must be made inside a 218 * {@link com.android.dx.mockito.inline.extended.StaticMockitoSession} built with 219 * {@code spyStatic(ServiceManager.class)}. 220 * 221 * @param name interface name of the service 222 * @param binder result of such call 223 */ mockSmGetService(@onNull String name, @NonNull IBinder binder)224 public static void mockSmGetService(@NonNull String name, @NonNull IBinder binder) { 225 doReturn(binder).when(() -> ServiceManager.getService(name)); 226 } 227 228 /** 229 * Returns mocked binder implementation from the given interface name. 230 * 231 * <p><b>Note: </b>it must be made inside a 232 * {@link com.android.dx.mockito.inline.extended.StaticMockitoSession} built with 233 * {@code spyStatic(ServiceManager.class)}. 234 * 235 * @param name interface name of the service 236 * @param binder mocked return of ServiceManager.getService 237 * @param service binder implementation 238 */ mockQueryService(@onNull String name, @NonNull IBinder binder, @NonNull T service)239 public static <T extends IInterface> void mockQueryService(@NonNull String name, 240 @NonNull IBinder binder, @NonNull T service) { 241 doReturn(binder).when(() -> ServiceManager.getService(name)); 242 when(binder.queryLocalInterface(anyString())).thenReturn(service); 243 } 244 245 /** 246 * Mocks a call to {@link Binder.getCallingUserHandle()}. 247 * 248 * <p><b>Note: </b>it must be made inside a 249 * {@link com.android.dx.mockito.inline.extended.StaticMockitoSession} built with 250 * {@code spyStatic(Binder.class)}. 251 * 252 * @param userId identifier of the {@link UserHandle} that will be returned. 253 */ mockBinderGetCallingUserHandle(@serIdInt int userId)254 public static void mockBinderGetCallingUserHandle(@UserIdInt int userId) { 255 doReturn(UserHandle.of(userId)).when(() -> Binder.getCallingUserHandle()); 256 } 257 258 /** 259 * Mocks a call to {@link Context#getSystemService(Class)}. 260 */ mockContextGetService(@onNull Context context, @NonNull Class<T> serviceClass, @NonNull T service)261 public static <T> void mockContextGetService(@NonNull Context context, 262 @NonNull Class<T> serviceClass, @NonNull T service) { 263 when(context.getSystemService(serviceClass)).thenReturn(service); 264 if (serviceClass.equals(PackageManager.class)) { 265 when(context.getPackageManager()).thenReturn(PackageManager.class.cast(service)); 266 } 267 } 268 269 // TODO(b/192307581): add unit tests 270 /** 271 * Returns the result of the giving {@code callable} in the main thread, preparing the 272 * {@link Looper} if needed and using a default timeout. 273 */ syncCallOnMainThread(Callable<T> c)274 public static <T> T syncCallOnMainThread(Callable<T> c) throws Exception { 275 return syncCallOnMainThread(JavaMockitoHelper.ASYNC_TIMEOUT_MS, c); 276 } 277 278 // TODO(b/192307581): add unit tests 279 /** 280 * Returns the result of the giving {@code callable} in the main thread, preparing the 281 * {@link Looper} if needed. 282 */ syncCallOnMainThread(long timeoutMs, Callable<T> callable)283 public static <T> T syncCallOnMainThread(long timeoutMs, Callable<T> callable) 284 throws Exception { 285 boolean quitLooper = false; 286 Looper looper = Looper.getMainLooper(); 287 if (looper == null) { 288 Log.i(TAG, "preparing main looper"); 289 Looper.prepareMainLooper(); 290 looper = Looper.getMainLooper(); 291 assertWithMessage("Looper.getMainLooper()").that(looper).isNotNull(); 292 quitLooper = true; 293 } 294 Log.i(TAG, "looper: " + looper); 295 AtomicReference<Exception> exception = new AtomicReference<>(); 296 AtomicReference<T> ref = new AtomicReference<>(); 297 try { 298 Handler handler = new Handler(looper); 299 CountDownLatch latch = new CountDownLatch(1); 300 handler.post(() -> { 301 T result = null; 302 try { 303 result = callable.call(); 304 } catch (Exception e) { 305 exception.set(e); 306 } 307 ref.set(result); 308 latch.countDown(); 309 }); 310 JavaMockitoHelper.await(latch, timeoutMs); 311 Exception e = exception.get(); 312 if (e != null) throw e; 313 return ref.get(); 314 } finally { 315 if (quitLooper) { 316 Log.i(TAG, "quitting looper: " + looper); 317 looper.quitSafely(); 318 } 319 } 320 } 321 322 // TODO(b/192307581): add unit tests 323 /** 324 * Runs the giving {@code runnable} in the activity's UI thread, using a default timeout. 325 */ syncRunOnUiThread(Activity activity, Runnable runnable)326 public static void syncRunOnUiThread(Activity activity, Runnable runnable) throws Exception { 327 syncRunOnUiThread(JavaMockitoHelper.ASYNC_TIMEOUT_MS, activity, runnable); 328 } 329 330 // TODO(b/192307581): add unit tests 331 /** 332 * Runs the giving {@code runnable} in the activity's UI thread. 333 */ syncRunOnUiThread(long timeoutMs, Activity activity, Runnable runnable)334 public static void syncRunOnUiThread(long timeoutMs, Activity activity, Runnable runnable) 335 throws Exception { 336 CountDownLatch latch = new CountDownLatch(1); 337 activity.runOnUiThread(() -> { 338 runnable.run(); 339 latch.countDown(); 340 }); 341 JavaMockitoHelper.await(latch, timeoutMs); 342 } 343 AndroidMockitoHelper()344 private AndroidMockitoHelper() { 345 throw new UnsupportedOperationException("contains only static methods"); 346 } 347 } 348