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 com.android.cts.encryptionapp; 18 19 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; 20 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import android.content.BroadcastReceiver; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.ComponentInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.PackageManager.NameNotFoundException; 33 import android.os.Environment; 34 import android.os.StrictMode; 35 import android.os.StrictMode.ViolationInfo; 36 import android.os.SystemClock; 37 import android.os.UserManager; 38 import android.os.strictmode.CredentialProtectedWhileLockedViolation; 39 import android.os.strictmode.ImplicitDirectBootViolation; 40 import android.os.strictmode.Violation; 41 import android.provider.Settings; 42 import android.support.test.uiautomator.UiDevice; 43 import android.test.InstrumentationTestCase; 44 import android.text.format.DateUtils; 45 import android.util.Log; 46 import android.view.KeyEvent; 47 48 import java.io.File; 49 import java.util.concurrent.CountDownLatch; 50 import java.util.concurrent.LinkedBlockingQueue; 51 import java.util.concurrent.TimeUnit; 52 import java.util.function.Consumer; 53 54 public class EncryptionAppTest extends InstrumentationTestCase { 55 private static final String TAG = "EncryptionAppTest"; 56 57 private static final long TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS; 58 59 private static final String KEY_BOOT = "boot"; 60 61 private static final String TEST_PKG = "com.android.cts.encryptionapp"; 62 private static final String TEST_ACTION = "com.android.cts.encryptionapp.TEST"; 63 64 private static final String OTHER_PKG = "com.android.cts.splitapp"; 65 66 private Context mCe; 67 private Context mDe; 68 private PackageManager mPm; 69 70 private UiDevice mDevice; 71 private AwareActivity mActivity; 72 73 @Override setUp()74 public void setUp() throws Exception { 75 super.setUp(); 76 77 mCe = getInstrumentation().getContext(); 78 mDe = mCe.createDeviceProtectedStorageContext(); 79 mPm = mCe.getPackageManager(); 80 81 mDevice = UiDevice.getInstance(getInstrumentation()); 82 assertNotNull(mDevice); 83 } 84 85 @Override tearDown()86 public void tearDown() throws Exception { 87 super.tearDown(); 88 89 if (mActivity != null) { 90 mActivity.finish(); 91 } 92 } 93 testSetUp()94 public void testSetUp() throws Exception { 95 // Write both CE/DE data for ourselves 96 assertTrue("CE file", getTestFile(mCe).createNewFile()); 97 assertTrue("DE file", getTestFile(mDe).createNewFile()); 98 99 doBootCountBefore(); 100 101 mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), 102 AwareActivity.class, null); 103 mDevice.waitForIdle(); 104 105 // Set a PIN for this user 106 mDevice.executeShellCommand("settings put global require_password_to_decrypt 0"); 107 mDevice.executeShellCommand("locksettings set-disabled false"); 108 mDevice.executeShellCommand("locksettings set-pin 12345"); 109 } 110 testTearDown()111 public void testTearDown() throws Exception { 112 // Just in case, always try tearing down keyguard 113 dismissKeyguard(); 114 115 mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), 116 AwareActivity.class, null); 117 mDevice.waitForIdle(); 118 119 // Clear PIN for this user 120 mDevice.executeShellCommand("locksettings clear --old 12345"); 121 mDevice.executeShellCommand("locksettings set-disabled true"); 122 mDevice.executeShellCommand("settings delete global require_password_to_decrypt"); 123 } 124 doBootCountBefore()125 public void doBootCountBefore() throws Exception { 126 final int thisCount = getBootCount(); 127 mDe.getSharedPreferences(KEY_BOOT, 0).edit().putInt(KEY_BOOT, thisCount).commit(); 128 } 129 doBootCountAfter()130 public void doBootCountAfter() throws Exception { 131 final int lastCount = mDe.getSharedPreferences(KEY_BOOT, 0).getInt(KEY_BOOT, -1); 132 final int thisCount = getBootCount(); 133 assertTrue("Current boot count " + thisCount + " not greater than last " + lastCount, 134 thisCount > lastCount); 135 } 136 testVerifyUnlockedAndDismiss()137 public void testVerifyUnlockedAndDismiss() throws Exception { 138 doBootCountAfter(); 139 assertUnlocked(); 140 dismissKeyguard(); 141 assertUnlocked(); 142 } 143 testVerifyLockedAndDismiss()144 public void testVerifyLockedAndDismiss() throws Exception { 145 doBootCountAfter(); 146 assertLocked(); 147 148 final CountDownLatch latch = new CountDownLatch(1); 149 final BroadcastReceiver receiver = new BroadcastReceiver() { 150 @Override 151 public void onReceive(Context context, Intent intent) { 152 latch.countDown(); 153 } 154 }; 155 mDe.registerReceiver(receiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); 156 157 dismissKeyguard(); 158 159 // Dismiss keyguard should have kicked off immediate broadcast 160 assertTrue("USER_UNLOCKED", latch.await(1, TimeUnit.MINUTES)); 161 162 // And we should now be fully unlocked; we run immediately like this to 163 // avoid missing BOOT_COMPLETED due to instrumentation being torn down. 164 assertUnlocked(); 165 } 166 enterTestPin()167 private void enterTestPin() throws Exception { 168 // TODO: change the combination on my luggage 169 mDevice.waitForIdle(); 170 mDevice.pressKeyCode(KeyEvent.KEYCODE_1); 171 mDevice.pressKeyCode(KeyEvent.KEYCODE_2); 172 mDevice.pressKeyCode(KeyEvent.KEYCODE_3); 173 mDevice.pressKeyCode(KeyEvent.KEYCODE_4); 174 mDevice.pressKeyCode(KeyEvent.KEYCODE_5); 175 mDevice.waitForIdle(); 176 mDevice.pressEnter(); 177 mDevice.waitForIdle(); 178 } 179 dismissKeyguard()180 private void dismissKeyguard() throws Exception { 181 mDevice.wakeUp(); 182 mDevice.waitForIdle(); 183 mDevice.pressMenu(); 184 mDevice.waitForIdle(); 185 enterTestPin(); 186 mDevice.waitForIdle(); 187 mDevice.pressHome(); 188 mDevice.waitForIdle(); 189 } 190 assertLocked()191 public void assertLocked() throws Exception { 192 awaitBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED); 193 194 assertFalse("CE exists", getTestFile(mCe).exists()); 195 assertTrue("DE exists", getTestFile(mDe).exists()); 196 197 assertFalse("isUserUnlocked", mCe.getSystemService(UserManager.class).isUserUnlocked()); 198 assertFalse("isUserUnlocked", mDe.getSystemService(UserManager.class).isUserUnlocked()); 199 200 assertTrue("AwareProvider", AwareProvider.sCreated); 201 assertFalse("UnawareProvider", UnawareProvider.sCreated); 202 203 assertNotNull("AwareProvider", 204 mPm.resolveContentProvider("com.android.cts.encryptionapp.aware", 0)); 205 assertNull("UnawareProvider", 206 mPm.resolveContentProvider("com.android.cts.encryptionapp.unaware", 0)); 207 208 assertGetAware(true, 0); 209 assertGetAware(true, MATCH_DIRECT_BOOT_AWARE); 210 assertGetAware(false, MATCH_DIRECT_BOOT_UNAWARE); 211 assertGetAware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); 212 213 assertGetUnaware(false, 0); 214 assertGetUnaware(false, MATCH_DIRECT_BOOT_AWARE); 215 assertGetUnaware(true, MATCH_DIRECT_BOOT_UNAWARE); 216 assertGetUnaware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); 217 218 assertQuery(1, 0); 219 assertQuery(1, MATCH_DIRECT_BOOT_AWARE); 220 assertQuery(1, MATCH_DIRECT_BOOT_UNAWARE); 221 assertQuery(2, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); 222 223 if (Environment.isExternalStorageEmulated()) { 224 assertEquals(Environment.MEDIA_UNMOUNTED, Environment.getExternalStorageState()); 225 226 final File expected = null; 227 assertEquals(expected, mCe.getExternalCacheDir()); 228 assertEquals(expected, mDe.getExternalCacheDir()); 229 } 230 231 assertViolation( 232 new StrictMode.VmPolicy.Builder().detectImplicitDirectBoot() 233 .penaltyLog().build(), 234 ImplicitDirectBootViolation.class, 235 () -> { 236 final Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); 237 mCe.getPackageManager().queryBroadcastReceivers(intent, 0); 238 }); 239 240 final File ceFile = getTestFile(mCe); 241 assertViolation( 242 new StrictMode.VmPolicy.Builder().detectCredentialProtectedWhileLocked() 243 .penaltyLog().build(), 244 CredentialProtectedWhileLockedViolation.class, 245 ceFile::exists); 246 } 247 assertUnlocked()248 public void assertUnlocked() throws Exception { 249 awaitBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED); 250 awaitBroadcast(Intent.ACTION_BOOT_COMPLETED); 251 252 assertTrue("CE exists", getTestFile(mCe).exists()); 253 assertTrue("DE exists", getTestFile(mDe).exists()); 254 255 assertTrue("isUserUnlocked", mCe.getSystemService(UserManager.class).isUserUnlocked()); 256 assertTrue("isUserUnlocked", mDe.getSystemService(UserManager.class).isUserUnlocked()); 257 258 assertTrue("AwareProvider", AwareProvider.sCreated); 259 assertTrue("UnawareProvider", UnawareProvider.sCreated); 260 261 assertNotNull("AwareProvider", 262 mPm.resolveContentProvider("com.android.cts.encryptionapp.aware", 0)); 263 assertNotNull("UnawareProvider", 264 mPm.resolveContentProvider("com.android.cts.encryptionapp.unaware", 0)); 265 266 assertGetAware(true, 0); 267 assertGetAware(true, MATCH_DIRECT_BOOT_AWARE); 268 assertGetAware(false, MATCH_DIRECT_BOOT_UNAWARE); 269 assertGetAware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); 270 271 assertGetUnaware(true, 0); 272 assertGetUnaware(false, MATCH_DIRECT_BOOT_AWARE); 273 assertGetUnaware(true, MATCH_DIRECT_BOOT_UNAWARE); 274 assertGetUnaware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); 275 276 assertQuery(2, 0); 277 assertQuery(1, MATCH_DIRECT_BOOT_AWARE); 278 assertQuery(1, MATCH_DIRECT_BOOT_UNAWARE); 279 assertQuery(2, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); 280 281 if (Environment.isExternalStorageEmulated()) { 282 assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState()); 283 284 final File expected = new File( 285 "/sdcard/Android/data/com.android.cts.encryptionapp/cache"); 286 assertCanonicalEquals(expected, mCe.getExternalCacheDir()); 287 assertCanonicalEquals(expected, mDe.getExternalCacheDir()); 288 } 289 290 assertNoViolation( 291 new StrictMode.VmPolicy.Builder().detectImplicitDirectBoot() 292 .penaltyLog().build(), 293 () -> { 294 final Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); 295 mCe.getPackageManager().queryBroadcastReceivers(intent, 0); 296 }); 297 298 final File ceFile = getTestFile(mCe); 299 assertNoViolation( 300 new StrictMode.VmPolicy.Builder().detectCredentialProtectedWhileLocked() 301 .penaltyLog().build(), 302 ceFile::exists); 303 } 304 assertQuery(int count, int flags)305 private void assertQuery(int count, int flags) throws Exception { 306 final Intent intent = new Intent(TEST_ACTION); 307 assertEquals("activity", count, mPm.queryIntentActivities(intent, flags).size()); 308 assertEquals("service", count, mPm.queryIntentServices(intent, flags).size()); 309 assertEquals("provider", count, mPm.queryIntentContentProviders(intent, flags).size()); 310 assertEquals("receiver", count, mPm.queryBroadcastReceivers(intent, flags).size()); 311 } 312 assertGetUnaware(boolean visible, int flags)313 private void assertGetUnaware(boolean visible, int flags) throws Exception { 314 assertGet(visible, false, flags); 315 } 316 assertGetAware(boolean visible, int flags)317 private void assertGetAware(boolean visible, int flags) throws Exception { 318 assertGet(visible, true, flags); 319 } 320 assertCanonicalEquals(File expected, File actual)321 private void assertCanonicalEquals(File expected, File actual) throws Exception { 322 assertEquals(expected.getCanonicalFile(), actual.getCanonicalFile()); 323 } 324 buildName(String prefix, String type)325 private ComponentName buildName(String prefix, String type) { 326 return new ComponentName(TEST_PKG, TEST_PKG + "." + prefix + type); 327 } 328 assertGet(boolean visible, boolean aware, int flags)329 private void assertGet(boolean visible, boolean aware, int flags) throws Exception { 330 final String prefix = aware ? "Aware" : "Unaware"; 331 332 ComponentName name; 333 ComponentInfo info; 334 335 name = buildName(prefix, "Activity"); 336 try { 337 info = mPm.getActivityInfo(name, flags); 338 assertTrue(name + " visible", visible); 339 assertEquals(name + " directBootAware", aware, info.directBootAware); 340 } catch (NameNotFoundException e) { 341 assertFalse(name + " visible", visible); 342 } 343 344 name = buildName(prefix, "Service"); 345 try { 346 info = mPm.getServiceInfo(name, flags); 347 assertTrue(name + " visible", visible); 348 assertEquals(name + " directBootAware", aware, info.directBootAware); 349 } catch (NameNotFoundException e) { 350 assertFalse(name + " visible", visible); 351 } 352 353 name = buildName(prefix, "Provider"); 354 try { 355 info = mPm.getProviderInfo(name, flags); 356 assertTrue(name + " visible", visible); 357 assertEquals(name + " directBootAware", aware, info.directBootAware); 358 } catch (NameNotFoundException e) { 359 assertFalse(name + " visible", visible); 360 } 361 362 name = buildName(prefix, "Receiver"); 363 try { 364 info = mPm.getReceiverInfo(name, flags); 365 assertTrue(name + " visible", visible); 366 assertEquals(name + " directBootAware", aware, info.directBootAware); 367 } catch (NameNotFoundException e) { 368 assertFalse(name + " visible", visible); 369 } 370 } 371 getTestFile(Context context)372 private File getTestFile(Context context) { 373 return new File(context.getFilesDir(), "test"); 374 } 375 getBootCount()376 private int getBootCount() throws Exception { 377 return Settings.Global.getInt(mDe.getContentResolver(), Settings.Global.BOOT_COUNT); 378 } 379 awaitBroadcast(String action)380 private void awaitBroadcast(String action) throws Exception { 381 final Context otherContext = mDe.createPackageContext(OTHER_PKG, 0) 382 .createDeviceProtectedStorageContext(); 383 final File probe = new File(otherContext.getFilesDir(), 384 getBootCount() + "." + action); 385 for (int i = 0; i < 150; i++) { 386 Log.d(TAG, "Waiting for " + probe + "..."); 387 if (probe.exists()) { 388 return; 389 } 390 SystemClock.sleep(1000); 391 } 392 throw new AssertionError("Failed to find " + probe); 393 } 394 395 public interface ThrowingRunnable { run()396 void run() throws Exception; 397 } 398 assertViolation(StrictMode.VmPolicy policy, Class<? extends Violation> expected, ThrowingRunnable r)399 private static void assertViolation(StrictMode.VmPolicy policy, 400 Class<? extends Violation> expected, ThrowingRunnable r) throws Exception { 401 inspectViolation(policy, r, 402 info -> assertThat(info.getViolationClass()).isAssignableTo(expected)); 403 } 404 assertNoViolation(StrictMode.VmPolicy policy, ThrowingRunnable r)405 private static void assertNoViolation(StrictMode.VmPolicy policy, ThrowingRunnable r) 406 throws Exception { 407 inspectViolation(policy, r, 408 info -> assertWithMessage("Unexpected violation").that(info).isNull()); 409 } 410 inspectViolation(StrictMode.VmPolicy policy, ThrowingRunnable violating, Consumer<ViolationInfo> consume)411 private static void inspectViolation(StrictMode.VmPolicy policy, ThrowingRunnable violating, 412 Consumer<ViolationInfo> consume) throws Exception { 413 final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>(); 414 StrictMode.setViolationLogger(violations::add); 415 416 final StrictMode.VmPolicy original = StrictMode.getVmPolicy(); 417 try { 418 StrictMode.setVmPolicy(policy); 419 violating.run(); 420 consume.accept(violations.poll(5, TimeUnit.SECONDS)); 421 } finally { 422 StrictMode.setVmPolicy(original); 423 StrictMode.setViolationLogger(null); 424 } 425 } 426 } 427