• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package android.appsecurity.cts;
18 
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertTrue;
21 
22 import android.platform.test.annotations.AppModeFull;
23 import com.android.ddmlib.AdbCommandRejectedException;
24 import com.android.ddmlib.CollectingOutputReceiver;
25 import com.android.ddmlib.Log;
26 import com.android.tradefed.device.DeviceNotAvailableException;
27 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
28 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
29 
30 import org.junit.After;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 
35 /**
36  * Set of tests that verify behavior of direct boot, if supported.
37  * <p>
38  * Note that these tests drive PIN setup manually instead of relying on device
39  * administrators, which are not supported by all devices.
40  */
41 @RunWith(DeviceJUnit4ClassRunner.class)
42 public class DirectBootHostTest extends BaseHostJUnit4Test {
43     private static final String TAG = "DirectBootHostTest";
44 
45     private static final String PKG = "com.android.cts.encryptionapp";
46     private static final String CLASS = PKG + ".EncryptionAppTest";
47     private static final String APK = "CtsEncryptionApp.apk";
48 
49     private static final String OTHER_APK = "CtsSplitApp.apk";
50     private static final String OTHER_PKG = "com.android.cts.splitapp";
51 
52     private static final String MODE_NATIVE = "native";
53     private static final String MODE_EMULATED = "emulated";
54     private static final String MODE_NONE = "none";
55 
56     private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin";
57     private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive";
58 
59     private static final long SHUTDOWN_TIME_MS = 30 * 1000;
60 
61     private int[] mUsers;
62 
63     @Before
setUp()64     public void setUp() throws Exception {
65         mUsers = Utils.prepareSingleUser(getDevice());
66         assertNotNull(getAbi());
67         assertNotNull(getBuild());
68 
69         getDevice().uninstallPackage(PKG);
70         getDevice().uninstallPackage(OTHER_PKG);
71     }
72 
73     @After
tearDown()74     public void tearDown() throws Exception {
75         getDevice().uninstallPackage(PKG);
76         getDevice().uninstallPackage(OTHER_PKG);
77     }
78 
79     /**
80      * Automotive devices MUST support native FBE.
81      */
82     @Test
83     @AppModeFull // TODO: Needs porting to instant
testAutomotiveNativeFbe()84     public void testAutomotiveNativeFbe() throws Exception {
85         if (!isSupportedDevice()) {
86             Log.v(TAG, "Device not supported; skipping test");
87             return;
88         } else if (!isAutomotiveDevice()) {
89             Log.v(TAG, "Device not automotive; skipping test");
90             return;
91         }
92 
93         assertTrue("Automotive devices must support native FBE",
94             MODE_NATIVE.equals(getFbeMode()));
95     }
96 
97     /**
98      * If device has native FBE, verify lifecycle.
99      */
100     @Test
101     @AppModeFull // TODO: Needs porting to instant
testDirectBootNative()102     public void testDirectBootNative() throws Exception {
103         if (!isSupportedDevice()) {
104             Log.v(TAG, "Device not supported; skipping test");
105             return;
106         } else if (!MODE_NATIVE.equals(getFbeMode())) {
107             Log.v(TAG, "Device doesn't have native FBE; skipping test");
108             return;
109         }
110 
111         doDirectBootTest(MODE_NATIVE);
112     }
113 
114     /**
115      * If device doesn't have native FBE, enable emulation and verify lifecycle.
116      */
117     @Test
118     @AppModeFull // TODO: Needs porting to instant
testDirectBootEmulated()119     public void testDirectBootEmulated() throws Exception {
120         if (!isSupportedDevice()) {
121             Log.v(TAG, "Device not supported; skipping test");
122             return;
123         } else if (MODE_NATIVE.equals(getFbeMode())) {
124             Log.v(TAG, "Device has native FBE; skipping test");
125             return;
126         }
127 
128         doDirectBootTest(MODE_EMULATED);
129     }
130 
131     /**
132      * If device doesn't have native FBE, verify normal lifecycle.
133      */
134     @Test
135     @AppModeFull // TODO: Needs porting to instant
testDirectBootNone()136     public void testDirectBootNone() throws Exception {
137         if (!isSupportedDevice()) {
138             Log.v(TAG, "Device not supported; skipping test");
139             return;
140         } else if (MODE_NATIVE.equals(getFbeMode())) {
141             Log.v(TAG, "Device has native FBE; skipping test");
142             return;
143         }
144 
145         doDirectBootTest(MODE_NONE);
146     }
147 
doDirectBootTest(String mode)148     public void doDirectBootTest(String mode) throws Exception {
149         boolean doTest = true;
150         try {
151             // Set up test app and secure lock screens
152             new InstallMultiple().addApk(APK).run();
153             new InstallMultiple().addApk(OTHER_APK).run();
154 
155             // To receive boot broadcasts, kick our other app out of stopped state
156             getDevice().executeShellCommand("am start -a android.intent.action.MAIN"
157                     + " -c android.intent.category.LAUNCHER com.android.cts.splitapp/.MyActivity");
158 
159             // Give enough time for PackageManager to persist stopped state
160             Thread.sleep(15000);
161 
162             runDeviceTests(PKG, CLASS, "testSetUp", mUsers);
163 
164             // Give enough time for vold to update keys
165             Thread.sleep(15000);
166 
167             // Reboot system into known state with keys ejected
168             if (MODE_EMULATED.equals(mode)) {
169                 final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
170                 if (res != null && res.contains("Emulation not supported")) {
171                     doTest = false;
172                 }
173                 getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS);
174                 getDevice().waitForDeviceOnline();
175             } else {
176                 getDevice().rebootUntilOnline();
177             }
178             waitForBootCompleted();
179 
180             if (doTest) {
181                 if (MODE_NONE.equals(mode)) {
182                     runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", mUsers);
183                 } else {
184                     runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", mUsers);
185                 }
186             }
187 
188         } finally {
189             try {
190                 // Remove secure lock screens and tear down test app
191                 runDeviceTests(PKG, CLASS, "testTearDown", mUsers);
192             } finally {
193                 getDevice().uninstallPackage(PKG);
194 
195                 // Get ourselves back into a known-good state
196                 if (MODE_EMULATED.equals(mode)) {
197                     getDevice().executeShellCommand("sm set-emulate-fbe false");
198                     getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS);
199                     getDevice().waitForDeviceOnline();
200                 } else {
201                     getDevice().rebootUntilOnline();
202                 }
203                 getDevice().waitForDeviceAvailable();
204             }
205         }
206     }
207 
runDeviceTests(String packageName, String testClassName, String testMethodName, int... users)208     private void runDeviceTests(String packageName, String testClassName, String testMethodName,
209             int... users) throws DeviceNotAvailableException {
210         for (int user : users) {
211             Log.d(TAG, "runDeviceTests " + testMethodName + " u" + user);
212             runDeviceTests(getDevice(), packageName, testClassName, testMethodName, user, null);
213         }
214     }
215 
getFbeMode()216     private String getFbeMode() throws Exception {
217         return getDevice().executeShellCommand("sm get-fbe-mode").trim();
218     }
219 
isBootCompleted()220     private boolean isBootCompleted() throws Exception {
221         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
222         try {
223             getDevice().getIDevice().executeShellCommand("getprop sys.boot_completed", receiver);
224         } catch (AdbCommandRejectedException e) {
225             // do nothing: device might be temporarily disconnected
226             Log.d(TAG, "Ignored AdbCommandRejectedException while `getprop sys.boot_completed`");
227         }
228         String output = receiver.getOutput();
229         if (output != null) {
230             output = output.trim();
231         }
232         return "1".equals(output);
233     }
234 
isSupportedDevice()235     private boolean isSupportedDevice() throws Exception {
236         return getDevice().hasFeature(FEATURE_DEVICE_ADMIN);
237     }
238 
isAutomotiveDevice()239     private boolean isAutomotiveDevice() throws Exception {
240         return getDevice().hasFeature(FEATURE_AUTOMOTIVE);
241     }
242 
waitForBootCompleted()243     private void waitForBootCompleted() throws Exception {
244         for (int i = 0; i < 45; i++) {
245             if (isBootCompleted()) {
246                 Log.d(TAG, "Yay, system is ready!");
247                 // or is it really ready?
248                 // guard against potential USB mode switch weirdness at boot
249                 Thread.sleep(10 * 1000);
250                 return;
251             }
252             Log.d(TAG, "Waiting for system ready...");
253             Thread.sleep(1000);
254         }
255         throw new AssertionError("System failed to become ready!");
256     }
257 
258     private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
InstallMultiple()259         public InstallMultiple() {
260             super(getDevice(), getBuild(), getAbi());
261         }
262     }
263 }
264