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