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