Home | History | Annotate | Download | only in security
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.security;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.pm.PackageManager;
     25 import android.os.Process;
     26 import android.os.UserHandle;
     27 import android.security.IKeyChainService;
     28 import android.util.Slog;
     29 
     30 import com.android.server.DeviceIdleController;
     31 import com.android.server.LocalServices;
     32 import com.android.server.SystemService;
     33 
     34 /**
     35  * Service related to {@link android.security.KeyChain}.
     36  * <p>
     37  * Most of the implementation of KeyChain is provided by the com.android.keychain app. Until O,
     38  * this was OK because a system app has roughly the same privileges as the system process.
     39  * <p>
     40  * With the introduction of background check, PACKAGE_* broadcasts (_ADDED, _REMOVED, _REPLACED)
     41  * aren't received when the KeyChain app is in the background, which is bad as it uses those to
     42  * drive internal cleanup.
     43  * <p>
     44  * TODO (b/35968281): take a more sophisticated look at what bits of KeyChain should be inside the
     45  *                    system server and which make sense inside a system app.
     46  */
     47 public class KeyChainSystemService extends SystemService {
     48 
     49     private static final String TAG = "KeyChainSystemService";
     50 
     51     /**
     52      * Maximum time limit for the KeyChain app to deal with packages being removed.
     53      */
     54     private static final int KEYCHAIN_IDLE_WHITELIST_DURATION_MS = 30 * 1000;
     55 
     56     public KeyChainSystemService(final Context context) {
     57         super(context);
     58     }
     59 
     60     @Override
     61     public void onStart() {
     62         IntentFilter packageFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
     63         packageFilter.addDataScheme("package");
     64         try {
     65             getContext().registerReceiverAsUser(mPackageReceiver, UserHandle.ALL,
     66                     packageFilter, null /*broadcastPermission*/, null /*handler*/);
     67         } catch (RuntimeException e) {
     68             Slog.w(TAG, "Unable to register for package removed broadcast", e);
     69         }
     70     }
     71 
     72     private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
     73         @Override
     74         public void onReceive(final Context context, final Intent broadcastIntent) {
     75             if (broadcastIntent.getPackage() != null) {
     76                 return;
     77             }
     78 
     79             try {
     80                 final Intent intent = new Intent(IKeyChainService.class.getName());
     81                 ComponentName service =
     82                         intent.resolveSystemService(getContext().getPackageManager(), 0 /*flags*/);
     83                 if (service == null) {
     84                     return;
     85                 }
     86                 intent.setComponent(service);
     87                 intent.setAction(broadcastIntent.getAction());
     88                 startServiceInBackgroundAsUser(intent, UserHandle.of(getSendingUserId()));
     89             } catch (RuntimeException e) {
     90                 Slog.e(TAG, "Unable to forward package removed broadcast to KeyChain", e);
     91             }
     92         }
     93     };
     94 
     95 
     96     private void startServiceInBackgroundAsUser(final Intent intent, final UserHandle user) {
     97         if (intent.getComponent() == null) {
     98             return;
     99         }
    100 
    101         final String packageName = intent.getComponent().getPackageName();
    102         final DeviceIdleController.LocalService idleController =
    103                 LocalServices.getService(DeviceIdleController.LocalService.class);
    104         idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
    105                 KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, "keychain");
    106 
    107         getContext().startServiceAsUser(intent, user);
    108     }
    109 }
    110