/* * 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}. *
* 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. *
* 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. *
* 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); } }