• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.accessibilityservice.AccessibilityService;
26 import android.app.KeyguardManager;
27 import android.content.BroadcastReceiver;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.ComponentInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.database.Cursor;
36 import android.net.Uri;
37 import android.os.Environment;
38 import android.os.PowerManager;
39 import android.os.StrictMode;
40 import android.os.StrictMode.ViolationInfo;
41 import android.os.SystemClock;
42 import android.os.UserManager;
43 import android.os.strictmode.CredentialProtectedWhileLockedViolation;
44 import android.os.strictmode.ImplicitDirectBootViolation;
45 import android.os.strictmode.Violation;
46 import android.provider.Settings;
47 import android.test.InstrumentationTestCase;
48 import android.util.Log;
49 import android.view.KeyEvent;
50 
51 import androidx.test.uiautomator.UiDevice;
52 
53 import com.android.compatibility.common.util.TestUtils;
54 
55 import java.io.File;
56 import java.util.Arrays;
57 import java.util.concurrent.CountDownLatch;
58 import java.util.concurrent.LinkedBlockingQueue;
59 import java.util.concurrent.TimeUnit;
60 import java.util.function.BooleanSupplier;
61 import java.util.function.Consumer;
62 
63 public class EncryptionAppTest extends InstrumentationTestCase {
64     private static final String TAG = "EncryptionAppTest";
65 
66     private static final String KEY_BOOT = "boot";
67 
68     private static final String TEST_PKG = "com.android.cts.encryptionapp";
69     private static final String TEST_ACTION = "com.android.cts.encryptionapp.TEST";
70 
71     private static final String OTHER_PKG = "com.android.cts.splitapp";
72 
73     private static final int BOOT_TIMEOUT_SECONDS = 150;
74     private static final int UNLOCK_SCREEN_START_TIME_SECONDS = 10;
75 
76     private static final Uri FILE_INFO_URI = Uri.parse("content://" + OTHER_PKG + "/files");
77 
78     private Context mCe;
79     private Context mDe;
80     private PackageManager mPm;
81 
82     private UiDevice mDevice;
83     private AwareActivity mActivity;
84 
85     @Override
setUp()86     public void setUp() throws Exception {
87         super.setUp();
88 
89         mCe = getInstrumentation().getContext();
90         mDe = mCe.createDeviceProtectedStorageContext();
91         mPm = mCe.getPackageManager();
92 
93         mDevice = UiDevice.getInstance(getInstrumentation());
94         assertNotNull(mDevice);
95     }
96 
97     @Override
tearDown()98     public void tearDown() throws Exception {
99         super.tearDown();
100 
101         if (mActivity != null) {
102             mActivity.finish();
103         }
104     }
105 
testSetUp()106     public void testSetUp() throws Exception {
107         // Write both CE/DE data for ourselves
108         assertTrue("CE file", getTestFile(mCe).createNewFile());
109         assertTrue("DE file", getTestFile(mDe).createNewFile());
110 
111         doBootCountBefore();
112 
113         mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
114                 AwareActivity.class, null);
115         mDevice.waitForIdle();
116 
117         // Set a PIN for this user
118         mDevice.executeShellCommand("locksettings set-disabled false");
119         String output = mDevice.executeShellCommand("locksettings set-pin 1234");
120         assertTrue("set-pin failed. Output: " + output, output.contains("1234"));
121 
122         // Clear all other requests for lskf from the system.
123         String clearOutput = mDevice.executeShellCommand("cmd recovery clear-lskf android");
124         assertTrue("clear-lskf failed for package android. Output: " + clearOutput,
125                 clearOutput.contains("success"));
126     }
127 
testTearDown()128     public void testTearDown() throws Exception {
129         // Since there's not a good way to check whether the keyguard is already dismissed, summon
130         // the keyguard and dismiss it.
131         summonKeyguard();
132         dismissKeyguard();
133 
134         mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
135                 AwareActivity.class, null);
136         mDevice.waitForIdle();
137 
138         // Clear PIN for this user
139         mDevice.executeShellCommand("locksettings clear --old 1234");
140         mDevice.executeShellCommand("locksettings set-disabled true");
141     }
142 
testLockScreen()143     public void testLockScreen() throws Exception {
144         summonKeyguard();
145     }
146 
testUnlockScreen()147     public void testUnlockScreen() throws Exception {
148         dismissKeyguard();
149     }
150 
doBootCountBefore()151     public void doBootCountBefore() throws Exception {
152         final int thisCount = getBootCount();
153         mDe.getSharedPreferences(KEY_BOOT, 0).edit().putInt(KEY_BOOT, thisCount).commit();
154     }
155 
doBootCountAfter()156     public void doBootCountAfter() throws Exception {
157         final int lastCount = mDe.getSharedPreferences(KEY_BOOT, 0).getInt(KEY_BOOT, -1);
158         final int thisCount = getBootCount();
159         assertTrue("Current boot count " + thisCount + " not greater than last " + lastCount,
160                 thisCount > lastCount);
161     }
162 
testCheckServiceInteraction()163     public void testCheckServiceInteraction() {
164         boolean wrapCalled =
165                 mDe.getSharedPreferences(RebootEscrowFakeService.SERVICE_PREFS, 0)
166                         .getBoolean("WRAP_CALLED", false);
167         assertTrue(wrapCalled);
168 
169         boolean unwrapCalled =
170                 mDe.getSharedPreferences(RebootEscrowFakeService.SERVICE_PREFS, 0)
171                         .getBoolean("UNWRAP_CALLED", false);
172         assertTrue(unwrapCalled);
173     }
174 
testVerifyUnlockedAndDismiss()175     public void testVerifyUnlockedAndDismiss() throws Exception {
176         doBootCountAfter();
177         assertUnlocked();
178         dismissKeyguard();
179         assertUnlocked();
180     }
181 
testVerifyLockedAndDismiss()182     public void testVerifyLockedAndDismiss() throws Exception {
183         doBootCountAfter();
184         assertLocked();
185 
186         final CountDownLatch latch = new CountDownLatch(1);
187         final BroadcastReceiver receiver = new BroadcastReceiver() {
188             @Override
189             public void onReceive(Context context, Intent intent) {
190                 latch.countDown();
191             }
192         };
193         mDe.registerReceiver(receiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
194 
195         dismissKeyguard();
196 
197         // Dismiss keyguard should have kicked off immediate broadcast
198         assertTrue("USER_UNLOCKED", latch.await(1, TimeUnit.MINUTES));
199 
200         // And we should now be fully unlocked; we run immediately like this to
201         // avoid missing BOOT_COMPLETED due to instrumentation being torn down.
202         assertUnlocked();
203     }
204 
enterTestPin()205     private void enterTestPin() throws Exception {
206         // TODO: change the combination on my luggage
207 
208         // Give enough time for the lock screen to show up in the UI.
209         SystemClock.sleep(UNLOCK_SCREEN_START_TIME_SECONDS * 1000);
210         mDevice.waitForIdle();
211         mDevice.pressKeyCode(KeyEvent.KEYCODE_1);
212         mDevice.pressKeyCode(KeyEvent.KEYCODE_2);
213         mDevice.pressKeyCode(KeyEvent.KEYCODE_3);
214         mDevice.pressKeyCode(KeyEvent.KEYCODE_4);
215         mDevice.waitForIdle();
216         mDevice.pressEnter();
217         mDevice.waitForIdle();
218 
219         // TODO(189853309) make sure RebootEscrowManager get the unlock event
220     }
221 
dismissKeyguard()222     private void dismissKeyguard() throws Exception {
223         mDevice.waitForIdle();
224         mDevice.wakeUp();
225         mDevice.waitForIdle();
226 
227         // Launch activity because KeyguardManager#requestDismissKeyguard()
228         // must be invoked with an activity.
229         mActivity =
230             launchActivity(
231                 getInstrumentation().getTargetContext().getPackageName(),
232                 AwareActivity.class,
233                 null);
234         KeyguardManager keyguardManager =
235             mDe.getSystemService(KeyguardManager.class);
236         keyguardManager.requestDismissKeyguard(mActivity, null);
237         // Close activity to avoid interference with other parts of the test
238         // that also use this activity.
239         mActivity.finish();
240         mActivity = null;
241 
242         enterTestPin();
243         mDevice.waitForIdle();
244         mDevice.pressHome();
245         mDevice.waitForIdle();
246     }
247 
waitFor(String msg, BooleanSupplier waitFor)248     private void waitFor(String msg, BooleanSupplier waitFor) {
249         int retry = 1;
250         do {
251             if (waitFor.getAsBoolean()) {
252                 return;
253             }
254             Log.d(TAG, msg + " retry=" + retry);
255             SystemClock.sleep(200);
256         } while (retry++ < 5);
257         if (!waitFor.getAsBoolean()) {
258             fail(msg + " FAILED");
259         }
260     }
261 
summonKeyguard()262     private void summonKeyguard() throws Exception {
263         final KeyguardManager keyguardManager =
264             mDe.getSystemService(KeyguardManager.class);
265         mDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
266         getInstrumentation().getUiAutomation().performGlobalAction(
267                 AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
268         waitFor(
269             "display to turn off",
270             () -> keyguardManager != null && keyguardManager.isKeyguardLocked());
271     }
272 
assertLocked()273     public void assertLocked() throws Exception {
274         awaitBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED);
275 
276         assertFalse("CE exists", getTestFile(mCe).exists());
277         assertTrue("DE exists", getTestFile(mDe).exists());
278 
279         assertFalse("isUserUnlocked", mCe.getSystemService(UserManager.class).isUserUnlocked());
280         assertFalse("isUserUnlocked", mDe.getSystemService(UserManager.class).isUserUnlocked());
281 
282         assertTrue("AwareProvider", AwareProvider.sCreated);
283         assertFalse("UnawareProvider", UnawareProvider.sCreated);
284 
285         assertNotNull("AwareProvider",
286                 mPm.resolveContentProvider("com.android.cts.encryptionapp.aware", 0));
287         assertNull("UnawareProvider",
288                 mPm.resolveContentProvider("com.android.cts.encryptionapp.unaware", 0));
289 
290         assertGetAware(true, 0);
291         assertGetAware(true, MATCH_DIRECT_BOOT_AWARE);
292         assertGetAware(false, MATCH_DIRECT_BOOT_UNAWARE);
293         assertGetAware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
294 
295         assertGetUnaware(false, 0);
296         assertGetUnaware(false, MATCH_DIRECT_BOOT_AWARE);
297         assertGetUnaware(true, MATCH_DIRECT_BOOT_UNAWARE);
298         assertGetUnaware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
299 
300         assertQuery(1, 0);
301         assertQuery(1, MATCH_DIRECT_BOOT_AWARE);
302         assertQuery(1, MATCH_DIRECT_BOOT_UNAWARE);
303         assertQuery(2, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
304 
305         if (Environment.isExternalStorageEmulated()) {
306             assertThat(Environment.getExternalStorageState())
307                     .isIn(Arrays.asList(Environment.MEDIA_UNMOUNTED, Environment.MEDIA_REMOVED));
308 
309             final File expected = null;
310             assertEquals(expected, mCe.getExternalCacheDir());
311             assertEquals(expected, mDe.getExternalCacheDir());
312         }
313 
314         assertViolation(
315                 new StrictMode.VmPolicy.Builder().detectImplicitDirectBoot()
316                         .penaltyLog().build(),
317                 ImplicitDirectBootViolation.class,
318                 () -> {
319                     final Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
320                     mCe.getPackageManager().queryBroadcastReceivers(intent, 0);
321                 });
322 
323         final File ceFile = getTestFile(mCe);
324         assertViolation(
325                 new StrictMode.VmPolicy.Builder().detectCredentialProtectedWhileLocked()
326                         .penaltyLog().build(),
327                 CredentialProtectedWhileLockedViolation.class,
328                 ceFile::exists);
329     }
330 
assertUnlocked()331     public void assertUnlocked() throws Exception {
332         awaitBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED);
333         awaitBroadcast(Intent.ACTION_BOOT_COMPLETED);
334 
335         assertTrue("CE exists", getTestFile(mCe).exists());
336         assertTrue("DE exists", getTestFile(mDe).exists());
337 
338         assertTrue("isUserUnlocked", mCe.getSystemService(UserManager.class).isUserUnlocked());
339         assertTrue("isUserUnlocked", mDe.getSystemService(UserManager.class).isUserUnlocked());
340 
341         assertTrue("AwareProvider", AwareProvider.sCreated);
342         assertTrue("UnawareProvider", UnawareProvider.sCreated);
343 
344         assertNotNull("AwareProvider",
345                 mPm.resolveContentProvider("com.android.cts.encryptionapp.aware", 0));
346         assertNotNull("UnawareProvider",
347                 mPm.resolveContentProvider("com.android.cts.encryptionapp.unaware", 0));
348 
349         assertGetAware(true, 0);
350         assertGetAware(true, MATCH_DIRECT_BOOT_AWARE);
351         assertGetAware(false, MATCH_DIRECT_BOOT_UNAWARE);
352         assertGetAware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
353 
354         assertGetUnaware(true, 0);
355         assertGetUnaware(false, MATCH_DIRECT_BOOT_AWARE);
356         assertGetUnaware(true, MATCH_DIRECT_BOOT_UNAWARE);
357         assertGetUnaware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
358 
359         assertQuery(2, 0);
360         assertQuery(1, MATCH_DIRECT_BOOT_AWARE);
361         assertQuery(1, MATCH_DIRECT_BOOT_UNAWARE);
362         assertQuery(2, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
363 
364         if (Environment.isExternalStorageEmulated()) {
365             pollForExternalStorageMountedState();
366             assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
367 
368             final File expected = new File(
369                     "/sdcard/Android/data/com.android.cts.encryptionapp/cache");
370             assertCanonicalEquals(expected, mCe.getExternalCacheDir());
371             assertCanonicalEquals(expected, mDe.getExternalCacheDir());
372         }
373 
374         assertNoViolation(
375                 new StrictMode.VmPolicy.Builder().detectImplicitDirectBoot()
376                         .penaltyLog().build(),
377                 () -> {
378                     final Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
379                     mCe.getPackageManager().queryBroadcastReceivers(intent, 0);
380                 });
381 
382         final File ceFile = getTestFile(mCe);
383         assertNoViolation(
384                 new StrictMode.VmPolicy.Builder().detectCredentialProtectedWhileLocked()
385                         .penaltyLog().build(),
386                 ceFile::exists);
387     }
388 
pollForExternalStorageMountedState()389     private void pollForExternalStorageMountedState() {
390         for (int i = 0; i < 10; i++) {
391             if (Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
392                 break;
393             }
394             SystemClock.sleep(500);
395         }
396     }
397 
assertQuery(int count, int flags)398     private void assertQuery(int count, int flags) throws Exception {
399         final Intent intent = new Intent(TEST_ACTION);
400         assertEquals("activity", count, mPm.queryIntentActivities(intent, flags).size());
401         assertEquals("service", count, mPm.queryIntentServices(intent, flags).size());
402         assertEquals("provider", count, mPm.queryIntentContentProviders(intent, flags).size());
403         assertEquals("receiver", count, mPm.queryBroadcastReceivers(intent, flags).size());
404     }
405 
assertGetUnaware(boolean visible, int flags)406     private void assertGetUnaware(boolean visible, int flags) throws Exception {
407         assertGet(visible, false, flags);
408     }
409 
assertGetAware(boolean visible, int flags)410     private void assertGetAware(boolean visible, int flags) throws Exception {
411         assertGet(visible, true, flags);
412     }
413 
assertCanonicalEquals(File expected, File actual)414     private void assertCanonicalEquals(File expected, File actual) throws Exception {
415         assertEquals(expected.getCanonicalFile(), actual.getCanonicalFile());
416     }
417 
buildName(String prefix, String type)418     private ComponentName buildName(String prefix, String type) {
419         return new ComponentName(TEST_PKG, TEST_PKG + "." + prefix + type);
420     }
421 
assertGet(boolean visible, boolean aware, int flags)422     private void assertGet(boolean visible, boolean aware, int flags) throws Exception {
423         final String prefix = aware ? "Aware" : "Unaware";
424 
425         ComponentName name;
426         ComponentInfo info;
427 
428         name = buildName(prefix, "Activity");
429         try {
430             info = mPm.getActivityInfo(name, flags);
431             assertTrue(name + " visible", visible);
432             assertEquals(name + " directBootAware", aware, info.directBootAware);
433         } catch (NameNotFoundException e) {
434             assertFalse(name + " visible", visible);
435         }
436 
437         name = buildName(prefix, "Service");
438         try {
439             info = mPm.getServiceInfo(name, flags);
440             assertTrue(name + " visible", visible);
441             assertEquals(name + " directBootAware", aware, info.directBootAware);
442         } catch (NameNotFoundException e) {
443             assertFalse(name + " visible", visible);
444         }
445 
446         name = buildName(prefix, "Provider");
447         try {
448             info = mPm.getProviderInfo(name, flags);
449             assertTrue(name + " visible", visible);
450             assertEquals(name + " directBootAware", aware, info.directBootAware);
451         } catch (NameNotFoundException e) {
452             assertFalse(name + " visible", visible);
453         }
454 
455         name = buildName(prefix, "Receiver");
456         try {
457             info = mPm.getReceiverInfo(name, flags);
458             assertTrue(name + " visible", visible);
459             assertEquals(name + " directBootAware", aware, info.directBootAware);
460         } catch (NameNotFoundException e) {
461             assertFalse(name + " visible", visible);
462         }
463     }
464 
getTestFile(Context context)465     private File getTestFile(Context context) {
466         return new File(context.getFilesDir(), "test");
467     }
468 
getBootCount()469     private int getBootCount() throws Exception {
470         return Settings.Global.getInt(mDe.getContentResolver(), Settings.Global.BOOT_COUNT);
471     }
472 
queryFileExists(Uri fileUri)473     private boolean queryFileExists(Uri fileUri) {
474         Cursor c = mDe.getContentResolver().query(fileUri, null, null, null, null);
475         if (c == null) {
476             Log.w(TAG, "Couldn't query for file " + fileUri + "; returning false");
477             return false;
478         }
479 
480         c.moveToFirst();
481 
482         int colIndex = c.getColumnIndex("exists");
483         if (colIndex < 0) {
484             Log.e(TAG, "Column 'exists' does not exist; returning false");
485             return false;
486         }
487 
488         return c.getInt(colIndex) == 1;
489     }
490 
awaitBroadcast(String action)491     private void awaitBroadcast(String action) throws Exception {
492         String fileName = getBootCount() + "." + action;
493         Uri fileUri = FILE_INFO_URI.buildUpon().appendPath(fileName).build();
494 
495         TestUtils.waitUntil("Didn't receive broadcast " + action + " for boot " + getBootCount(),
496                 BOOT_TIMEOUT_SECONDS, () -> queryFileExists(fileUri));
497     }
498 
499     public interface ThrowingRunnable {
run()500         void run() throws Exception;
501     }
502 
assertViolation(StrictMode.VmPolicy policy, Class<? extends Violation> expected, ThrowingRunnable r)503     private static void assertViolation(StrictMode.VmPolicy policy,
504             Class<? extends Violation> expected, ThrowingRunnable r) throws Exception {
505         inspectViolation(policy, r,
506                 info -> assertThat(info.getViolationClass()).isAssignableTo(expected));
507     }
508 
assertNoViolation(StrictMode.VmPolicy policy, ThrowingRunnable r)509     private static void assertNoViolation(StrictMode.VmPolicy policy, ThrowingRunnable r)
510             throws Exception {
511         inspectViolation(policy, r,
512                 info -> assertWithMessage("Unexpected violation").that(info).isNull());
513     }
514 
inspectViolation(StrictMode.VmPolicy policy, ThrowingRunnable violating, Consumer<ViolationInfo> consume)515     private static void inspectViolation(StrictMode.VmPolicy policy, ThrowingRunnable violating,
516             Consumer<ViolationInfo> consume) throws Exception {
517         final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
518         StrictMode.setViolationLogger(violations::add);
519 
520         final StrictMode.VmPolicy original = StrictMode.getVmPolicy();
521         try {
522             StrictMode.setVmPolicy(policy);
523             violating.run();
524             consume.accept(violations.poll(5, TimeUnit.SECONDS));
525         } finally {
526             StrictMode.setVmPolicy(original);
527             StrictMode.setViolationLogger(null);
528         }
529     }
530 }
531