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 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 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 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 */ 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 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 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 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 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 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 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 356 public String getBestPackageName() { 357 synchronized (mLock) { 358 return mPackageName; 359 } 360 } 361 362 public int getBestVersion() { 363 synchronized (mLock) { 364 return mVersion; 365 } 366 } 367 368 public IBinder getBinder() { 369 synchronized (mLock) { 370 return mBinder; 371 } 372 } 373 374 public void switchUser() { 375 synchronized (mLock) { 376 if (!mIsMultiuser) { 377 unbindLocked(); 378 bindBestPackageLocked(mServicePackageName); 379 } 380 } 381 } 382 } 383