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)81     public static DevicePolicyManager getDevicePolicyManager(Context context,
82             Class<? extends BroadcastReceiver> receiverClass) {
83         return getSystemService(context, DevicePolicyManager.class, receiverClass);
84     }
85 
86     /**
87      * Gets the proper {@link WifiManager} instance to be used by the test.
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     }
93 
94     /**
95      * Gets the proper {@link HardwarePropertiesManager} instance to be used by the test.
96      */
getHardwarePropertiesManager(Context context, Class<? extends BroadcastReceiver> receiverClass)97     public static HardwarePropertiesManager getHardwarePropertiesManager(Context context,
98             Class<? extends BroadcastReceiver> receiverClass) {
99         return getSystemService(context, HardwarePropertiesManager.class, receiverClass);
100     }
101 
102     /**
103      * Gets the proper {@link UserManager} instance to be used by the test.
104      */
getUserManager(Context context, Class<? extends BroadcastReceiver> receiverClass)105     public static UserManager getUserManager(Context context,
106             Class<? extends BroadcastReceiver> receiverClass) {
107         return getSystemService(context, UserManager.class, receiverClass);
108     }
109 
assertHasRequiredReceiver(Context context)110     private static void assertHasRequiredReceiver(Context context) {
111         if (!UserManager.isHeadlessSystemUserMode()) return;
112 
113         String packageName = context.getPackageName();
114         Boolean hasIt = sHasRequiredReceiver.get(packageName);
115         if (hasIt != null && hasIt) {
116             return;
117         }
118         PackageManager pm = context.getPackageManager();
119         Class<?> targetClass = TestAppCallbacksReceiver.class;
120         PackageInfo packageInfo;
121         try {
122             packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_RECEIVERS);
123         } catch (NameNotFoundException e) {
124             Log.wtf(TAG, "Could not get receivers for " + packageName);
125             return;
126         }
127 
128         if (packageInfo.receivers != null) {
129             for (ActivityInfo receiver : packageInfo.receivers) {
130                 Class<?> receiverClass = null;
131                 try {
132                     receiverClass = Class.forName(receiver.name);
133                 } catch (ClassNotFoundException e) {
134                     Log.e(TAG, "Invalid receiver class on manifest: " + receiver.name);
135                     continue;
136                 }
137                 if (TestAppCallbacksReceiver.class.isAssignableFrom(receiverClass)) {
138                     Log.d(TAG, "Found " + receiverClass + " on " + packageName);
139                     sHasRequiredReceiver.put(packageName, Boolean.TRUE);
140                     return;
141                 }
142             }
143         }
144         fail("Package " + packageName + " doesn't have a " + TestAppCallbacksReceiver.class
145                 + " receiver - did you add it to the manifest?");
146     }
147 
getSystemService(Context context, Class<T> serviceClass, Class<? extends BroadcastReceiver> receiverClass)148     private static <T> T getSystemService(Context context, Class<T> serviceClass,
149             Class<? extends BroadcastReceiver> receiverClass) {
150         assertHasRequiredReceiver(context);
151 
152         ServiceManagerWrapper<T> wrapper = null;
153         Class<?> wrappedClass;
154 
155         if (serviceClass.equals(DevicePolicyManager.class)) {
156             wrappedClass = DevicePolicyManager.class;
157             @SuppressWarnings("unchecked")
158             ServiceManagerWrapper<T> safeCastWrapper =
159                     (ServiceManagerWrapper<T>) new DevicePolicyManagerWrapper();
160             wrapper = safeCastWrapper;
161         } else if (serviceClass.equals(WifiManager.class)) {
162             @SuppressWarnings("unchecked")
163             ServiceManagerWrapper<T> safeCastWrapper =
164                     (ServiceManagerWrapper<T>) new WifiManagerWrapper();
165             wrapper = safeCastWrapper;
166             wrappedClass = WifiManager.class;
167         } else if (serviceClass.equals(HardwarePropertiesManager.class)) {
168             @SuppressWarnings("unchecked")
169             ServiceManagerWrapper<T> safeCastWrapper =
170                     (ServiceManagerWrapper<T>) new HardwarePropertiesManagerWrapper();
171             wrapper = safeCastWrapper;
172             wrappedClass = HardwarePropertiesManager.class;
173         } else if (serviceClass.equals(UserManager.class)) {
174             @SuppressWarnings("unchecked")
175             ServiceManagerWrapper<T> safeCastWrapper =
176                     (ServiceManagerWrapper<T>) new UserManagerWrapper();
177             wrapper = safeCastWrapper;
178             wrappedClass = UserManager.class;
179         } else {
180             throw new IllegalArgumentException("invalid service class: " + serviceClass);
181         }
182 
183         @SuppressWarnings("unchecked")
184         T manager = (T) context.getSystemService(wrappedClass);
185 
186         int userId = context.getUserId();
187         if (userId == UserHandle.USER_SYSTEM || !UserManager.isHeadlessSystemUserMode()) {
188             Log.i(TAG, "get(): returning 'pure' DevicePolicyManager for user " + userId);
189             return manager;
190         }
191 
192         String receiverClassName = receiverClass.getName();
193         final String wrappedClassName = wrappedClass.getName();
194         if (VERBOSE) {
195             Log.v(TAG, "get(): receiverClassName: " + receiverClassName
196                     + ", wrappedClassName: " + wrappedClassName);
197         }
198 
199         Answer<?> answer = (inv) -> {
200             Object[] args = inv.getArguments();
201             if (VERBOSE) {
202                 Log.v(TAG, "spying " + inv + " method: " + inv.getMethod());
203             } else {
204                 Log.i(TAG, "spying " + inv.getMethod());
205             }
206             String methodName = inv.getMethod().getName();
207             Intent intent = new Intent(ACTION_WRAPPED_MANAGER_CALL)
208                     .setClassName(context, receiverClassName)
209                     .putExtra(EXTRA_CLASS, wrappedClassName)
210                     .putExtra(EXTRA_METHOD, methodName)
211                     .putExtra(EXTRA_NUMBER_ARGS, args.length);
212             for (int i = 0; i < args.length; i++) {
213                 addArg(intent, args, i);
214             }
215 
216             final CountDownLatch latch = new CountDownLatch(1);
217             final AtomicReference<Result> resultRef = new AtomicReference<>();
218             BroadcastReceiver myReceiver = new BroadcastReceiver() {
219                 public void onReceive(Context context, Intent intent) {
220                     String action = intent.getAction();
221                     if (VERBOSE) {
222                         Log.v(TAG, "spy received intent " + action + " for user "
223                                 + context.getUserId());
224                     }
225                     Result result = new Result(this);
226                     if (VERBOSE) Log.v(TAG, "result:" + result);
227                     resultRef.set(result);
228                     latch.countDown();
229                 };
230 
231             };
232             if (VERBOSE) {
233                 Log.v(TAG, "Sending ordered broadcast (" + Utils.toString(intent) + ") from user "
234                         + userId + " to user " + UserHandle.SYSTEM);
235             }
236 
237             // NOTE: method below used to be wrapped under runWithShellPermissionIdentity() to get
238             // INTERACT_ACROSS_USERS permission, but that's not needed anymore (as the permission
239             // is granted by the test. Besides, this class is now also used by DO apps that are not
240             // instrumented, so it was removed
241             if (context.checkSelfPermission(INTERACT_ACROSS_USERS)
242                     != PackageManager.PERMISSION_GRANTED) {
243                 fail("Package " + context.getPackageName() + " doesn't have "
244                         + INTERACT_ACROSS_USERS + " - did you add it to the manifest and called "
245                         + "grantDpmWrapper() (for user " + userId + ") in the host-side test?");
246             }
247             context.sendOrderedBroadcastAsUser(intent,
248                     UserHandle.SYSTEM, /* permission= */ null, myReceiver, getHandler(),
249                     RESULT_NOT_SENT_TO_ANY_RECEIVER, /* initialData= */ null,
250                     /* initialExtras= */ null);
251 
252             if (VERBOSE) {
253                 Log.d(TAG, "Waiting up to " + TIMEOUT_MS + "ms for response on "
254                         + Thread.currentThread());
255             }
256             if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
257                 fail("Ordered broadcast for %s() not received in %dms", methodName, TIMEOUT_MS);
258             }
259 
260             Result result = resultRef.get();
261             Log.d(TAG, "Received result on user " + userId + ". Code: "
262                     + resultCodeToString(result.code));
263 
264             if (VERBOSE) {
265                 // Some results - like network logging events - are quite large
266                 Log.v(TAG, "Result: " + result);
267             }
268 
269             switch (result.code) {
270                 case RESULT_OK:
271                     return result.value;
272                 case RESULT_EXCEPTION:
273                     Exception e = (Exception) result.value;
274                     throw (e instanceof InvocationTargetException) ? e.getCause() : e;
275                 case RESULT_NOT_SENT_TO_ANY_RECEIVER:
276                     fail("Didn't receive result from ordered broadcast - did you override "
277                             + receiverClassName + ".onReceive() to call "
278                             + "DeviceOwnerHelper.runManagerMethod()?");
279                     return null;
280                 default:
281                     fail("Received invalid result for method %s: %s", methodName, result);
282                     return null;
283             }
284         };
285 
286         T spy = wrapper.getWrapper(context, manager, answer);
287 
288         return spy;
289 
290     }
291 
resultCodeToString(int code)292     static String resultCodeToString(int code) {
293         // Can't use DebugUtils.constantToString() because some valus are private
294         switch (code) {
295             case RESULT_NOT_SENT_TO_ANY_RECEIVER:
296                 return "RESULT_NOT_SENT_TO_ANY_RECEIVER";
297             case RESULT_OK:
298                 return "RESULT_OK";
299             case RESULT_EXCEPTION:
300                 return "RESULT_EXCEPTION";
301             default:
302                 return "RESULT_UNKNOWN:" + code;
303         }
304     }
305 
fail(String template, Object... args)306     private static void fail(String template, Object... args) {
307         throw new AssertionError(String.format(Locale.ENGLISH, template, args));
308     }
309 
310     private static final class Result {
311         public final int code;
312         @Nullable public final String error;
313         @Nullable public final Bundle extras;
314         @Nullable public final Object value;
315 
Result(BroadcastReceiver receiver)316         Result(BroadcastReceiver receiver) {
317             int resultCode = receiver.getResultCode();
318             String data = receiver.getResultData();
319             extras = receiver.getResultExtras(/* makeMap= */ true);
320             Object parsedValue = null;
321             try {
322                 if (extras != null && !extras.isEmpty()) {
323                     Object[] result = new Object[1];
324                     int index = 0;
325                     getArg(extras, result, /* parameterTypes= */ null, index);
326                     parsedValue = result[index];
327                 }
328             } catch (Exception e) {
329                 Log.e(TAG, "error parsing extras (code=" + resultCode + ", data=" + data, e);
330                 data = "error parsing extras";
331                 resultCode = RESULT_EXCEPTION;
332             }
333             code = resultCode;
334             error = data;
335             value = parsedValue;
336         }
337 
338         @Override
toString()339         public String toString() {
340             return "Result[code=" + resultCodeToString(code) + ", error=" + error
341                     + ", extras=" + extras + ", value=" + value + "]";
342         }
343     }
344 
345     abstract static class ServiceManagerWrapper<T> {
getWrapper(Context context, T manager, Answer<?> answer)346         abstract T getWrapper(Context context, T manager, Answer<?> answer);
347     }
348 
TestAppSystemServiceFactory()349     private TestAppSystemServiceFactory() {
350         throw new UnsupportedOperationException("contains only static methods");
351     }
352 }
353