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