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