Merge "Exfiltrate cert code from DevicePolicyManager"
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
new file mode 100644
index 0000000..a2bc195
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -0,0 +1,232 @@
+/*
+ * 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
+import android.util.Log;
+
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.R;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Set;
+
+public class CertificateMonitor {
+ 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;
+ private final Handler mHandler;
+
+ public CertificateMonitor(final DevicePolicyManagerService service,
+ final DevicePolicyManagerService.Injector injector, final Handler handler) {
+ mService = service;
+ mInjector = injector;
+ mHandler = handler;
+
+ // Broadcast filter for changes to the trusted certificate store. Listens on the background
+ // handler to avoid blocking time-critical tasks on the main handler thread.
+ 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);
+ mInjector.mContext.registerReceiverAsUser(
+ mRootCaReceiver, UserHandle.ALL, filter, null, mHandler);
+ }
+
+ public String installCaCert(final UserHandle userHandle, byte[] certBuffer) {
+ // Convert certificate data from X509 format to PEM.
+ byte[] pemCert;
+ try {
+ X509Certificate cert = parseCert(certBuffer);
+ pemCert = Credentials.convertToPem(cert);
+ } catch (CertificateException | IOException ce) {
+ Log.e(LOG_TAG, "Problem converting cert", ce);
+ return null;
+ }
+
+ try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
+ return keyChainConnection.getService().installCaCertificate(pemCert);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+ } catch (InterruptedException e1) {
+ Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ }
+
+ public void uninstallCaCerts(final UserHandle userHandle, final String[] aliases) {
+ try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
+ for (int i = 0 ; i < aliases.length; i++) {
+ keyChainConnection.getService().deleteCaCertificate(aliases[i]);
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
+ } catch (InterruptedException ie) {
+ Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public List<String> getInstalledCaCertificates(UserHandle userHandle)
+ throws RemoteException, RuntimeException {
+ try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) {
+ return conn.getService().getUserCaAliases().getList();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return null;
+ } catch (AssertionError e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void onCertificateApprovalsChanged(int userId) {
+ mHandler.post(() -> updateInstalledCertificates(UserHandle.of(userId)));
+ }
+
+ /**
+ * Broadcast receiver for changes to the trusted certificate store.
+ */
+ private final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (StorageManager.inCryptKeeperBounce()) {
+ return;
+ }
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+ updateInstalledCertificates(UserHandle.of(userId));
+ }
+ };
+
+ private void updateInstalledCertificates(final UserHandle userHandle) {
+ if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+ return;
+ }
+
+ final List<String> installedCerts;
+ try {
+ installedCerts = getInstalledCaCertificates(userHandle);
+ } catch (RemoteException | RuntimeException e) {
+ Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+ return;
+ }
+ mService.onInstalledCertificatesChanged(userHandle, installedCerts);
+
+ final int pendingCertificateCount =
+ installedCerts.size() - mService.getAcceptedCaCertificates(userHandle).size();
+ if (pendingCertificateCount != 0) {
+ final Notification noti = buildNotification(userHandle, pendingCertificateCount);
+ mInjector.getNotificationManager().notifyAsUser(
+ LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
+ } else {
+ mInjector.getNotificationManager().cancelAsUser(
+ LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle);
+ }
+ }
+
+ private Notification buildNotification(UserHandle userHandle, int pendingCertificateCount) {
+ final Context userContext;
+ try {
+ userContext = mInjector.createContextAsUser(userHandle);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+ return null;
+ }
+
+ final Resources resources = mInjector.getResources();
+ final int smallIconId;
+ final String contentText;
+
+ int parentUserId = userHandle.getIdentifier();
+
+ 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()) {
+ final String ownerName = mService.getDeviceOwnerName();
+ 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;
+ }
+
+ // Create an intent to launch an activity showing information about the certificate.
+ Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
+ dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount);
+ dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
+
+ // The intent should only be allowed to resolve to a system app.
+ ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
+ mInjector.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
+ if (targetInfo != null) {
+ dialogIntent.setComponent(targetInfo.getComponentName());
+ }
+
+ PendingIntent notifyIntent = mInjector.pendingIntentGetActivityAsUser(userContext, 0,
+ dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
+ UserHandle.of(parentUserId));
+
+ return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY)
+ .setSmallIcon(smallIconId)
+ .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
+ pendingCertificateCount))
+ .setContentText(contentText)
+ .setContentIntent(notifyIntent)
+ .setShowWhen(false)
+ .setColor(R.color.system_notification_accent_color)
+ .build();
+ }
+
+ private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(
+ certBuffer));
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 536d454..ecbd312 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -114,7 +114,6 @@
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -184,7 +183,6 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -193,9 +191,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
@@ -398,6 +393,7 @@
*/
boolean mIsWatch;
+ private final CertificateMonitor mCertificateMonitor;
private final SecurityLogMonitor mSecurityLogMonitor;
private NetworkLogger mNetworkLogger;
@@ -530,19 +526,6 @@
final Handler mHandler;
final Handler mBackgroundHandler;
- /** 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
@@ -630,25 +613,6 @@
handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
} else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) {
clearWipeProfileNotification();
- } else if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
- mBackgroundHandler.post(() -> {
- try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
- UserHandle.of(userHandle))) {
- final List<String> caCerts =
- keyChainConnection.getService().getUserCaAliases().getList();
- synchronized (DevicePolicyManagerService.this) {
- if (getUserData(userHandle).mOwnerInstalledCaCerts
- .retainAll(caCerts)) {
- saveSettingsLocked(userHandle);
- }
- }
- } catch (InterruptedException e) {
- Slog.w(LOG_TAG, "error talking to IKeyChainService", e);
- Thread.currentThread().interrupt();
- } catch (RemoteException e) {
- Slog.w(LOG_TAG, "error talking to IKeyChainService", e);
- }
- });
}
}
@@ -1527,7 +1491,7 @@
@VisibleForTesting
static class Injector {
- private final Context mContext;
+ public final Context mContext;
Injector(Context context) {
mContext = context;
@@ -1720,6 +1684,12 @@
return "/data/system/";
}
+ PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
+ @NonNull Intent intent, int flags, Bundle options, UserHandle user) {
+ return PendingIntent.getActivityAsUser(
+ context, requestCode, intent, flags, options, user);
+ }
+
void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, int userHandle) {
mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents,
@@ -1810,6 +1780,7 @@
mLocalService = new LocalService();
mLockPatternUtils = injector.newLockPatternUtils();
+ // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false?
mSecurityLogMonitor = new SecurityLogMonitor(this);
mHasFeature = mInjector.getPackageManager()
@@ -1818,27 +1789,20 @@
.hasSystemFeature(PackageManager.FEATURE_WATCH);
mBackgroundHandler = BackgroundThread.getHandler();
- // 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);
+ // Needed when mHasFeature == false, because it controls the certificate warning text.
+ mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
if (!mHasFeature) {
// Skip the rest of the initialization
return;
}
- filter = new IntentFilter();
+ 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(KeyChain.ACTION_TRUST_STORE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
@@ -3083,33 +3047,43 @@
}
/**
- * Remove deleted CA certificates from the "approved" list for a particular user, counting
- * the number still remaining to approve.
+ * Clean up internal state when the set of installed trusted CA certificates changes.
*
* @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,
+ protected void onInstalledCertificatesChanged(final UserHandle userHandle,
final @NonNull Collection<String> installedCertificates) {
+ if (!mHasFeature) {
+ return;
+ }
enforceManageUsers();
- if (!mHasFeature) {
- return installedCertificates.size();
- } else {
+ synchronized (this) {
final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
- // Remove deleted certificates. Flush xml if necessary.
- if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) {
+ boolean changed = false;
+ changed |= policy.mAcceptedCaCertificates.retainAll(installedCertificates);
+ changed |= policy.mOwnerInstalledCaCerts.retainAll(installedCertificates);
+ if (changed) {
saveSettingsLocked(userHandle.getIdentifier());
}
+ }
+ }
- // Trim approved certificates from the count.
- return installedCertificates.size() - policy.mAcceptedCaCertificates.size();
+ /**
+ * Internal method used by {@link CertificateMonitor}.
+ */
+ protected Set<String> getAcceptedCaCertificates(final UserHandle userHandle) {
+ if (!mHasFeature) {
+ return Collections.<String> emptySet();
+ }
+ synchronized (this) {
+ final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
+ return policy.mAcceptedCaCertificates;
}
}
@@ -4690,7 +4664,7 @@
}
saveSettingsLocked(userId);
}
- new MonitoringCertNotificationTask(this, mInjector).execute(userId);
+ mCertificateMonitor.onCertificateApprovalsChanged(userId);
return true;
}
@@ -4713,8 +4687,7 @@
getUserData(userInfo.id).mAcceptedCaCertificates.clear();
saveSettingsLocked(userInfo.id);
}
-
- new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id);
+ mCertificateMonitor.onCertificateApprovalsChanged(userId);
}
}
}
@@ -4722,79 +4695,47 @@
@Override
public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer)
throws RemoteException {
- enforceCanManageCaCerts(admin, callerPackage);
-
- byte[] pemCert;
- try {
- X509Certificate cert = parseCert(certBuffer);
- pemCert = Credentials.convertToPem(cert);
- } catch (CertificateException ce) {
- Log.e(LOG_TAG, "Problem converting cert", ce);
- return false;
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Problem reading cert", ioe);
+ if (!mHasFeature) {
return false;
}
+ enforceCanManageCaCerts(admin, callerPackage);
- final UserHandle userHandle = UserHandle.of(mInjector.userHandleGetCallingUserId());
+ final String alias;
+
+ final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final long id = mInjector.binderClearCallingIdentity();
- String alias = null;
try {
- try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
- userHandle)) {
- alias = keyChainConnection.getService().installCaCertificate(pemCert);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+ alias = mCertificateMonitor.installCaCert(userHandle, certBuffer);
+ if (alias == null) {
+ Log.w(LOG_TAG, "Problem installing cert");
+ return false;
}
- } catch (InterruptedException e1) {
- Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
- Thread.currentThread().interrupt();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
- if (alias == null) {
- Log.w(LOG_TAG, "Problem installing cert");
- } else {
- synchronized (this) {
- final int userId = userHandle.getIdentifier();
- getUserData(userId).mOwnerInstalledCaCerts.add(alias);
- saveSettingsLocked(userId);
- }
- return true;
- }
- return false;
- }
- private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException {
- CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(
- certBuffer));
+ synchronized (this) {
+ getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias);
+ saveSettingsLocked(userHandle.getIdentifier());
+ }
+ return true;
}
@Override
public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) {
+ if (!mHasFeature) {
+ return;
+ }
enforceCanManageCaCerts(admin, callerPackage);
final int userId = mInjector.userHandleGetCallingUserId();
- final UserHandle userHandle = UserHandle.of(userId);
final long id = mInjector.binderClearCallingIdentity();
try {
- try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
- userHandle)) {
- for (int i = 0 ; i < aliases.length; i++) {
- keyChainConnection.getService().deleteCaCertificate(aliases[i]);
- }
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
- return;
- }
- } catch (InterruptedException ie) {
- Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
- Thread.currentThread().interrupt();
- return;
+ mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
+
synchronized (this) {
if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) {
saveSettingsLocked(userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java b/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java
deleted file mode 100644
index 1933fe7..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.notification.SystemNotificationChannels;
-import com.android.internal.R;
-
-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, SystemNotificationChannels.SECURITY)
- .setSmallIcon(smallIconId)
- .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
- pendingCertificateCount))
- .setContentText(contentText)
- .setContentIntent(notifyIntent)
- .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)) {
- return conn.getService().getUserCaAliases().getList();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return null;
- } catch (AssertionError e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index ca9285b..a6ce1d5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -17,13 +17,18 @@
import android.app.IActivityManager;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.media.IAudioService;
import android.net.IIpConnectivityMetrics;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Looper;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
@@ -302,6 +307,12 @@
}
@Override
+ PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
+ Intent intent, int flags, Bundle options, UserHandle user) {
+ return null;
+ }
+
+ @Override
void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, int userHandle) {
mContentObservers.put(new Pair<Uri, Integer>(uri, userHandle), observer);