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.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.ServiceConnection; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.ResolveInfo; 29 import android.content.pm.Signature; 30 import android.content.res.Resources; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.UserHandle; 34 import android.util.Log; 35 36 import com.android.internal.content.PackageMonitor; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.HashSet; 42 import java.util.List; 43 44 /** 45 * Find the best Service, and bind to it. 46 * Handles run-time package changes. 47 */ 48 public class ServiceWatcher implements ServiceConnection { 49 private static final boolean D = false; 50 public static final String EXTRA_SERVICE_VERSION = "serviceVersion"; 51 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser"; 52 53 private final String mTag; 54 private final Context mContext; 55 private final PackageManager mPm; 56 private final List<HashSet<Signature>> mSignatureSets; 57 private final String mAction; 58 59 /** 60 * If mServicePackageName is not null, only this package will be searched for the service that 61 * implements mAction. When null, all packages in the system that matches one of the signature 62 * in mSignatureSets are searched. 63 */ 64 private final String mServicePackageName; 65 private final Runnable mNewServiceWork; 66 private final Handler mHandler; 67 68 private Object mLock = new Object(); 69 70 // all fields below synchronized on mLock 71 private IBinder mBinder; // connected service 72 private String mPackageName; // current best package 73 private int mVersion = Integer.MIN_VALUE; // current best version 74 /** 75 * Whether the currently-connected service is multiuser-aware. This can change at run-time 76 * when switching from one version of a service to another. 77 */ 78 private boolean mIsMultiuser = false; 79 getSignatureSets(Context context, List<String> initialPackageNames)80 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, 81 List<String> initialPackageNames) { 82 PackageManager pm = context.getPackageManager(); 83 ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>(); 84 for (int i = 0, size = initialPackageNames.size(); i < size; i++) { 85 String pkg = initialPackageNames.get(i); 86 try { 87 HashSet<Signature> set = new HashSet<Signature>(); 88 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; 89 set.addAll(Arrays.asList(sigs)); 90 sigSets.add(set); 91 } catch (NameNotFoundException e) { 92 Log.w("ServiceWatcher", pkg + " not found"); 93 } 94 } 95 return sigSets; 96 } 97 ServiceWatcher(Context context, String logTag, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Runnable newServiceWork, Handler handler)98 public ServiceWatcher(Context context, String logTag, String action, 99 int overlaySwitchResId, int defaultServicePackageNameResId, 100 int initialPackageNamesResId, Runnable newServiceWork, 101 Handler handler) { 102 mContext = context; 103 mTag = logTag; 104 mAction = action; 105 mPm = mContext.getPackageManager(); 106 mNewServiceWork = newServiceWork; 107 mHandler = handler; 108 Resources resources = context.getResources(); 109 110 // Whether to enable service overlay. 111 boolean enableOverlay = resources.getBoolean(overlaySwitchResId); 112 ArrayList<String> initialPackageNames = new ArrayList<String>(); 113 if (enableOverlay) { 114 // A list of package names used to create the signatures. 115 String[] pkgs = resources.getStringArray(initialPackageNamesResId); 116 if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs)); 117 mServicePackageName = null; 118 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs)); 119 } else { 120 // The default package name that is searched for service implementation when overlay is 121 // disabled. 122 String servicePackageName = resources.getString(defaultServicePackageNameResId); 123 if (servicePackageName != null) initialPackageNames.add(servicePackageName); 124 mServicePackageName = servicePackageName; 125 if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName); 126 } 127 mSignatureSets = getSignatureSets(context, initialPackageNames); 128 } 129 start()130 public boolean start() { 131 synchronized (mLock) { 132 if (!bindBestPackageLocked(mServicePackageName)) return false; 133 } 134 135 // listen for user change 136 IntentFilter intentFilter = new IntentFilter(); 137 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 138 mContext.registerReceiverAsUser(new BroadcastReceiver() { 139 @Override 140 public void onReceive(Context context, Intent intent) { 141 String action = intent.getAction(); 142 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 143 switchUser(); 144 } 145 } 146 }, UserHandle.ALL, intentFilter, null, mHandler); 147 148 // listen for relevant package changes if service overlay is enabled. 149 if (mServicePackageName == null) { 150 mPackageMonitor.register(mContext, null, UserHandle.ALL, true); 151 } 152 153 return true; 154 } 155 156 /** 157 * Searches and binds to the best package, or do nothing 158 * if the best package is already bound. 159 * Only checks the named package, or checks all packages if it 160 * is null. 161 * Return true if a new package was found to bind to. 162 */ bindBestPackageLocked(String justCheckThisPackage)163 private boolean bindBestPackageLocked(String justCheckThisPackage) { 164 Intent intent = new Intent(mAction); 165 if (justCheckThisPackage != null) { 166 intent.setPackage(justCheckThisPackage); 167 } 168 List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent, 169 PackageManager.GET_META_DATA, UserHandle.USER_OWNER); 170 int bestVersion = Integer.MIN_VALUE; 171 String bestPackage = null; 172 boolean bestIsMultiuser = false; 173 if (rInfos != null) { 174 for (ResolveInfo rInfo : rInfos) { 175 String packageName = rInfo.serviceInfo.packageName; 176 177 // check signature 178 try { 179 PackageInfo pInfo; 180 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 181 if (!isSignatureMatch(pInfo.signatures)) { 182 Log.w(mTag, packageName + " resolves service " + mAction 183 + ", but has wrong signature, ignoring"); 184 continue; 185 } 186 } catch (NameNotFoundException e) { 187 Log.wtf(mTag, e); 188 continue; 189 } 190 191 // check metadata 192 int version = Integer.MIN_VALUE; 193 boolean isMultiuser = false; 194 if (rInfo.serviceInfo.metaData != null) { 195 version = rInfo.serviceInfo.metaData.getInt( 196 EXTRA_SERVICE_VERSION, Integer.MIN_VALUE); 197 isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER); 198 } 199 200 if (version > mVersion) { 201 bestVersion = version; 202 bestPackage = packageName; 203 bestIsMultiuser = isMultiuser; 204 } 205 } 206 207 if (D) { 208 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, 209 (justCheckThisPackage == null ? "" 210 : "(" + justCheckThisPackage + ") "), rInfos.size(), 211 (bestPackage == null ? "no new best package" 212 : "new best package: " + bestPackage))); 213 } 214 } else { 215 if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction); 216 } 217 if (bestPackage != null) { 218 bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser); 219 return true; 220 } 221 return false; 222 } 223 unbindLocked()224 private void unbindLocked() { 225 String pkg; 226 pkg = mPackageName; 227 mPackageName = null; 228 mVersion = Integer.MIN_VALUE; 229 mIsMultiuser = false; 230 if (pkg != null) { 231 if (D) Log.d(mTag, "unbinding " + pkg); 232 mContext.unbindService(this); 233 } 234 } 235 bindToPackageLocked(String packageName, int version, boolean isMultiuser)236 private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) { 237 unbindLocked(); 238 Intent intent = new Intent(mAction); 239 intent.setPackage(packageName); 240 mPackageName = packageName; 241 mVersion = version; 242 mIsMultiuser = isMultiuser; 243 if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") (" 244 + (isMultiuser ? "multi" : "single") + "-user)"); 245 mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND 246 | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT); 247 } 248 isSignatureMatch(Signature[] signatures, List<HashSet<Signature>> sigSets)249 public static boolean isSignatureMatch(Signature[] signatures, 250 List<HashSet<Signature>> sigSets) { 251 if (signatures == null) return false; 252 253 // build hashset of input to test against 254 HashSet<Signature> inputSet = new HashSet<Signature>(); 255 for (Signature s : signatures) { 256 inputSet.add(s); 257 } 258 259 // test input against each of the signature sets 260 for (HashSet<Signature> referenceSet : sigSets) { 261 if (referenceSet.equals(inputSet)) { 262 return true; 263 } 264 } 265 return false; 266 } 267 isSignatureMatch(Signature[] signatures)268 private boolean isSignatureMatch(Signature[] signatures) { 269 return isSignatureMatch(signatures, mSignatureSets); 270 } 271 272 private final PackageMonitor mPackageMonitor = new PackageMonitor() { 273 /** 274 * Called when package has been reinstalled 275 */ 276 @Override 277 public void onPackageUpdateFinished(String packageName, int uid) { 278 synchronized (mLock) { 279 if (packageName.equals(mPackageName)) { 280 // package updated, make sure to rebind 281 unbindLocked(); 282 } 283 // Need to check all packages because this method is also called when a 284 // system app is uninstalled and the stock version in reinstalled. 285 bindBestPackageLocked(null); 286 } 287 } 288 289 @Override 290 public void onPackageAdded(String packageName, int uid) { 291 synchronized (mLock) { 292 if (packageName.equals(mPackageName)) { 293 // package updated, make sure to rebind 294 unbindLocked(); 295 } 296 // check the new package is case it is better 297 bindBestPackageLocked(null); 298 } 299 } 300 301 @Override 302 public void onPackageRemoved(String packageName, int uid) { 303 synchronized (mLock) { 304 if (packageName.equals(mPackageName)) { 305 unbindLocked(); 306 // the currently bound package was removed, 307 // need to search for a new package 308 bindBestPackageLocked(null); 309 } 310 } 311 } 312 313 @Override 314 public boolean onPackageChanged(String packageName, int uid, String[] components) { 315 synchronized (mLock) { 316 if (packageName.equals(mPackageName)) { 317 // service enabled or disabled, make sure to rebind 318 unbindLocked(); 319 } 320 // the service might be disabled, need to search for a new 321 // package 322 bindBestPackageLocked(null); 323 } 324 return super.onPackageChanged(packageName, uid, components); 325 } 326 }; 327 328 @Override onServiceConnected(ComponentName name, IBinder binder)329 public void onServiceConnected(ComponentName name, IBinder binder) { 330 synchronized (mLock) { 331 String packageName = name.getPackageName(); 332 if (packageName.equals(mPackageName)) { 333 if (D) Log.d(mTag, packageName + " connected"); 334 mBinder = binder; 335 if (mHandler !=null && mNewServiceWork != null) { 336 mHandler.post(mNewServiceWork); 337 } 338 } else { 339 Log.w(mTag, "unexpected onServiceConnected: " + packageName); 340 } 341 } 342 } 343 344 @Override onServiceDisconnected(ComponentName name)345 public void onServiceDisconnected(ComponentName name) { 346 synchronized (mLock) { 347 String packageName = name.getPackageName(); 348 if (D) Log.d(mTag, packageName + " disconnected"); 349 350 if (packageName.equals(mPackageName)) { 351 mBinder = null; 352 } 353 } 354 } 355 getBestPackageName()356 public String getBestPackageName() { 357 synchronized (mLock) { 358 return mPackageName; 359 } 360 } 361 getBestVersion()362 public int getBestVersion() { 363 synchronized (mLock) { 364 return mVersion; 365 } 366 } 367 getBinder()368 public IBinder getBinder() { 369 synchronized (mLock) { 370 return mBinder; 371 } 372 } 373 switchUser()374 public void switchUser() { 375 synchronized (mLock) { 376 if (!mIsMultiuser) { 377 unbindLocked(); 378 bindBestPackageLocked(mServicePackageName); 379 } 380 } 381 } 382 } 383