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 17package com.android.server; 18 19import android.annotation.Nullable; 20import android.content.BroadcastReceiver; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.ServiceConnection; 26import android.content.pm.PackageInfo; 27import android.content.pm.PackageManager; 28import android.content.pm.PackageManager.NameNotFoundException; 29import android.content.pm.ResolveInfo; 30import android.content.pm.Signature; 31import android.content.res.Resources; 32import android.os.Handler; 33import android.os.IBinder; 34import android.os.UserHandle; 35import android.util.Log; 36import android.util.Slog; 37 38import com.android.internal.annotations.GuardedBy; 39import com.android.internal.content.PackageMonitor; 40 41import java.util.ArrayList; 42import java.util.Arrays; 43import java.util.HashSet; 44import java.util.List; 45import java.util.Objects; 46 47/** 48 * Find the best Service, and bind to it. 49 * Handles run-time package changes. 50 */ 51public 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 public boolean start() { 146 synchronized (mLock) { 147 bindBestPackageLocked(mServicePackageName, false); 148 } 149 150 // listen for user change 151 IntentFilter intentFilter = new IntentFilter(); 152 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 153 intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); 154 mContext.registerReceiverAsUser(new BroadcastReceiver() { 155 @Override 156 public void onReceive(Context context, Intent intent) { 157 final String action = intent.getAction(); 158 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 159 UserHandle.USER_NULL); 160 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 161 switchUser(userId); 162 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 163 unlockUser(userId); 164 } 165 } 166 }, UserHandle.ALL, intentFilter, null, mHandler); 167 168 // listen for relevant package changes if service overlay is enabled. 169 if (mServicePackageName == null) { 170 mPackageMonitor.register(mContext, null, UserHandle.ALL, true); 171 } 172 173 return true; 174 } 175 176 /** 177 * Searches and binds to the best package, or do nothing if the best package 178 * is already bound, unless force rebinding is requested. 179 * 180 * @param justCheckThisPackage Only consider this package, or consider all 181 * packages if it is {@code null}. 182 * @param forceRebind Force a rebinding to the best package if it's already 183 * bound. 184 * @return {@code true} if a valid package was found to bind to. 185 */ 186 private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) { 187 Intent intent = new Intent(mAction); 188 if (justCheckThisPackage != null) { 189 intent.setPackage(justCheckThisPackage); 190 } 191 final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent, 192 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 193 mCurrentUserId); 194 int bestVersion = Integer.MIN_VALUE; 195 ComponentName bestComponent = null; 196 boolean bestIsMultiuser = false; 197 if (rInfos != null) { 198 for (ResolveInfo rInfo : rInfos) { 199 final ComponentName component = rInfo.serviceInfo.getComponentName(); 200 final String packageName = component.getPackageName(); 201 202 // check signature 203 try { 204 PackageInfo pInfo; 205 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES 206 | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); 207 if (!isSignatureMatch(pInfo.signatures)) { 208 Log.w(mTag, packageName + " resolves service " + mAction 209 + ", but has wrong signature, ignoring"); 210 continue; 211 } 212 } catch (NameNotFoundException e) { 213 Log.wtf(mTag, e); 214 continue; 215 } 216 217 // check metadata 218 int version = Integer.MIN_VALUE; 219 boolean isMultiuser = false; 220 if (rInfo.serviceInfo.metaData != null) { 221 version = rInfo.serviceInfo.metaData.getInt( 222 EXTRA_SERVICE_VERSION, Integer.MIN_VALUE); 223 isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER); 224 } 225 226 if (version > bestVersion) { 227 bestVersion = version; 228 bestComponent = component; 229 bestIsMultiuser = isMultiuser; 230 } 231 } 232 233 if (D) { 234 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, 235 (justCheckThisPackage == null ? "" 236 : "(" + justCheckThisPackage + ") "), rInfos.size(), 237 (bestComponent == null ? "no new best component" 238 : "new best component: " + bestComponent))); 239 } 240 } else { 241 if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction); 242 } 243 244 if (bestComponent == null) { 245 Slog.w(mTag, "Odd, no component found for service " + mAction); 246 unbindLocked(); 247 return false; 248 } 249 250 final int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId; 251 final boolean alreadyBound = Objects.equals(bestComponent, mBoundComponent) 252 && bestVersion == mBoundVersion && userId == mBoundUserId; 253 if (forceRebind || !alreadyBound) { 254 unbindLocked(); 255 bindToPackageLocked(bestComponent, bestVersion, userId); 256 } 257 return true; 258 } 259 260 private void unbindLocked() { 261 ComponentName component; 262 component = mBoundComponent; 263 mBoundComponent = null; 264 mBoundPackageName = null; 265 mBoundVersion = Integer.MIN_VALUE; 266 mBoundUserId = UserHandle.USER_NULL; 267 if (component != null) { 268 if (D) Log.d(mTag, "unbinding " + component); 269 mContext.unbindService(this); 270 } 271 } 272 273 private void bindToPackageLocked(ComponentName component, int version, int userId) { 274 Intent intent = new Intent(mAction); 275 intent.setComponent(component); 276 mBoundComponent = component; 277 mBoundPackageName = component.getPackageName(); 278 mBoundVersion = version; 279 mBoundUserId = userId; 280 if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")"); 281 mContext.bindServiceAsUser(intent, this, 282 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE, 283 new UserHandle(userId)); 284 } 285 286 public static boolean isSignatureMatch(Signature[] signatures, 287 List<HashSet<Signature>> sigSets) { 288 if (signatures == null) return false; 289 290 // build hashset of input to test against 291 HashSet<Signature> inputSet = new HashSet<Signature>(); 292 for (Signature s : signatures) { 293 inputSet.add(s); 294 } 295 296 // test input against each of the signature sets 297 for (HashSet<Signature> referenceSet : sigSets) { 298 if (referenceSet.equals(inputSet)) { 299 return true; 300 } 301 } 302 return false; 303 } 304 305 private boolean isSignatureMatch(Signature[] signatures) { 306 return isSignatureMatch(signatures, mSignatureSets); 307 } 308 309 private final PackageMonitor mPackageMonitor = new PackageMonitor() { 310 /** 311 * Called when package has been reinstalled 312 */ 313 @Override 314 public void onPackageUpdateFinished(String packageName, int uid) { 315 synchronized (mLock) { 316 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); 317 bindBestPackageLocked(null, forceRebind); 318 } 319 } 320 321 @Override 322 public void onPackageAdded(String packageName, int uid) { 323 synchronized (mLock) { 324 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); 325 bindBestPackageLocked(null, forceRebind); 326 } 327 } 328 329 @Override 330 public void onPackageRemoved(String packageName, int uid) { 331 synchronized (mLock) { 332 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); 333 bindBestPackageLocked(null, forceRebind); 334 } 335 } 336 337 @Override 338 public boolean onPackageChanged(String packageName, int uid, String[] components) { 339 synchronized (mLock) { 340 final boolean forceRebind = Objects.equals(packageName, mBoundPackageName); 341 bindBestPackageLocked(null, forceRebind); 342 } 343 return super.onPackageChanged(packageName, uid, components); 344 } 345 }; 346 347 @Override 348 public void onServiceConnected(ComponentName component, IBinder binder) { 349 synchronized (mLock) { 350 if (component.equals(mBoundComponent)) { 351 if (D) Log.d(mTag, component + " connected"); 352 mBoundService = binder; 353 if (mHandler !=null && mNewServiceWork != null) { 354 mHandler.post(mNewServiceWork); 355 } 356 } else { 357 Log.w(mTag, "unexpected onServiceConnected: " + component); 358 } 359 } 360 } 361 362 @Override 363 public void onServiceDisconnected(ComponentName component) { 364 synchronized (mLock) { 365 if (D) Log.d(mTag, component + " disconnected"); 366 367 if (component.equals(mBoundComponent)) { 368 mBoundService = null; 369 } 370 } 371 } 372 373 public @Nullable String getBestPackageName() { 374 synchronized (mLock) { 375 return mBoundPackageName; 376 } 377 } 378 379 public int getBestVersion() { 380 synchronized (mLock) { 381 return mBoundVersion; 382 } 383 } 384 385 public @Nullable IBinder getBinder() { 386 synchronized (mLock) { 387 return mBoundService; 388 } 389 } 390 391 public void switchUser(int userId) { 392 synchronized (mLock) { 393 mCurrentUserId = userId; 394 bindBestPackageLocked(mServicePackageName, false); 395 } 396 } 397 398 public void unlockUser(int userId) { 399 synchronized (mLock) { 400 if (userId == mCurrentUserId) { 401 bindBestPackageLocked(mServicePackageName, false); 402 } 403 } 404 } 405} 406