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