MonitoringCertTask no longer relies on software.device_admin

Added a test to validate that it still works the way it should before
and after the change.

Bug: 33258404
Bug: 35196414
Fix: 35129745
Test: runtest -x services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
Test: also manual, instructions:
Test: (1) Disable software.device_admin from tablet_core_hardware, rebuild.
Test: (2) Install CA cert. Notification should appear.
Test: (3) Reboot. Notification should still be there.
Change-Id: Id992725c1844a2fffbde4d8acaba531e99f853ad
This commit is contained in:
Robin Lee
2017-02-08 21:27:02 +00:00
parent f9a55d42d2
commit 7f5c91c6bc
6 changed files with 339 additions and 133 deletions

View File

@@ -588,7 +588,7 @@ public final class KeyChain {
* @hide for reuse by CertInstaller and Settings.
* @see KeyChain#bind
*/
public final static class KeyChainConnection implements Closeable {
public static class KeyChainConnection implements Closeable {
private final Context context;
private final ServiceConnection serviceConnection;
private final IKeyChainService service;

View File

@@ -99,6 +99,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -208,7 +209,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String LOG_TAG = "DevicePolicyManager";
protected static final String LOG_TAG = "DevicePolicyManager";
private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
@@ -252,7 +253,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
= "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
private static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
private static final int PROFILE_WIPED_NOTIFICATION_ID = 1001;
private static final int NETWORK_LOGGING_NOTIFICATION_ID = 1002;
@@ -409,6 +409,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
};
/** Listens only if mHasFeature == true. */
private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
@Override
@@ -420,6 +421,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
};
/** Listens only if mHasFeature == true. */
private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
@Override
@@ -513,7 +515,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final Handler mHandler;
BroadcastReceiver mReceiver = new BroadcastReceiver() {
/** Listens on any device, even when mHasFeature == false. */
final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (StorageManager.inCryptKeeperBounce()) {
return;
}
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
new MonitoringCertNotificationTask(DevicePolicyManagerService.this, mInjector)
.execute(userHandle);
}
};
/** Listens only if mHasFeature == true. */
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
@@ -559,14 +575,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
});
}
if (Intent.ACTION_USER_UNLOCKED.equals(action)
|| Intent.ACTION_USER_STARTED.equals(action)
|| KeyChain.ACTION_TRUST_STORE_CHANGED.equals(action)) {
if (!StorageManager.inCryptKeeperBounce()) {
new MonitoringCertNotificationTask().execute(
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL));
}
}
if (Intent.ACTION_USER_ADDED.equals(action)) {
sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
synchronized (DevicePolicyManagerService.this) {
@@ -1490,6 +1499,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mContext = context;
}
Context createContextAsUser(UserHandle user) throws PackageManager.NameNotFoundException {
final String packageName = mContext.getPackageName();
return mContext.createPackageContextAsUser(packageName, 0, user);
}
Resources getResources() {
return mContext.getResources();
}
Owners newOwners() {
return new Owners(getUserManager(), getUserManagerInternal(),
getPackageManagerInternal());
@@ -1725,6 +1743,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean securityLogIsLoggingEnabled() {
return SecurityLog.isLoggingEnabled();
}
KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException {
return KeyChain.bindAsUser(mContext, user);
}
}
/**
@@ -1755,18 +1777,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
mIsWatch = mInjector.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_WATCH);
// Broadcast filter for changes to the trusted certificate store. These changes get a
// separate intent filter so we can listen to them even when device_admin is off.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_STARTED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mRootCaReceiver, UserHandle.ALL, filter, null, mHandler);
if (!mHasFeature) {
// Skip the rest of the initialization
return;
}
IntentFilter filter = new IntentFilter();
filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
@@ -2946,125 +2977,34 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
private class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
@Override
protected Void doInBackground(Integer... params) {
int userHandle = params[0];
/**
* Remove deleted CA certificates from the "approved" list for a particular user, counting
* the number still remaining to approve.
*
* @param userHandle user to check for. This must be a real user and not, for example,
* {@link UserHandle#ALL}.
* @param installedCertificates the full set of certificate authorities currently installed for
* {@param userHandle}. After calling this function, {@code mAcceptedCaCertificates} will
* correspond to some subset of this.
*
* @return number of certificates yet to be approved by {@param userHandle}.
*/
protected synchronized int retainAcceptedCertificates(final UserHandle userHandle,
final @NonNull Collection<String> installedCertificates) {
enforceManageUsers();
if (userHandle == UserHandle.USER_ALL) {
for (UserInfo userInfo : mUserManager.getUsers(true)) {
manageNotification(userInfo.getUserHandle());
}
} else {
manageNotification(UserHandle.of(userHandle));
}
return null;
}
if (!mHasFeature) {
return installedCertificates.size();
} else {
final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
private void manageNotification(UserHandle userHandle) {
if (!mUserManager.isUserUnlocked(userHandle)) {
return;
// Remove deleted certificates. Flush xml if necessary.
if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) {
saveSettingsLocked(userHandle.getIdentifier());
}
// Call out to KeyChain to check for CAs which are waiting for approval.
final List<String> pendingCertificates;
try {
pendingCertificates = getInstalledCaCertificates(userHandle);
} catch (RemoteException | RuntimeException e) {
Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
return;
}
synchronized (DevicePolicyManagerService.this) {
final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
// Remove deleted certificates. Flush xml if necessary.
if (policy.mAcceptedCaCertificates.retainAll(pendingCertificates)) {
saveSettingsLocked(userHandle.getIdentifier());
}
// Trim to approved certificates.
pendingCertificates.removeAll(policy.mAcceptedCaCertificates);
}
if (pendingCertificates.isEmpty()) {
mInjector.getNotificationManager().cancelAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
return;
}
// Build and show a warning notification
int smallIconId;
String contentText;
int parentUserId = userHandle.getIdentifier();
if (getProfileOwner(userHandle.getIdentifier()) != null) {
contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed,
getProfileOwnerName(userHandle.getIdentifier()));
smallIconId = R.drawable.stat_sys_certificate_info;
parentUserId = getProfileParentId(userHandle.getIdentifier());
} else if (getDeviceOwnerUserId() == userHandle.getIdentifier()) {
contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed,
getDeviceOwnerName());
smallIconId = R.drawable.stat_sys_certificate_info;
} else {
contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_unknown);
smallIconId = android.R.drawable.stat_sys_warning;
}
final int numberOfCertificates = pendingCertificates.size();
Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
dialogIntent.setPackage("com.android.settings");
dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, numberOfCertificates);
dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
PendingIntent notifyIntent = PendingIntent.getActivityAsUser(mContext, 0,
dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
new UserHandle(parentUserId));
final Context userContext;
try {
final String packageName = mContext.getPackageName();
userContext = mContext.createPackageContextAsUser(packageName, 0, userHandle);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
return;
}
final Notification noti = new Notification.Builder(userContext)
.setSmallIcon(smallIconId)
.setContentTitle(mContext.getResources().getQuantityText(
R.plurals.ssl_ca_cert_warning, numberOfCertificates))
.setContentText(contentText)
.setContentIntent(notifyIntent)
.setPriority(Notification.PRIORITY_HIGH)
.setShowWhen(false)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
.build();
mInjector.getNotificationManager().notifyAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
}
private List<String> getInstalledCaCertificates(UserHandle userHandle)
throws RemoteException, RuntimeException {
KeyChainConnection conn = null;
try {
conn = KeyChain.bindAsUser(mContext, userHandle);
List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList();
List<String> result = new ArrayList<>(aliases.size());
for (int i = 0; i < aliases.size(); i++) {
result.add(aliases.get(i).string);
}
return result;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} catch (AssertionError e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
conn.close();
}
}
// Trim approved certificates from the count.
return installedCertificates.size() - policy.mAcceptedCaCertificates.size();
}
}
@@ -4629,7 +4569,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
saveSettingsLocked(userId);
}
new MonitoringCertNotificationTask().execute(userId);
new MonitoringCertNotificationTask(this, mInjector).execute(userId);
return true;
}
@@ -4653,7 +4593,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
saveSettingsLocked(userInfo.id);
}
new MonitoringCertNotificationTask().execute(userInfo.id);
new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id);
}
}
}
@@ -7148,7 +7088,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
}
private int getProfileParentId(int userHandle) {
protected int getProfileParentId(int userHandle) {
final long ident = mInjector.binderClearCallingIdentity();
try {
UserInfo parentUser = mUserManager.getProfileParent(userHandle);

View File

@@ -0,0 +1,166 @@
/*
* Copyright (C) 2017 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.server.devicepolicy;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.security.KeyChain.KeyChainConnection;
import android.util.Log;
import com.android.internal.R;
import com.android.internal.util.ParcelableString;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
protected static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
public MonitoringCertNotificationTask(final DevicePolicyManagerService service,
final DevicePolicyManagerService.Injector injector) {
super();
mService = service;
mInjector = injector;
}
@Override
protected Void doInBackground(Integer... params) {
int userHandle = params[0];
if (userHandle == UserHandle.USER_ALL) {
for (UserInfo userInfo : mInjector.getUserManager().getUsers(true)) {
repostOrClearNotification(userInfo.getUserHandle());
}
} else {
repostOrClearNotification(UserHandle.of(userHandle));
}
return null;
}
private void repostOrClearNotification(UserHandle userHandle) {
if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
return;
}
// Call out to KeyChain to check for CAs which are waiting for approval.
final int pendingCertificateCount;
try {
pendingCertificateCount = mService.retainAcceptedCertificates(
userHandle, getInstalledCaCertificates(userHandle));
} catch (RemoteException | RuntimeException e) {
Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
return;
}
if (pendingCertificateCount != 0) {
showNotification(userHandle, pendingCertificateCount);
} else {
mInjector.getNotificationManager().cancelAsUser(
LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle);
}
}
private void showNotification(UserHandle userHandle, int pendingCertificateCount) {
// Create a context for the target user.
final Context userContext;
try {
userContext = mInjector.createContextAsUser(userHandle);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
return;
}
// Build and show a warning notification
int smallIconId;
String contentText;
int parentUserId = userHandle.getIdentifier();
Resources resources = mInjector.getResources();
if (mService.getProfileOwner(userHandle.getIdentifier()) != null) {
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
mService.getProfileOwnerName(userHandle.getIdentifier()));
smallIconId = R.drawable.stat_sys_certificate_info;
parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
} else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
mService.getDeviceOwnerName());
smallIconId = R.drawable.stat_sys_certificate_info;
} else {
contentText = resources.getString(R.string.ssl_ca_cert_noti_by_unknown);
smallIconId = android.R.drawable.stat_sys_warning;
}
Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// TODO this next line is taken from original notification code in
// {@link DevicePolicyManagerService} but not a very good way of doing it. Do it better.
dialogIntent.setPackage("com.android.settings");
dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount);
dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
PendingIntent notifyIntent = PendingIntent.getActivityAsUser(userContext, 0,
dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
UserHandle.of(parentUserId));
final Notification noti = new Notification.Builder(userContext)
.setSmallIcon(smallIconId)
.setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
pendingCertificateCount))
.setContentText(contentText)
.setContentIntent(notifyIntent)
.setPriority(Notification.PRIORITY_HIGH)
.setShowWhen(false)
.setColor(R.color.system_notification_accent_color)
.build();
mInjector.getNotificationManager().notifyAsUser(
LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
}
private List<String> getInstalledCaCertificates(UserHandle userHandle)
throws RemoteException, RuntimeException {
try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) {
List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList();
List<String> result = new ArrayList<>(aliases.size());
for (int i = 0; i < aliases.size(); i++) {
result.add(aliases.get(i).string);
}
return result;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} catch (AssertionError e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -29,6 +29,7 @@ import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.security.KeyChain;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Pair;
@@ -375,5 +376,10 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
boolean isBuildDebuggable() {
return context.buildMock.isDebuggable;
}
@Override
KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) {
return context.keyChainConnection;
}
}
}

View File

@@ -21,6 +21,8 @@ import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
import android.Manifest.permission;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -32,6 +34,7 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Color;
import android.net.IIpConnectivityMetrics;
@@ -52,10 +55,13 @@ import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.R;
import com.android.internal.util.ParcelableString;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserRestrictionsUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -76,13 +82,16 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -1193,6 +1202,53 @@ public class DevicePolicyManagerTest extends DpmTestBase {
return uid;
}
public void testCertificateDisclosure() throws Exception {
final int userId = DpmMockContext.CALLER_USER_HANDLE;
final UserHandle user = UserHandle.of(userId);
mContext.applicationInfo = new ApplicationInfo();
mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.packageName = "com.android.frameworks.servicestests";
mContext.userContexts.put(user, mContext);
when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
ParceledListSlice<ParcelableString> oneCert = asSlice(new String[] {"1"});
ParceledListSlice<ParcelableString> fourCerts = asSlice(new String[] {"1", "2", "3", "4"});
final String TEST_STRING = "Test for exactly 2 certs out of 4";
doReturn(TEST_STRING).when(mContext.resources).getQuantityText(anyInt(), eq(2));
// Given that we have exactly one certificate installed,
when(mContext.keyChainConnection.getService().getUserCaAliases()).thenReturn(oneCert);
// when that certificate is approved,
dpms.approveCaCert(oneCert.getList().get(0).string, userId, true);
// a notification should not be shown.
verify(mContext.notificationManager, timeout(1000))
.cancelAsUser(anyString(), anyInt(), eq(user));
// Given that we have four certificates installed,
when(mContext.keyChainConnection.getService().getUserCaAliases()).thenReturn(fourCerts);
// when two of them are approved (one of them approved twice hence no action),
dpms.approveCaCert(fourCerts.getList().get(0).string, userId, true);
dpms.approveCaCert(fourCerts.getList().get(1).string, userId, true);
// a notification should be shown saying that there are two certificates left to approve.
verify(mContext.notificationManager, timeout(1000))
.notifyAsUser(anyString(), anyInt(), argThat(
new BaseMatcher<Notification>() {
@Override
public boolean matches(Object item) {
final Notification noti = (Notification) item;
return TEST_STRING.equals(
noti.extras.getString(Notification.EXTRA_TITLE));
}
@Override
public void describeTo(Description description) {
description.appendText(
"Notification{title=\"" + TEST_STRING + "\"}");
}
}), eq(user));
}
/**
* Simple test for delegate set/get and general delegation. Tests verifying that delegated
* privileges can acually be exercised by a delegate are not covered here.
@@ -3734,4 +3790,20 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.setProfileOwner(admin, null, userId));
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
/**
* Convert String[] to ParceledListSlice&lt;ParcelableString&gt;.
* <p>
* TODO: This shouldn't be necessary. If ParcelableString does need to exist, it also needs
* a real constructor.
*/
private static ParceledListSlice<ParcelableString> asSlice(String[] s) {
List<ParcelableString> list = new ArrayList<>(s.length);
for (int i = 0; i < s.length; i++) {
ParcelableString item = new ParcelableString();
item.string = s[i];
list.add(i, item);
}
return new ParceledListSlice<ParcelableString>(list);
}
}

View File

@@ -43,9 +43,11 @@ import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.security.KeyChain;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.view.IWindowManager;
import com.android.internal.widget.LockPatternUtils;
@@ -58,10 +60,12 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -290,6 +294,7 @@ public class DpmMockContext extends MockContext {
public final TelephonyManager telephonyManager;
public final AccountManager accountManager;
public final AlarmManager alarmManager;
public final KeyChain.KeyChainConnection keyChainConnection;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -300,6 +305,9 @@ public class DpmMockContext extends MockContext {
public final BuildMock buildMock = new BuildMock();
/** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */
public final Map<UserHandle, Context> userContexts = new ArrayMap<>();
public String packageName = null;
public ApplicationInfo applicationInfo = null;
@@ -335,6 +343,7 @@ public class DpmMockContext extends MockContext {
telephonyManager = mock(TelephonyManager.class);
accountManager = mock(AccountManager.class);
alarmManager = mock(AlarmManager.class);
keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(context.getPackageManager());
@@ -689,6 +698,19 @@ public class DpmMockContext extends MockContext {
spiedContext.unregisterReceiver(receiver);
}
@Override
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
throws PackageManager.NameNotFoundException {
if (!userContexts.containsKey(user)) {
return super.createPackageContextAsUser(packageName, flags, user);
}
if (!getPackageName().equals(packageName)) {
throw new UnsupportedOperationException(
"Creating a context as another package is not implemented");
}
return userContexts.get(user);
}
@Override
public ContentResolver getContentResolver() {
return contentResolver;