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