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.Nullable; 20 import android.app.ActivityManager; 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.Bundle; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.Looper; 37 import android.os.RemoteException; 38 import android.os.UserHandle; 39 import android.util.Log; 40 import android.util.Slog; 41 42 import com.android.internal.content.PackageMonitor; 43 import com.android.internal.util.Preconditions; 44 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.HashSet; 49 import java.util.List; 50 import java.util.Objects; 51 import java.util.concurrent.Callable; 52 import java.util.concurrent.ExecutionException; 53 import java.util.concurrent.FutureTask; 54 55 /** 56 * Find the best Service, and bind to it. 57 * Handles run-time package changes. 58 */ 59 public class ServiceWatcher implements ServiceConnection { 60 61 private static final String TAG = "ServiceWatcher"; 62 private static final boolean D = false; 63 64 public static final String EXTRA_SERVICE_VERSION = "serviceVersion"; 65 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser"; 66 67 68 /** Function to run on binder interface. */ 69 public interface BinderRunner { 70 /** Called to run client code with the binder. */ run(IBinder binder)71 void run(IBinder binder) throws RemoteException; 72 } 73 74 /** 75 * Function to run on binder interface. 76 * @param <T> Type to return. 77 */ 78 public interface BlockingBinderRunner<T> { 79 /** Called to run client code with the binder. */ run(IBinder binder)80 T run(IBinder binder) throws RemoteException; 81 } 82 getSignatureSets(Context context, String... packageNames)83 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, 84 String... packageNames) { 85 PackageManager pm = context.getPackageManager(); 86 87 ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length); 88 for (String packageName : packageNames) { 89 try { 90 Signature[] signatures = pm.getPackageInfo(packageName, 91 PackageManager.MATCH_SYSTEM_ONLY 92 | PackageManager.GET_SIGNATURES).signatures; 93 94 HashSet<Signature> set = new HashSet<>(); 95 Collections.addAll(set, signatures); 96 signatureSets.add(set); 97 } catch (NameNotFoundException e) { 98 Log.w(TAG, packageName + " not found"); 99 } 100 } 101 return signatureSets; 102 } 103 104 /** Checks if signatures match. */ isSignatureMatch(Signature[] signatures, List<HashSet<Signature>> sigSets)105 public static boolean isSignatureMatch(Signature[] signatures, 106 List<HashSet<Signature>> sigSets) { 107 if (signatures == null) return false; 108 109 // build hashset of input to test against 110 HashSet<Signature> inputSet = new HashSet<>(); 111 Collections.addAll(inputSet, signatures); 112 113 // test input against each of the signature sets 114 for (HashSet<Signature> referenceSet : sigSets) { 115 if (referenceSet.equals(inputSet)) { 116 return true; 117 } 118 } 119 return false; 120 } 121 122 private final Context mContext; 123 private final String mTag; 124 private final String mAction; 125 private final String mServicePackageName; 126 private final List<HashSet<Signature>> mSignatureSets; 127 128 private final Handler mHandler; 129 130 // read/write from handler thread 131 private IBinder mBestService; 132 private int mCurrentUserId; 133 134 // read from any thread, write from handler thread 135 private volatile ComponentName mBestComponent; 136 private volatile int mBestVersion; 137 private volatile int mBestUserId; 138 ServiceWatcher(Context context, String logTag, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Handler handler)139 public ServiceWatcher(Context context, String logTag, String action, 140 int overlaySwitchResId, int defaultServicePackageNameResId, 141 int initialPackageNamesResId, Handler handler) { 142 Resources resources = context.getResources(); 143 144 mContext = context; 145 mTag = logTag; 146 mAction = action; 147 148 boolean enableOverlay = resources.getBoolean(overlaySwitchResId); 149 if (enableOverlay) { 150 String[] pkgs = resources.getStringArray(initialPackageNamesResId); 151 mServicePackageName = null; 152 mSignatureSets = getSignatureSets(context, pkgs); 153 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs)); 154 } else { 155 mServicePackageName = resources.getString(defaultServicePackageNameResId); 156 mSignatureSets = getSignatureSets(context, mServicePackageName); 157 if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName); 158 } 159 160 mHandler = handler; 161 162 mBestComponent = null; 163 mBestVersion = Integer.MIN_VALUE; 164 mBestUserId = UserHandle.USER_NULL; 165 166 mBestService = null; 167 } 168 onBind()169 protected void onBind() {} 170 onUnbind()171 protected void onUnbind() {} 172 173 /** 174 * Start this watcher, including binding to the current best match and 175 * re-binding to any better matches down the road. 176 * <p> 177 * Note that if there are no matching encryption-aware services, we may not 178 * bind to a real service until after the current user is unlocked. 179 * 180 * @return {@code true} if a potential service implementation was found. 181 */ start()182 public final boolean start() { 183 // if we have to return false, do it before registering anything 184 if (isServiceMissing()) return false; 185 186 // listen for relevant package changes if service overlay is enabled on handler 187 if (mServicePackageName == null) { 188 new PackageMonitor() { 189 @Override 190 public void onPackageUpdateFinished(String packageName, int uid) { 191 bindBestPackage(Objects.equals(packageName, getCurrentPackageName())); 192 } 193 194 @Override 195 public void onPackageAdded(String packageName, int uid) { 196 bindBestPackage(Objects.equals(packageName, getCurrentPackageName())); 197 } 198 199 @Override 200 public void onPackageRemoved(String packageName, int uid) { 201 bindBestPackage(Objects.equals(packageName, getCurrentPackageName())); 202 } 203 204 @Override 205 public boolean onPackageChanged(String packageName, int uid, String[] components) { 206 bindBestPackage(Objects.equals(packageName, getCurrentPackageName())); 207 return super.onPackageChanged(packageName, uid, components); 208 } 209 }.register(mContext, UserHandle.ALL, true, mHandler); 210 } 211 212 // listen for user change on handler 213 IntentFilter intentFilter = new IntentFilter(); 214 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 215 intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); 216 mContext.registerReceiverAsUser(new BroadcastReceiver() { 217 @Override 218 public void onReceive(Context context, Intent intent) { 219 final String action = intent.getAction(); 220 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 221 UserHandle.USER_NULL); 222 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 223 mCurrentUserId = userId; 224 bindBestPackage(false); 225 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 226 if (userId == mCurrentUserId) { 227 bindBestPackage(false); 228 } 229 } 230 } 231 }, UserHandle.ALL, intentFilter, null, mHandler); 232 233 mCurrentUserId = ActivityManager.getCurrentUser(); 234 235 mHandler.post(() -> bindBestPackage(false)); 236 return true; 237 } 238 239 /** Returns the name of the currently connected package or null. */ 240 @Nullable getCurrentPackageName()241 public String getCurrentPackageName() { 242 ComponentName bestComponent = mBestComponent; 243 return bestComponent == null ? null : bestComponent.getPackageName(); 244 } 245 isServiceMissing()246 private boolean isServiceMissing() { 247 return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction), 248 PackageManager.MATCH_DIRECT_BOOT_AWARE 249 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 250 UserHandle.USER_SYSTEM).isEmpty(); 251 } 252 bindBestPackage(boolean forceRebind)253 private void bindBestPackage(boolean forceRebind) { 254 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); 255 256 Intent intent = new Intent(mAction); 257 if (mServicePackageName != null) { 258 intent.setPackage(mServicePackageName); 259 } 260 261 List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent, 262 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO, 263 mCurrentUserId); 264 if (rInfos == null) { 265 rInfos = Collections.emptyList(); 266 } 267 268 ComponentName bestComponent = null; 269 int bestVersion = Integer.MIN_VALUE; 270 boolean bestIsMultiuser = false; 271 272 for (ResolveInfo rInfo : rInfos) { 273 ComponentName component = rInfo.serviceInfo.getComponentName(); 274 String packageName = component.getPackageName(); 275 276 // check signature 277 try { 278 PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName, 279 PackageManager.GET_SIGNATURES 280 | PackageManager.MATCH_DIRECT_BOOT_AUTO); 281 if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) { 282 Log.w(mTag, packageName + " resolves service " + mAction 283 + ", but has wrong signature, ignoring"); 284 continue; 285 } 286 } catch (NameNotFoundException e) { 287 Log.wtf(mTag, e); 288 continue; 289 } 290 291 // check metadata 292 Bundle metadata = rInfo.serviceInfo.metaData; 293 int version = Integer.MIN_VALUE; 294 boolean isMultiuser = false; 295 if (metadata != null) { 296 version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE); 297 isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false); 298 } 299 300 if (version > bestVersion) { 301 bestComponent = component; 302 bestVersion = version; 303 bestIsMultiuser = isMultiuser; 304 } 305 } 306 307 if (D) { 308 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, 309 (mServicePackageName == null ? "" 310 : "(" + mServicePackageName + ") "), rInfos.size(), 311 (bestComponent == null ? "no new best component" 312 : "new best component: " + bestComponent))); 313 } 314 315 if (bestComponent == null) { 316 Slog.w(mTag, "Odd, no component found for service " + mAction); 317 unbind(); 318 return; 319 } 320 321 int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId; 322 boolean alreadyBound = Objects.equals(bestComponent, mBestComponent) 323 && bestVersion == mBestVersion && userId == mBestUserId; 324 if (forceRebind || !alreadyBound) { 325 unbind(); 326 bind(bestComponent, bestVersion, userId); 327 } 328 } 329 bind(ComponentName component, int version, int userId)330 private void bind(ComponentName component, int version, int userId) { 331 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); 332 333 Intent intent = new Intent(mAction); 334 intent.setComponent(component); 335 336 mBestComponent = component; 337 mBestVersion = version; 338 mBestUserId = userId; 339 340 if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")"); 341 mContext.bindServiceAsUser(intent, this, 342 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE, 343 UserHandle.of(userId)); 344 } 345 unbind()346 private void unbind() { 347 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); 348 349 if (mBestComponent != null) { 350 if (D) Log.d(mTag, "unbinding " + mBestComponent); 351 mContext.unbindService(this); 352 } 353 354 mBestComponent = null; 355 mBestVersion = Integer.MIN_VALUE; 356 mBestUserId = UserHandle.USER_NULL; 357 } 358 359 /** 360 * Runs the given function asynchronously if currently connected. Suppresses any RemoteException 361 * thrown during execution. 362 */ runOnBinder(BinderRunner runner)363 public final void runOnBinder(BinderRunner runner) { 364 runOnHandler(() -> { 365 if (mBestService == null) { 366 return; 367 } 368 369 try { 370 runner.run(mBestService); 371 } catch (RuntimeException e) { 372 // the code being run is privileged, but may be outside the system server, and thus 373 // we cannot allow runtime exceptions to crash the system server 374 Log.e(TAG, "exception while while running " + runner + " on " + mBestService 375 + " from " + this, e); 376 } catch (RemoteException e) { 377 // do nothing 378 } 379 }); 380 } 381 382 /** 383 * Runs the given function synchronously if currently connected, and returns the default value 384 * if not currently connected or if any exception is thrown. 385 * 386 * @deprecated Using this function is an indication that your AIDL API is broken. Calls from 387 * system server to outside MUST be one-way, and so cannot return any result, and this 388 * method should not be needed or used. Use a separate callback interface to allow outside 389 * components to return results back to the system server. 390 */ 391 @Deprecated runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue)392 public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) { 393 try { 394 return runOnHandlerBlocking(() -> { 395 if (mBestService == null) { 396 return defaultValue; 397 } 398 399 try { 400 return runner.run(mBestService); 401 } catch (RemoteException e) { 402 return defaultValue; 403 } 404 }); 405 } catch (InterruptedException e) { 406 return defaultValue; 407 } 408 } 409 410 @Override 411 public final void onServiceConnected(ComponentName component, IBinder binder) { 412 runOnHandler(() -> { 413 if (D) Log.d(mTag, component + " connected"); 414 mBestService = binder; 415 onBind(); 416 }); 417 } 418 419 @Override 420 public final void onServiceDisconnected(ComponentName component) { 421 runOnHandler(() -> { 422 if (D) Log.d(mTag, component + " disconnected"); 423 mBestService = null; 424 onUnbind(); 425 }); 426 } 427 428 @Override 429 public String toString() { 430 ComponentName bestComponent = mBestComponent; 431 return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion; 432 } 433 434 private void runOnHandler(Runnable r) { 435 if (Looper.myLooper() == mHandler.getLooper()) { 436 r.run(); 437 } else { 438 mHandler.post(r); 439 } 440 } 441 442 private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException { 443 if (Looper.myLooper() == mHandler.getLooper()) { 444 try { 445 return c.call(); 446 } catch (Exception e) { 447 // Function cannot throw exception, this should never happen 448 throw new IllegalStateException(e); 449 } 450 } else { 451 FutureTask<T> task = new FutureTask<>(c); 452 mHandler.post(task); 453 try { 454 return task.get(); 455 } catch (ExecutionException e) { 456 // Function cannot throw exception, this should never happen 457 throw new IllegalStateException(e); 458 } 459 } 460 } 461 } 462