1 /*
2  * Copyright (C) 2017 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.cts.devicepolicy;
17 
18 import static org.junit.Assert.fail;
19 
20 import com.android.tradefed.log.LogUtil.CLog;
21 
22 import org.junit.Test;
23 
24 import java.util.concurrent.TimeUnit;
25 
26 public abstract class BaseDeviceAdminServiceTest extends BaseDevicePolicyTest {
27     protected static final String OWNER_PKG = "com.android.cts.deviceadminservice";
28     protected static final String OWNER_PKG_B = "com.android.cts.deviceadminserviceb";
29 
30     protected static final String OWNER_APK_1 = "CtsDeviceAdminService1.apk";
31     protected static final String OWNER_APK_2 = "CtsDeviceAdminService2.apk";
32     protected static final String OWNER_APK_3 = "CtsDeviceAdminService3.apk";
33     protected static final String OWNER_APK_4 = "CtsDeviceAdminService4.apk";
34 
35     protected static final String OWNER_APK_B = "CtsDeviceAdminServiceB.apk";
36 
37     protected static final String ADMIN_RECEIVER_TEST_CLASS = ".MyOwner";
38 
39     protected static final String OWNER_COMPONENT = OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS;
40     protected static final String OWNER_SERVICE = OWNER_PKG + "/.MyService";
41     protected static final String OWNER_SERVICE2 = OWNER_PKG + "/.MyService2";
42 
43     protected static final String OWNER_COMPONENT_B = OWNER_PKG_B + "/"
44             + OWNER_PKG + ADMIN_RECEIVER_TEST_CLASS;
45     protected static final String OWNER_SERVICE_B = OWNER_PKG_B + "/"
46             + OWNER_PKG + ".MyService";
47 
48     private static final int TIMEOUT_SECONDS = 3 * 60;
49 
50     @Override
tearDown()51     public void tearDown() throws Exception {
52         removeAdmin(OWNER_COMPONENT, getUserId());
53         removeAdmin(OWNER_COMPONENT_B, getUserId());
54         getDevice().uninstallPackage(OWNER_PKG);
55         getDevice().uninstallPackage(OWNER_PKG_B);
56 
57         super.tearDown();
58     }
59 
getUserId()60     protected abstract int getUserId() throws Exception;
61 
executeDeviceTestMethod(String className, String testName)62     protected void executeDeviceTestMethod(String className, String testName) throws Exception {
63         runDeviceTestsAsUser(OWNER_PKG, className, testName, getUserId());
64     }
65 
66     protected interface RunnableWithThrowable {
run()67         void run() throws Throwable;
68     }
69 
withRetry(RunnableWithThrowable test)70     protected void withRetry(RunnableWithThrowable test) throws Throwable {
71         final long until = System.nanoTime() + TimeUnit.SECONDS.toNanos(TIMEOUT_SECONDS);
72 
73         sleep(500);
74 
75         Throwable lastThrowable = null;
76         while (System.nanoTime() < until) {
77             try {
78                 test.run();
79                 // Pass, return.
80                 return;
81             } catch (Throwable th) {
82                 lastThrowable = th;
83             }
84             sleep(3000);
85         }
86         if (lastThrowable != null) {
87             throw lastThrowable;
88         }
89         fail("Internal error: test " + test + " didn't run, exception not thrown.");
90     }
91 
installOwnerApp(String apk)92     protected abstract void installOwnerApp(String apk) throws Exception;
93 
removeAdmin(String ownerComponent)94     protected abstract void removeAdmin(String ownerComponent) throws Exception;
95 
setAsOwnerOrFail(String component)96     protected abstract void setAsOwnerOrFail(String component) throws Exception;
97 
98     @Test
testAll()99     public void testAll() throws Throwable {
100         // Install
101         CLog.i("Installing apk1 (%s)...", OWNER_APK_1);
102         installOwnerApp(OWNER_APK_1);
103 
104         CLog.i("Making it (%s) a device/profile owner...", OWNER_COMPONENT);
105         setAsOwnerOrFail(OWNER_COMPONENT);
106 
107         withRetry(() -> assertServiceBound(OWNER_SERVICE));
108 
109         // Remove admin.
110         CLog.i("Removing admin...");
111         removeAdmin(OWNER_COMPONENT);
112         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
113 
114         // Overwrite -> update.
115         CLog.i("Re-installing apk1...");
116         installOwnerApp(OWNER_APK_1);
117 
118         CLog.i("Making it a device/profile owner...");
119         setAsOwnerOrFail(OWNER_COMPONENT);
120         withRetry(() -> assertServiceBound(OWNER_SERVICE));
121 
122         CLog.i("Installing apk2 (%s)...", OWNER_APK_2);
123         installOwnerApp(OWNER_APK_2);
124         withRetry(() -> assertServiceBound(OWNER_SERVICE)); // Should still be bound.
125 
126         // Service exported -> not bound.
127         CLog.i("Installing apk3 (%s)...", OWNER_APK_3);
128         installOwnerApp(OWNER_APK_3);
129         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
130 
131         // Recover.
132         CLog.i("Installing apk2 again...");
133         installOwnerApp(OWNER_APK_2);
134         withRetry(() -> assertServiceBound(OWNER_SERVICE));
135 
136         // Multiple service found -> not bound.
137         CLog.i("Installing apk4 (%s)...", OWNER_APK_4);
138         installOwnerApp(OWNER_APK_4);
139         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
140         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
141 
142         // Disable service1 -> now there's only one service, so should be bound.
143         CLog.i("Running testDisableService1...");
144         executeDeviceTestMethod(".ComponentController", "testDisableService1");
145         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
146         // There's a rare flake which occurs here - will fix with migration
147 //        withRetry(() -> assertServiceBound(OWNER_SERVICE2));
148 
149         CLog.i("Running testDisableService2...");
150         executeDeviceTestMethod(".ComponentController", "testDisableService2");
151         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
152         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
153 
154         CLog.i("Running testEnableService1...");
155         executeDeviceTestMethod(".ComponentController", "testEnableService1");
156         withRetry(() -> assertServiceBound(OWNER_SERVICE));
157         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
158 
159         CLog.i("Running testEnableService2...");
160         executeDeviceTestMethod(".ComponentController", "testEnableService2");
161         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
162         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
163 
164         // Remove admin.
165         CLog.i("Removing admin again...");
166         removeAdmin(OWNER_COMPONENT);
167         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
168 
169         // Retry with package 1 and remove admin.
170         CLog.i("Installing apk1 again...");
171         installOwnerApp(OWNER_APK_1);
172 
173         CLog.i("Making it a device/profile owner again...");
174         setAsOwnerOrFail(OWNER_COMPONENT);
175         withRetry(() -> assertServiceBound(OWNER_SERVICE));
176 
177         CLog.i("Removing admin again...");
178         removeAdmin(OWNER_COMPONENT);
179         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
180 
181         // Now install package B and make it the owner.  OWNER_APK_1 still exists, but it shouldn't
182         // interfere.
183         CLog.i("Installing apk B (%s)...", OWNER_APK_B);
184         installOwnerApp(OWNER_APK_B);
185 
186         CLog.i("Making it a device/profile owner...");
187         setAsOwnerOrFail(OWNER_COMPONENT_B);
188         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
189         withRetry(() -> assertServiceBound(OWNER_SERVICE_B));
190     }
191 
rumpDumpSysService(String component)192     private String rumpDumpSysService(String component) throws Exception {
193         final String command = "dumpsys activity services " + component;
194         final String commandOutput = getDevice().executeShellCommand(command);
195         CLog.d("Output for command %s: \n%s", command, commandOutput);
196         return commandOutput;
197     }
198 
assertServiceBound(String component)199     private void assertServiceBound(String component) throws Exception {
200         final String commandOutput = rumpDumpSysService(component);
201         for (String line : commandOutput.split("\r*\n")) {
202             if (line.contains("ConnectionRecord") && line.contains(component)) {
203                 return;
204             }
205         }
206         fail("Service " + OWNER_SERVICE + " not bound.  Output was:\n" + commandOutput);
207     }
208 
assertServiceNotBound(String component)209     private void assertServiceNotBound(String component) throws Exception {
210         final String commandOutput = rumpDumpSysService(component);
211         for (String line : commandOutput.split("\r*\n")) {
212             if (line.contains("ConnectionRecord") && line.contains(component)) {
213                 fail("Service " + OWNER_SERVICE + " is bound.  Output was:\n" + commandOutput);
214             }
215         }
216     }
217 
218 /* When the service is bound, "dumpsys activity services" shows something like this:
219   * ServiceRecord{1525afe u0 com.android.cts.deviceadminservice/.MyService}
220     intent={cmp=com.android.cts.deviceadminservice/.MyService}
221     packageName=com.android.cts.deviceadminservice
222     processName=com.android.cts.deviceadminservice
223     baseDir=/data/app/com.android.cts.deviceadminservice-kXKTlCILmDfib2P76FI75A==/base.apk
224     dataDir=/data/user/0/com.android.cts.deviceadminservice
225     app=ProcessRecord{205d686 22751:com.android.cts.deviceadminservice/u0a143}
226     createTime=-3s957ms startingBgTimeout=--
227     lastActivity=-3s927ms restartTime=-3s927ms createdFromFg=true
228     Bindings:
229     * IntentBindRecord{57d5b47 CREATE}:
230       intent={cmp=com.android.cts.deviceadminservice/.MyService}
231       binder=android.os.BinderProxy@819af74
232       requested=true received=true hasBound=true doRebind=false
233       * Client AppBindRecord{e1d3c9d ProcessRecord{a8162c0 757:system/1000}}
234         Per-process Connections:
235           ConnectionRecord{10ab6b9 u0 CR FGS com.android.cts.deviceadminservice/.MyService:@51e9080}
236     All Connections:
237       ConnectionRecord{10ab6b9 u0 CR FGS com.android.cts.deviceadminservice/.MyService:@51e9080}
238 
239   * ConnectionRecord{10ab6b9 u0 CR FGS com.android.cts.deviceadminservice/.MyService:@51e9080}
240     binding=AppBindRecord{e1d3c9d com.android.cts.deviceadminservice/.MyService:system}
241     conn=android.app.LoadedApk$ServiceDispatcher$InnerConnection@51e9080 flags=0x4000001
242  */
243 }
244