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