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