blob: 38cdb3c7c4dc8efdbfd40ed53ed47e213a2ef704 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.encryptionapp;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Environment;
import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.KeyEvent;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class EncryptionAppTest extends InstrumentationTestCase {
private static final String TAG = "EncryptionAppTest";
private static final long TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
private static final String KEY_BOOT = "boot";
private static final String TEST_PKG = "com.android.cts.encryptionapp";
private static final String TEST_ACTION = "com.android.cts.encryptionapp.TEST";
private static final String OTHER_PKG = "com.android.cts.splitapp";
private Context mCe;
private Context mDe;
private PackageManager mPm;
private KeyguardManager mKm;
private UiDevice mDevice;
private AwareActivity mActivity;
@Override
public void setUp() throws Exception {
super.setUp();
mCe = getInstrumentation().getContext();
mDe = mCe.createDeviceProtectedStorageContext();
mPm = mCe.getPackageManager();
mKm = (KeyguardManager) mCe.getSystemService(Context.KEYGUARD_SERVICE);
mDevice = UiDevice.getInstance(getInstrumentation());
assertNotNull(mDevice);
}
@Override
public void tearDown() throws Exception {
super.tearDown();
if (mActivity != null) {
mActivity.finish();
}
}
public void testSetUp() throws Exception {
// Write both CE/DE data for ourselves
assertTrue("CE file", getTestFile(mCe).createNewFile());
assertTrue("DE file", getTestFile(mDe).createNewFile());
doBootCountBefore();
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
AwareActivity.class, null);
mDevice.waitForIdle();
// Set a PIN for this user
mDevice.executeShellCommand("settings put global require_password_to_decrypt 0");
mDevice.executeShellCommand("locksettings set-disabled false");
mDevice.executeShellCommand("locksettings set-pin 12345");
}
public void testTearDown() throws Exception {
// Just in case, always try tearing down keyguard
if (mKm.isKeyguardLocked()) {
dismissKeyguard();
}
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
AwareActivity.class, null);
mDevice.waitForIdle();
// Clear PIN for this user
mDevice.executeShellCommand("locksettings clear --old 12345");
mDevice.executeShellCommand("locksettings set-disabled true");
mDevice.executeShellCommand("settings delete global require_password_to_decrypt");
}
public void doBootCountBefore() throws Exception {
final int thisCount = getBootCount();
mDe.getSharedPreferences(KEY_BOOT, 0).edit().putInt(KEY_BOOT, thisCount).commit();
}
public void doBootCountAfter() throws Exception {
final int lastCount = mDe.getSharedPreferences(KEY_BOOT, 0).getInt(KEY_BOOT, -1);
final int thisCount = getBootCount();
assertTrue("Current boot count " + thisCount + " not greater than last " + lastCount,
thisCount > lastCount);
}
public void testVerifyUnlockedAndDismiss() throws Exception {
doBootCountAfter();
assertUnlocked();
dismissKeyguard();
assertUnlocked();
}
public void testVerifyLockedAndDismiss() throws Exception {
doBootCountAfter();
assertLocked();
final CountDownLatch latch = new CountDownLatch(1);
final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
latch.countDown();
}
};
mDe.registerReceiver(receiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
dismissKeyguard();
// Dismiss keyguard should have kicked off immediate broadcast
assertTrue("USER_UNLOCKED", latch.await(1, TimeUnit.MINUTES));
// And we should now be fully unlocked; we run immediately like this to
// avoid missing BOOT_COMPLETED due to instrumentation being torn down.
assertUnlocked();
}
private void enterTestPin() throws Exception {
// TODO: change the combination on my luggage
mDevice.waitForIdle();
mDevice.pressKeyCode(KeyEvent.KEYCODE_1);
mDevice.pressKeyCode(KeyEvent.KEYCODE_2);
mDevice.pressKeyCode(KeyEvent.KEYCODE_3);
mDevice.pressKeyCode(KeyEvent.KEYCODE_4);
mDevice.pressKeyCode(KeyEvent.KEYCODE_5);
mDevice.waitForIdle();
mDevice.pressEnter();
mDevice.waitForIdle();
}
private void dismissKeyguard() throws Exception {
mDevice.wakeUp();
mDevice.waitForIdle();
mDevice.pressMenu();
mDevice.waitForIdle();
enterTestPin();
mDevice.waitForIdle();
mDevice.pressHome();
mDevice.waitForIdle();
}
public void assertLocked() throws Exception {
awaitBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED);
assertFalse("CE exists", getTestFile(mCe).exists());
assertTrue("DE exists", getTestFile(mDe).exists());
assertFalse("isUserUnlocked", mCe.getSystemService(UserManager.class).isUserUnlocked());
assertFalse("isUserUnlocked", mDe.getSystemService(UserManager.class).isUserUnlocked());
assertTrue("AwareProvider", AwareProvider.sCreated);
assertFalse("UnawareProvider", UnawareProvider.sCreated);
assertNotNull("AwareProvider",
mPm.resolveContentProvider("com.android.cts.encryptionapp.aware", 0));
assertNull("UnawareProvider",
mPm.resolveContentProvider("com.android.cts.encryptionapp.unaware", 0));
assertGetAware(true, 0);
assertGetAware(true, MATCH_DIRECT_BOOT_AWARE);
assertGetAware(false, MATCH_DIRECT_BOOT_UNAWARE);
assertGetAware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
assertGetUnaware(false, 0);
assertGetUnaware(false, MATCH_DIRECT_BOOT_AWARE);
assertGetUnaware(true, MATCH_DIRECT_BOOT_UNAWARE);
assertGetUnaware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
assertQuery(1, 0);
assertQuery(1, MATCH_DIRECT_BOOT_AWARE);
assertQuery(1, MATCH_DIRECT_BOOT_UNAWARE);
assertQuery(2, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
if (Environment.isExternalStorageEmulated()) {
assertEquals(Environment.MEDIA_UNMOUNTED, Environment.getExternalStorageState());
final File expected = null;
assertEquals(expected, mCe.getExternalCacheDir());
assertEquals(expected, mDe.getExternalCacheDir());
}
}
public void assertUnlocked() throws Exception {
awaitBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED);
awaitBroadcast(Intent.ACTION_BOOT_COMPLETED);
assertTrue("CE exists", getTestFile(mCe).exists());
assertTrue("DE exists", getTestFile(mDe).exists());
assertTrue("isUserUnlocked", mCe.getSystemService(UserManager.class).isUserUnlocked());
assertTrue("isUserUnlocked", mDe.getSystemService(UserManager.class).isUserUnlocked());
assertTrue("AwareProvider", AwareProvider.sCreated);
assertTrue("UnawareProvider", UnawareProvider.sCreated);
assertNotNull("AwareProvider",
mPm.resolveContentProvider("com.android.cts.encryptionapp.aware", 0));
assertNotNull("UnawareProvider",
mPm.resolveContentProvider("com.android.cts.encryptionapp.unaware", 0));
assertGetAware(true, 0);
assertGetAware(true, MATCH_DIRECT_BOOT_AWARE);
assertGetAware(false, MATCH_DIRECT_BOOT_UNAWARE);
assertGetAware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
assertGetUnaware(true, 0);
assertGetUnaware(false, MATCH_DIRECT_BOOT_AWARE);
assertGetUnaware(true, MATCH_DIRECT_BOOT_UNAWARE);
assertGetUnaware(true, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
assertQuery(2, 0);
assertQuery(1, MATCH_DIRECT_BOOT_AWARE);
assertQuery(1, MATCH_DIRECT_BOOT_UNAWARE);
assertQuery(2, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
if (Environment.isExternalStorageEmulated()) {
assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
final File expected = new File(
"/sdcard/Android/data/com.android.cts.encryptionapp/cache");
assertCanonicalEquals(expected, mCe.getExternalCacheDir());
assertCanonicalEquals(expected, mDe.getExternalCacheDir());
}
}
private void assertQuery(int count, int flags) throws Exception {
final Intent intent = new Intent(TEST_ACTION);
assertEquals("activity", count, mPm.queryIntentActivities(intent, flags).size());
assertEquals("service", count, mPm.queryIntentServices(intent, flags).size());
assertEquals("provider", count, mPm.queryIntentContentProviders(intent, flags).size());
assertEquals("receiver", count, mPm.queryBroadcastReceivers(intent, flags).size());
}
private void assertGetUnaware(boolean visible, int flags) throws Exception {
assertGet(visible, false, flags);
}
private void assertGetAware(boolean visible, int flags) throws Exception {
assertGet(visible, true, flags);
}
private void assertCanonicalEquals(File expected, File actual) throws Exception {
assertEquals(expected.getCanonicalFile(), actual.getCanonicalFile());
}
private ComponentName buildName(String prefix, String type) {
return new ComponentName(TEST_PKG, TEST_PKG + "." + prefix + type);
}
private void assertGet(boolean visible, boolean aware, int flags) throws Exception {
final String prefix = aware ? "Aware" : "Unaware";
ComponentName name;
ComponentInfo info;
name = buildName(prefix, "Activity");
try {
info = mPm.getActivityInfo(name, flags);
assertTrue(name + " visible", visible);
assertEquals(name + " directBootAware", aware, info.directBootAware);
} catch (NameNotFoundException e) {
assertFalse(name + " visible", visible);
}
name = buildName(prefix, "Service");
try {
info = mPm.getServiceInfo(name, flags);
assertTrue(name + " visible", visible);
assertEquals(name + " directBootAware", aware, info.directBootAware);
} catch (NameNotFoundException e) {
assertFalse(name + " visible", visible);
}
name = buildName(prefix, "Provider");
try {
info = mPm.getProviderInfo(name, flags);
assertTrue(name + " visible", visible);
assertEquals(name + " directBootAware", aware, info.directBootAware);
} catch (NameNotFoundException e) {
assertFalse(name + " visible", visible);
}
name = buildName(prefix, "Receiver");
try {
info = mPm.getReceiverInfo(name, flags);
assertTrue(name + " visible", visible);
assertEquals(name + " directBootAware", aware, info.directBootAware);
} catch (NameNotFoundException e) {
assertFalse(name + " visible", visible);
}
}
private File getTestFile(Context context) {
return new File(context.getFilesDir(), "test");
}
private int getBootCount() throws Exception {
return Settings.Global.getInt(mDe.getContentResolver(), Settings.Global.BOOT_COUNT);
}
private void awaitBroadcast(String action) throws Exception {
final Context otherContext = mDe.createPackageContext(OTHER_PKG, 0)
.createDeviceProtectedStorageContext();
final File probe = new File(otherContext.getFilesDir(),
getBootCount() + "." + action);
for (int i = 0; i < 150; i++) {
Log.d(TAG, "Waiting for " + probe + "...");
if (probe.exists()) {
return;
}
SystemClock.sleep(1000);
}
throw new AssertionError("Failed to find " + probe);
}
}