Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2012 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;
     18 
     19 import android.annotation.Nullable;
     20 import android.content.BroadcastReceiver;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.content.ServiceConnection;
     26 import android.content.pm.PackageInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.PackageManager.NameNotFoundException;
     29 import android.content.pm.ResolveInfo;
     30 import android.content.pm.Signature;
     31 import android.content.res.Resources;
     32 import android.os.Handler;
     33 import android.os.IBinder;
     34 import android.os.UserHandle;
     35 import android.util.Log;
     36 import android.util.Slog;
     37 
     38 import com.android.internal.annotations.GuardedBy;
     39 import com.android.internal.content.PackageMonitor;
     40 
     41 import java.util.ArrayList;
     42 import java.util.Arrays;
     43 import java.util.HashSet;
     44 import java.util.List;
     45 import java.util.Objects;
     46 
     47 /**
     48  * Find the best Service, and bind to it.
     49  * Handles run-time package changes.
     50  */
     51 public class ServiceWatcher implements ServiceConnection {
     52     private static final boolean D = false;
     53     public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
     54     public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
     55 
     56     private final String mTag;
     57     private final Context mContext;
     58     private final PackageManager mPm;
     59     private final List<HashSet<Signature>> mSignatureSets;
     60     private final String mAction;
     61 
     62     /**
     63      * If mServicePackageName is not null, only this package will be searched for the service that
     64      * implements mAction. When null, all packages in the system that matches one of the signature
     65      * in mSignatureSets are searched.
     66      */
     67     private final String mServicePackageName;
     68     private final Runnable mNewServiceWork;
     69     private final Handler mHandler;
     70 
     71     private final Object mLock = new Object();
     72 
     73     @GuardedBy("mLock")
     74     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     75 
     76     @GuardedBy("mLock")
     77     private IBinder mBoundService;
     78     @GuardedBy("mLock")
     79     private ComponentName mBoundComponent;
     80     @GuardedBy("mLock")
     81     private String mBoundPackageName;
     82     @GuardedBy("mLock")
     83     private int mBoundVersion = Integer.MIN_VALUE;
     84     @GuardedBy("mLock")
     85     private int mBoundUserId = UserHandle.USER_NULL;
     86 
     87     public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
     88             List<String> initialPackageNames) {
     89         PackageManager pm = context.getPackageManager();
     90         ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
     91         for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
     92             String pkg = initialPackageNames.get(i);
     93             try {
     94                 HashSet<Signature> set = new HashSet<Signature>();
     95                 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.MATCH_SYSTEM_ONLY
     96                         | PackageManager.GET_SIGNATURES).signatures;
     97                 set.addAll(Arrays.asList(sigs));
     98                 sigSets.add(set);
     99             } catch (NameNotFoundException e) {
    100                 Log.w("ServiceWatcher", pkg + " not found");
    101             }
    102         }
    103         return sigSets;
    104     }
    105 
    106     public ServiceWatcher(Context context, String logTag, String action,
    107             int overlaySwitchResId, int defaultServicePackageNameResId,
    108             int initialPackageNamesResId, Runnable newServiceWork,
    109             Handler handler) {
    110         mContext = context;
    111         mTag = logTag;
    112         mAction = action;
    113         mPm = mContext.getPackageManager();
    114         mNewServiceWork = newServiceWork;
    115         mHandler = handler;
    116         Resources resources = context.getResources();
    117 
    118         // Whether to enable service overlay.
    119         boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
    120         ArrayList<String> initialPackageNames = new ArrayList<String>();
    121         if (enableOverlay) {
    122             // A list of package names used to create the signatures.
    123             String[] pkgs = resources.getStringArray(initialPackageNamesResId);
    124             if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
    125             mServicePackageName = null;
    126             if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
    127         } else {
    128             // The default package name that is searched for service implementation when overlay is
    129             // disabled.
    130             String servicePackageName = resources.getString(defaultServicePackageNameResId);
    131             if (servicePackageName != null) initialPackageNames.add(servicePackageName);
    132             mServicePackageName = servicePackageName;
    133             if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
    134         }
    135         mSignatureSets = getSignatureSets(context, initialPackageNames);
    136     }
    137 
    138     /**
    139      * Start this watcher, including binding to the current best match and
    140      * re-binding to any better matches down the road.
    141      * <p>
    142      * Note that if there are no matching encryption-aware services, we may not
    143      * bind to a real service until after the current user is unlocked.
    144      *
    145      * @returns {@code true} if a potential service implementation was found.
    146      */
    147     public boolean start() {
    148         if (isServiceMissing()) return false;
    149 
    150         synchronized (mLock) {
    151             bindBestPackageLocked(mServicePackageName, false);
    152         }
    153 
    154         // listen for user change
    155         IntentFilter intentFilter = new IntentFilter();
    156         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
    157         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
    158         mContext.registerReceiverAsUser(new BroadcastReceiver() {
    159             @Override
    160             public void onReceive(Context context, Intent intent) {
    161                 final String action = intent.getAction();
    162                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
    163                         UserHandle.USER_NULL);
    164                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
    165                     switchUser(userId);
    166                 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
    167                     unlockUser(userId);
    168                 }
    169             }
    170         }, UserHandle.ALL, intentFilter, null, mHandler);
    171 
    172         // listen for relevant package changes if service overlay is enabled.
    173         if (mServicePackageName == null) {
    174             mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
    175         }
    176 
    177         return true;
    178     }
    179 
    180     /**
    181      * Check if any instance of this service is present on the device,
    182      * regardless of it being encryption-aware or not.
    183      */
    184     private boolean isServiceMissing() {
    185         final Intent intent = new Intent(mAction);
    186         final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
    187                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
    188         return mPm.queryIntentServicesAsUser(intent, flags, mCurrentUserId).isEmpty();
    189     }
    190 
    191     /**
    192      * Searches and binds to the best package, or do nothing if the best package
    193      * is already bound, unless force rebinding is requested.
    194      *
    195      * @param justCheckThisPackage Only consider this package, or consider all
    196      *            packages if it is {@code null}.
    197      * @param forceRebind Force a rebinding to the best package if it's already
    198      *            bound.
    199      * @returns {@code true} if a valid package was found to bind to.
    200      */
    201     private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
    202         Intent intent = new Intent(mAction);
    203         if (justCheckThisPackage != null) {
    204             intent.setPackage(justCheckThisPackage);
    205         }
    206         final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
    207                 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
    208                 mCurrentUserId);
    209         int bestVersion = Integer.MIN_VALUE;
    210         ComponentName bestComponent = null;
    211         boolean bestIsMultiuser = false;
    212         if (rInfos != null) {
    213             for (ResolveInfo rInfo : rInfos) {
    214                 final ComponentName component = rInfo.serviceInfo.getComponentName();
    215                 final String packageName = component.getPackageName();
    216 
    217                 // check signature
    218                 try {
    219                     PackageInfo pInfo;
    220                     pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES
    221                             | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
    222                     if (!isSignatureMatch(pInfo.signatures)) {
    223                         Log.w(mTag, packageName + " resolves service " + mAction
    224                                 + ", but has wrong signature, ignoring");
    225                         continue;
    226                     }
    227                 } catch (NameNotFoundException e) {
    228                     Log.wtf(mTag, e);
    229                     continue;
    230                 }
    231 
    232                 // check metadata
    233                 int version = Integer.MIN_VALUE;
    234                 boolean isMultiuser = false;
    235                 if (rInfo.serviceInfo.metaData != null) {
    236                     version = rInfo.serviceInfo.metaData.getInt(
    237                             EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
    238                     isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
    239                 }
    240 
    241                 if (version > bestVersion) {
    242                     bestVersion = version;
    243                     bestComponent = component;
    244                     bestIsMultiuser = isMultiuser;
    245                 }
    246             }
    247 
    248             if (D) {
    249                 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
    250                         (justCheckThisPackage == null ? ""
    251                                 : "(" + justCheckThisPackage + ") "), rInfos.size(),
    252                         (bestComponent == null ? "no new best component"
    253                                 : "new best component: " + bestComponent)));
    254             }
    255         } else {
    256             if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
    257         }
    258 
    259         if (bestComponent == null) {
    260             Slog.w(mTag, "Odd, no component found for service " + mAction);
    261             unbindLocked();
    262             return false;
    263         }
    264 
    265         final int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
    266         final boolean alreadyBound = Objects.equals(bestComponent, mBoundComponent)
    267                 && bestVersion == mBoundVersion && userId == mBoundUserId;
    268         if (forceRebind || !alreadyBound) {
    269             unbindLocked();
    270             bindToPackageLocked(bestComponent, bestVersion, userId);
    271         }
    272         return true;
    273     }
    274 
    275     private void unbindLocked() {
    276         ComponentName component;
    277         component = mBoundComponent;
    278         mBoundComponent = null;
    279         mBoundPackageName = null;
    280         mBoundVersion = Integer.MIN_VALUE;
    281         mBoundUserId = UserHandle.USER_NULL;
    282         if (component != null) {
    283             if (D) Log.d(mTag, "unbinding " + component);
    284             mContext.unbindService(this);
    285         }
    286     }
    287 
    288     private void bindToPackageLocked(ComponentName component, int version, int userId) {
    289         Intent intent = new Intent(mAction);
    290         intent.setComponent(component);
    291         mBoundComponent = component;
    292         mBoundPackageName = component.getPackageName();
    293         mBoundVersion = version;
    294         mBoundUserId = userId;
    295         if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
    296         mContext.bindServiceAsUser(intent, this,
    297                 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
    298                 new UserHandle(userId));
    299     }
    300 
    301     public static boolean isSignatureMatch(Signature[] signatures,
    302             List<HashSet<Signature>> sigSets) {
    303         if (signatures == null) return false;
    304 
    305         // build hashset of input to test against
    306         HashSet<Signature> inputSet = new HashSet<Signature>();
    307         for (Signature s : signatures) {
    308             inputSet.add(s);
    309         }
    310 
    311         // test input against each of the signature sets
    312         for (HashSet<Signature> referenceSet : sigSets) {
    313             if (referenceSet.equals(inputSet)) {
    314                 return true;
    315             }
    316         }
    317         return false;
    318     }
    319 
    320     private boolean isSignatureMatch(Signature[] signatures) {
    321         return isSignatureMatch(signatures, mSignatureSets);
    322     }
    323 
    324     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
    325         /**
    326          * Called when package has been reinstalled
    327          */
    328         @Override
    329         public void onPackageUpdateFinished(String packageName, int uid) {
    330             synchronized (mLock) {
    331                 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
    332                 bindBestPackageLocked(null, forceRebind);
    333             }
    334         }
    335 
    336         @Override
    337         public void onPackageAdded(String packageName, int uid) {
    338             synchronized (mLock) {
    339                 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
    340                 bindBestPackageLocked(null, forceRebind);
    341             }
    342         }
    343 
    344         @Override
    345         public void onPackageRemoved(String packageName, int uid) {
    346             synchronized (mLock) {
    347                 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
    348                 bindBestPackageLocked(null, forceRebind);
    349             }
    350         }
    351 
    352         @Override
    353         public boolean onPackageChanged(String packageName, int uid, String[] components) {
    354             synchronized (mLock) {
    355                 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
    356                 bindBestPackageLocked(null, forceRebind);
    357             }
    358             return super.onPackageChanged(packageName, uid, components);
    359         }
    360     };
    361 
    362     @Override
    363     public void onServiceConnected(ComponentName component, IBinder binder) {
    364         synchronized (mLock) {
    365             if (component.equals(mBoundComponent)) {
    366                 if (D) Log.d(mTag, component + " connected");
    367                 mBoundService = binder;
    368                 if (mHandler !=null && mNewServiceWork != null) {
    369                     mHandler.post(mNewServiceWork);
    370                 }
    371             } else {
    372                 Log.w(mTag, "unexpected onServiceConnected: " + component);
    373             }
    374         }
    375     }
    376 
    377     @Override
    378     public void onServiceDisconnected(ComponentName component) {
    379         synchronized (mLock) {
    380             if (D) Log.d(mTag, component + " disconnected");
    381 
    382             if (component.equals(mBoundComponent)) {
    383                 mBoundService = null;
    384             }
    385         }
    386     }
    387 
    388     public @Nullable String getBestPackageName() {
    389         synchronized (mLock) {
    390             return mBoundPackageName;
    391         }
    392     }
    393 
    394     public int getBestVersion() {
    395         synchronized (mLock) {
    396             return mBoundVersion;
    397         }
    398     }
    399 
    400     public @Nullable IBinder getBinder() {
    401         synchronized (mLock) {
    402             return mBoundService;
    403         }
    404     }
    405 
    406     public void switchUser(int userId) {
    407         synchronized (mLock) {
    408             mCurrentUserId = userId;
    409             bindBestPackageLocked(mServicePackageName, false);
    410         }
    411     }
    412 
    413     public void unlockUser(int userId) {
    414         synchronized (mLock) {
    415             if (userId == mCurrentUserId) {
    416                 bindBestPackageLocked(mServicePackageName, false);
    417             }
    418         }
    419     }
    420 }
    421