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 * @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