Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a10b057b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "MonitoringCertTask no longer relies on software.device_admin"

parents dacc1377 7f5c91c6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
+72 −132
Original line number Diff line number Diff line
@@ -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();
@@ -2952,125 +2983,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());
                }
        if (!mHasFeature) {
            return installedCertificates.size();
        } else {
                manageNotification(UserHandle.of(userHandle));
            }
            return null;
        }

        private void manageNotification(UserHandle userHandle) {
            if (!mUserManager.isUserUnlocked(userHandle)) {
                return;
            }

            // 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)) {
            if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) {
                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();
        }
    }

@@ -4635,7 +4575,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            }
            saveSettingsLocked(userId);
        }
        new MonitoringCertNotificationTask().execute(userId);
        new MonitoringCertNotificationTask(this, mInjector).execute(userId);
        return true;
    }

@@ -4659,7 +4599,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                    saveSettingsLocked(userInfo.id);
                }

                new MonitoringCertNotificationTask().execute(userInfo.id);
                new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id);
            }
        }
    }
@@ -7154,7 +7094,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);
+166 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -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;
        }
    }
}
+72 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading