Forward package removed broadcast to KeyChainService
Test: Cts Verifier > Security > KeyChain Test
Test: Manually uninstall and reinstalling Chrome
Test: Manually disable KeyChain and then reinstall Chrome
Bug: 35968281
Change-Id: I1692f7042f081d1a743e0e64ea24fe1dbe63192d
diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java
new file mode 100644
index 0000000..2f681a3
--- /dev/null
+++ b/services/core/java/com/android/server/security/KeyChainSystemService.java
@@ -0,0 +1,109 @@
+/*
+ * 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.security;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.security.IKeyChainService;
+import android.util.Slog;
+
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service related to {@link android.security.KeyChain}.
+ * <p>
+ * Most of the implementation of KeyChain is provided by the com.android.keychain app. Until O,
+ * this was OK because a system app has roughly the same privileges as the system process.
+ * <p>
+ * With the introduction of background check, PACKAGE_* broadcasts (_ADDED, _REMOVED, _REPLACED)
+ * aren't received when the KeyChain app is in the background, which is bad as it uses those to
+ * drive internal cleanup.
+ * <p>
+ * TODO (b/35968281): take a more sophisticated look at what bits of KeyChain should be inside the
+ * system server and which make sense inside a system app.
+ */
+public class KeyChainSystemService extends SystemService {
+
+ private static final String TAG = "KeyChainSystemService";
+
+ /**
+ * Maximum time limit for the KeyChain app to deal with packages being removed.
+ */
+ private static final int KEYCHAIN_IDLE_WHITELIST_DURATION_MS = 30 * 1000;
+
+ public KeyChainSystemService(final Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ IntentFilter packageFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ try {
+ getContext().registerReceiverAsUser(mPackageReceiver, UserHandle.ALL,
+ packageFilter, null /*broadcastPermission*/, null /*handler*/);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Unable to register for package removed broadcast", e);
+ }
+ }
+
+ private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent broadcastIntent) {
+ if (broadcastIntent.getPackage() != null) {
+ return;
+ }
+
+ try {
+ final Intent intent = new Intent(IKeyChainService.class.getName());
+ ComponentName service =
+ intent.resolveSystemService(getContext().getPackageManager(), 0 /*flags*/);
+ if (service == null) {
+ return;
+ }
+ intent.setComponent(service);
+ intent.setAction(broadcastIntent.getAction());
+ startServiceInBackgroundAsUser(intent, UserHandle.of(getSendingUserId()));
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Unable to forward package removed broadcast to KeyChain", e);
+ }
+ }
+ };
+
+
+ private void startServiceInBackgroundAsUser(final Intent intent, final UserHandle user) {
+ if (intent.getComponent() == null) {
+ return;
+ }
+
+ final String packageName = intent.getComponent().getPackageName();
+ final DeviceIdleController.LocalService idleController =
+ LocalServices.getService(DeviceIdleController.LocalService.class);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
+ KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, "keychain");
+
+ getContext().startServiceAsUser(intent, user);
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0ed56ab..5c58234 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -99,6 +99,7 @@
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.retaildemo.RetailDemoModeService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
+import com.android.server.security.KeyChainSystemService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
@@ -727,6 +728,10 @@
new KeyAttestationApplicationIdProviderService(context));
traceEnd();
+ traceBeginAndSlog("StartKeyChainSystemService");
+ mSystemServiceManager.startService(KeyChainSystemService.class);
+ traceEnd();
+
traceBeginAndSlog("StartSchedulingPolicyService");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
traceEnd();