1 /* 2 * Copyright (C) 2021 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 com.android.bedstead.dpmwrapper; 17 18 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 19 20 import static com.android.bedstead.dpmwrapper.DataFormatter.addArg; 21 import static com.android.bedstead.dpmwrapper.DataFormatter.getArg; 22 import static com.android.bedstead.dpmwrapper.Utils.ACTION_WRAPPED_MANAGER_CALL; 23 import static com.android.bedstead.dpmwrapper.Utils.EXTRA_CLASS; 24 import static com.android.bedstead.dpmwrapper.Utils.EXTRA_METHOD; 25 import static com.android.bedstead.dpmwrapper.Utils.EXTRA_NUMBER_ARGS; 26 import static com.android.bedstead.dpmwrapper.Utils.VERBOSE; 27 import static com.android.bedstead.dpmwrapper.Utils.getHandler; 28 29 import android.annotation.Nullable; 30 import android.app.admin.DevicePolicyManager; 31 import android.content.BroadcastReceiver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.ActivityInfo; 35 import android.content.pm.PackageInfo; 36 import android.content.pm.PackageManager; 37 import android.content.pm.PackageManager.NameNotFoundException; 38 import android.net.wifi.WifiManager; 39 import android.os.Bundle; 40 import android.os.HardwarePropertiesManager; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.util.Log; 44 45 import org.mockito.stubbing.Answer; 46 47 import java.lang.reflect.InvocationTargetException; 48 import java.util.HashMap; 49 import java.util.Locale; 50 import java.util.concurrent.CountDownLatch; 51 import java.util.concurrent.TimeUnit; 52 import java.util.concurrent.atomic.AtomicReference; 53 54 //TODO(b/176993670): STOPSHIP - it currently uses ordered broadcasts and a Mockito spy to implement 55 //the IPC between users, but before S is shipped it should be changed to use the connected apps SDK 56 //or the new CTS infrastructure. 57 /** 58 * Class used to create to provide a {@link DevicePolicyManager} implementation (and other managers 59 * that must be run by the device owner user) that automatically funnels calls between the user 60 * running the tests and the user that is the device owner. 61 */ 62 public final class TestAppSystemServiceFactory { 63 64 private static final String TAG = TestAppSystemServiceFactory.class.getSimpleName(); 65 66 private static final int RESULT_NOT_SENT_TO_ANY_RECEIVER = 108; 67 static final int RESULT_OK = 42; 68 static final int RESULT_EXCEPTION = 666; 69 70 // Must be high enough to outlast long tests like NetworkLoggingTest, which waits up to 71 // 6 minutes for network monitoring events. 72 private static final long TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10); 73 74 // Caches whether the package declares the required receiver (otherwise each test would be 75 // querying package manager, which is expensive) 76 private static final HashMap<String, Boolean> sHasRequiredReceiver = new HashMap<>(); 77 78 /** 79 * Gets the proper {@link DevicePolicyManager} instance to be used by the test. 80 */ getDevicePolicyManager(Context context, Class<? extends BroadcastReceiver> receiverClass, boolean forDeviceOwner)81 public static DevicePolicyManager getDevicePolicyManager(Context context, 82 Class<? extends BroadcastReceiver> receiverClass, boolean forDeviceOwner) { 83 return getSystemService(context, DevicePolicyManager.class, receiverClass, forDeviceOwner); 84 } 85 86 /** 87 * Gets the proper {@link WifiManager} instance to be used by device owner tests. 88 */ getWifiManager(Context context, Class<? extends BroadcastReceiver> receiverClass)89 public static WifiManager getWifiManager(Context context, 90 Class<? extends BroadcastReceiver> receiverClass) { 91 return getSystemService(context, WifiManager.class, receiverClass, 92 /* forDeviceOwner= */ true); 93 } 94 95 /** 96 * Gets the proper {@link HardwarePropertiesManager} instance to be used by device owner tests. 97 */ getHardwarePropertiesManager(Context context, Class<? extends BroadcastReceiver> receiverClass)98 public static HardwarePropertiesManager getHardwarePropertiesManager(Context context, 99 Class<? extends BroadcastReceiver> receiverClass) { 100 return getSystemService(context, HardwarePropertiesManager.class, receiverClass, 101 /* forDeviceOwner= */ true); 102 } 103 104 /** 105 * Gets the proper {@link UserManager} instance to be used by device owner tests. 106 */ getUserManager(Context context, Class<? extends BroadcastReceiver> receiverClass)107 public static UserManager getUserManager(Context context, 108 Class<? extends BroadcastReceiver> receiverClass) { 109 return getSystemService(context, UserManager.class, receiverClass, 110 /* forDeviceOwner= */ true); 111 } 112 113 /** 114 * Gets the proper {@link GenericManager} instance to be used by the test. 115 */ getGenericManager(Context context, Class<? extends BroadcastReceiver> receiverClass)116 public static GenericManager getGenericManager(Context context, 117 Class<? extends BroadcastReceiver> receiverClass) { 118 return getSystemService(context, GenericManager.class, receiverClass, 119 /* forDeviceOwner= */ true); 120 } 121 assertHasRequiredReceiver(Context context)122 private static void assertHasRequiredReceiver(Context context) { 123 if (!UserManager.isHeadlessSystemUserMode()) return; 124 125 String packageName = context.getPackageName(); 126 Boolean hasIt = sHasRequiredReceiver.get(packageName); 127 if (hasIt != null && hasIt) { 128 return; 129 } 130 PackageManager pm = context.getPackageManager(); 131 Class<?> targetClass = TestAppCallbacksReceiver.class; 132 PackageInfo packageInfo; 133 try { 134 packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_RECEIVERS); 135 } catch (NameNotFoundException e) { 136 Log.wtf(TAG, "Could not get receivers for " + packageName); 137 return; 138 } 139 140 int numberReceivers = (packageInfo.receivers == null ? 0 : packageInfo.receivers.length); 141 Log.d(TAG, "assertHasRequiredReceiver(" + packageName + "): userId=" + context.getUserId() 142 + ", info=" + packageInfo + ", receivers=" + numberReceivers); 143 144 if (packageInfo.receivers != null) { 145 for (ActivityInfo receiver : packageInfo.receivers) { 146 Log.v(TAG, "checking receiver " + receiver); 147 Class<?> receiverClass = null; 148 try { 149 receiverClass = Class.forName(receiver.name); 150 } catch (ClassNotFoundException e) { 151 Log.e(TAG, "Invalid receiver class on manifest: " + receiver.name); 152 continue; 153 } 154 if (TestAppCallbacksReceiver.class.isAssignableFrom(receiverClass)) { 155 Log.d(TAG, "Found " + receiverClass.getName() + " on " + packageName); 156 sHasRequiredReceiver.put(packageName, Boolean.TRUE); 157 return; 158 } 159 } 160 } 161 if (numberReceivers == 0) { 162 // This is happening sometimes on headless system user; most likely it's a permission 163 // issue querying pm, but given that the DpmWrapper is temporary and this check is more 164 // of a validation to avoid other issues, it's ok to just log... 165 Log.wtf(TAG, "Package " + packageName + " has no receivers"); 166 return; 167 } 168 fail("Package " + packageName + " has " + numberReceivers + " receivers, but not extends " 169 + TestAppCallbacksReceiver.class.getName() + " - did you add one to the manifest?"); 170 } 171 getSystemService(Context context, Class<T> serviceClass, Class<? extends BroadcastReceiver> receiverClass, boolean forDeviceOwner)172 private static <T> T getSystemService(Context context, Class<T> serviceClass, 173 Class<? extends BroadcastReceiver> receiverClass, boolean forDeviceOwner) { 174 ServiceManagerWrapper<T> wrapper = null; 175 Class<?> wrappedClass; 176 177 @SuppressWarnings("unchecked") 178 T manager = null; 179 180 if (serviceClass.equals(DevicePolicyManager.class)) { 181 wrappedClass = DevicePolicyManager.class; 182 @SuppressWarnings("unchecked") 183 ServiceManagerWrapper<T> safeCastWrapper = 184 (ServiceManagerWrapper<T>) new DevicePolicyManagerWrapper(); 185 wrapper = safeCastWrapper; 186 } else if (serviceClass.equals(WifiManager.class)) { 187 @SuppressWarnings("unchecked") 188 ServiceManagerWrapper<T> safeCastWrapper = 189 (ServiceManagerWrapper<T>) new WifiManagerWrapper(); 190 wrapper = safeCastWrapper; 191 wrappedClass = WifiManager.class; 192 } else if (serviceClass.equals(HardwarePropertiesManager.class)) { 193 @SuppressWarnings("unchecked") 194 ServiceManagerWrapper<T> safeCastWrapper = 195 (ServiceManagerWrapper<T>) new HardwarePropertiesManagerWrapper(); 196 wrapper = safeCastWrapper; 197 wrappedClass = HardwarePropertiesManager.class; 198 } else if (serviceClass.equals(UserManager.class)) { 199 @SuppressWarnings("unchecked") 200 ServiceManagerWrapper<T> safeCastWrapper = 201 (ServiceManagerWrapper<T>) new UserManagerWrapper(); 202 wrapper = safeCastWrapper; 203 wrappedClass = UserManager.class; 204 } else if (serviceClass.equals(GenericManager.class)) { 205 @SuppressWarnings("unchecked") 206 ServiceManagerWrapper<T> safeCastWrapper = 207 (ServiceManagerWrapper<T>) new GenericManagerWrapper(); 208 @SuppressWarnings("unchecked") 209 T safeCastManager = (T) new GenericManagerImpl(context); 210 wrapper = safeCastWrapper; 211 wrappedClass = GenericManager.class; 212 manager = safeCastManager; 213 } else { 214 throw new IllegalArgumentException("invalid service class: " + serviceClass); 215 } 216 if (manager == null) { 217 manager = (T) context.getSystemService(wrappedClass); 218 } 219 220 if (manager == null) { 221 fail("Could not get a manager of type " + serviceClass); 222 } 223 224 if (!forDeviceOwner) return manager; 225 226 assertHasRequiredReceiver(context); 227 228 int userId = context.getUserId(); 229 if (userId == UserHandle.USER_SYSTEM || !UserManager.isHeadlessSystemUserMode()) { 230 Log.i(TAG, "get(): returning 'pure' DevicePolicyManager for user " + userId); 231 return manager; 232 } 233 234 String receiverClassName = receiverClass.getName(); 235 final String wrappedClassName = wrappedClass.getName(); 236 if (VERBOSE) { 237 Log.v(TAG, "get(): receiverClassName: " + receiverClassName 238 + ", wrappedClassName: " + wrappedClassName); 239 } 240 241 Answer<?> answer = (inv) -> { 242 Object[] args = inv.getArguments(); 243 if (VERBOSE) { 244 Log.v(TAG, "spying " + inv + " method: " + inv.getMethod()); 245 } else { 246 Log.i(TAG, "spying " + inv.getMethod()); 247 } 248 String methodName = inv.getMethod().getName(); 249 Intent intent = new Intent(ACTION_WRAPPED_MANAGER_CALL) 250 .setClassName(context, receiverClassName) 251 .putExtra(EXTRA_CLASS, wrappedClassName) 252 .putExtra(EXTRA_METHOD, methodName) 253 .putExtra(EXTRA_NUMBER_ARGS, args.length); 254 for (int i = 0; i < args.length; i++) { 255 addArg(intent, args, i); 256 } 257 258 final CountDownLatch latch = new CountDownLatch(1); 259 final AtomicReference<Result> resultRef = new AtomicReference<>(); 260 BroadcastReceiver myReceiver = new BroadcastReceiver() { 261 public void onReceive(Context context, Intent intent) { 262 String action = intent.getAction(); 263 if (VERBOSE) { 264 Log.v(TAG, "spy received intent " + action + " for user " 265 + context.getUserId()); 266 } 267 Result result = new Result(this); 268 if (VERBOSE) Log.v(TAG, "result:" + result); 269 resultRef.set(result); 270 latch.countDown(); 271 }; 272 273 }; 274 if (VERBOSE) { 275 Log.v(TAG, "Sending ordered broadcast (" + Utils.toString(intent) + ") from user " 276 + userId + " to user " + UserHandle.SYSTEM); 277 } 278 279 // NOTE: method below used to be wrapped under runWithShellPermissionIdentity() to get 280 // INTERACT_ACROSS_USERS permission, but that's not needed anymore (as the permission 281 // is granted by the test. Besides, this class is now also used by DO apps that are not 282 // instrumented, so it was removed 283 if (context.checkSelfPermission(INTERACT_ACROSS_USERS) 284 != PackageManager.PERMISSION_GRANTED) { 285 fail("Package " + context.getPackageName() + " doesn't have " 286 + INTERACT_ACROSS_USERS + " - did you add it to the manifest and called " 287 + "grantDpmWrapper() (for user " + userId + ") in the host-side test?"); 288 } 289 context.sendOrderedBroadcastAsUser(intent, 290 UserHandle.SYSTEM, /* permission= */ null, myReceiver, getHandler(), 291 RESULT_NOT_SENT_TO_ANY_RECEIVER, /* initialData= */ null, 292 /* initialExtras= */ null); 293 294 if (VERBOSE) { 295 Log.d(TAG, "Waiting up to " + TIMEOUT_MS + "ms for response on " 296 + Thread.currentThread()); 297 } 298 if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 299 fail("Ordered broadcast for %s() not received in %dms", methodName, TIMEOUT_MS); 300 } 301 302 Result result = resultRef.get(); 303 Log.d(TAG, "Received result on user " + userId + ". Code: " 304 + resultCodeToString(result.code)); 305 306 if (VERBOSE) { 307 // Some results - like network logging events - are quite large 308 Log.v(TAG, "Result: " + result); 309 } 310 311 switch (result.code) { 312 case RESULT_OK: 313 return result.value; 314 case RESULT_EXCEPTION: 315 Exception e = (Exception) result.value; 316 throw (e instanceof InvocationTargetException) ? e.getCause() : e; 317 case RESULT_NOT_SENT_TO_ANY_RECEIVER: 318 fail("Didn't receive result from ordered broadcast - did you override " 319 + receiverClassName + ".onReceive() to call " 320 + "DeviceOwnerHelper.runManagerMethod()?"); 321 return null; 322 default: 323 fail("Received invalid result for method %s: %s", methodName, result); 324 return null; 325 } 326 }; 327 328 T spy = wrapper.getWrapper(context, manager, answer); 329 330 return spy; 331 332 } 333 resultCodeToString(int code)334 static String resultCodeToString(int code) { 335 // Can't use DebugUtils.constantToString() because some valus are private 336 switch (code) { 337 case RESULT_NOT_SENT_TO_ANY_RECEIVER: 338 return "RESULT_NOT_SENT_TO_ANY_RECEIVER"; 339 case RESULT_OK: 340 return "RESULT_OK"; 341 case RESULT_EXCEPTION: 342 return "RESULT_EXCEPTION"; 343 default: 344 return "RESULT_UNKNOWN:" + code; 345 } 346 } 347 fail(String template, Object... args)348 private static void fail(String template, Object... args) { 349 throw new AssertionError(String.format(Locale.ENGLISH, template, args)); 350 } 351 352 private static final class Result { 353 public final int code; 354 @Nullable public final String error; 355 @Nullable public final Bundle extras; 356 @Nullable public final Object value; 357 Result(BroadcastReceiver receiver)358 Result(BroadcastReceiver receiver) { 359 int resultCode = receiver.getResultCode(); 360 String data = receiver.getResultData(); 361 extras = receiver.getResultExtras(/* makeMap= */ true); 362 Object parsedValue = null; 363 try { 364 if (extras != null && !extras.isEmpty()) { 365 Object[] result = new Object[1]; 366 int index = 0; 367 getArg(extras, result, /* parameterTypes= */ null, index); 368 parsedValue = result[index]; 369 } 370 } catch (Exception e) { 371 Log.e(TAG, "error parsing extras (code=" + resultCode + ", data=" + data, e); 372 data = "error parsing extras"; 373 resultCode = RESULT_EXCEPTION; 374 } 375 code = resultCode; 376 error = data; 377 value = parsedValue; 378 } 379 380 @Override toString()381 public String toString() { 382 return "Result[code=" + resultCodeToString(code) + ", error=" + error 383 + ", extras=" + extras + ", value=" + value + "]"; 384 } 385 } 386 387 abstract static class ServiceManagerWrapper<T> { getWrapper(Context context, T manager, Answer<?> answer)388 abstract T getWrapper(Context context, T manager, Answer<?> answer); 389 } 390 TestAppSystemServiceFactory()391 private TestAppSystemServiceFactory() { 392 throw new UnsupportedOperationException("contains only static methods"); 393 } 394 } 395