ManagedServices.java revision af8d6c44f06d2f8baac2c5774a9efdae3fc36797
1/** 2 * Copyright (c) 2014, 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.notification; 18 19import android.app.ActivityManager; 20import android.app.PendingIntent; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.ServiceConnection; 26import android.content.pm.ApplicationInfo; 27import android.content.pm.PackageManager; 28import android.content.pm.PackageManager.NameNotFoundException; 29import android.content.pm.ResolveInfo; 30import android.content.pm.ServiceInfo; 31import android.content.pm.UserInfo; 32import android.database.ContentObserver; 33import android.net.Uri; 34import android.os.Build; 35import android.os.Handler; 36import android.os.IBinder; 37import android.os.IInterface; 38import android.os.RemoteException; 39import android.os.UserHandle; 40import android.os.UserManager; 41import android.provider.Settings; 42import android.text.TextUtils; 43import android.util.ArraySet; 44import android.util.Slog; 45import android.util.SparseArray; 46 47import java.io.PrintWriter; 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.List; 51import java.util.Set; 52 53/** 54 * Manages the lifecycle of application-provided services bound by system server. 55 * 56 * Services managed by this helper must have: 57 * - An associated system settings value with a list of enabled component names. 58 * - A well-known action for services to use in their intent-filter. 59 * - A system permission for services to require in order to ensure system has exclusive binding. 60 * - A settings page for user configuration of enabled services, and associated intent action. 61 * - A remote interface definition (aidl) provided by the service used for communication. 62 */ 63abstract public class ManagedServices { 64 protected final String TAG = getClass().getSimpleName(); 65 protected static final boolean DEBUG = true; 66 67 private static final String ENABLED_SERVICES_SEPARATOR = ":"; 68 69 protected final Context mContext; 70 protected final Object mMutex; 71 private final UserProfiles mUserProfiles; 72 private final SettingsObserver mSettingsObserver; 73 private final Config mConfig; 74 75 // contains connections to all connected services, including app services 76 // and system services 77 protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>(); 78 // things that will be put into mServices as soon as they're ready 79 private final ArrayList<String> mServicesBinding = new ArrayList<String>(); 80 // lists the component names of all enabled (and therefore connected) 81 // app services for current profiles. 82 private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles 83 = new ArraySet<ComponentName>(); 84 // Just the packages from mEnabledServicesForCurrentProfiles 85 private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<String>(); 86 87 public ManagedServices(Context context, Handler handler, Object mutex, 88 UserProfiles userProfiles) { 89 mContext = context; 90 mMutex = mutex; 91 mUserProfiles = userProfiles; 92 mConfig = getConfig(); 93 mSettingsObserver = new SettingsObserver(handler); 94 } 95 96 abstract protected Config getConfig(); 97 98 private String getCaption() { 99 return mConfig.caption; 100 } 101 102 abstract protected IInterface asInterface(IBinder binder); 103 104 abstract protected void onServiceAdded(ManagedServiceInfo info); 105 106 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { } 107 108 private ManagedServiceInfo newServiceInfo(IInterface service, 109 ComponentName component, int userid, boolean isSystem, ServiceConnection connection, 110 int targetSdkVersion) { 111 return new ManagedServiceInfo(service, component, userid, isSystem, connection, 112 targetSdkVersion); 113 } 114 115 public void onBootPhaseAppsCanStart() { 116 mSettingsObserver.observe(); 117 } 118 119 public void dump(PrintWriter pw) { 120 pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size() 121 + ") enabled for current profiles:"); 122 for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) { 123 pw.println(" " + cmpt); 124 } 125 126 pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):"); 127 for (ManagedServiceInfo info : mServices) { 128 pw.println(" " + info.component 129 + " (user " + info.userid + "): " + info.service 130 + (info.isSystem?" SYSTEM":"")); 131 } 132 } 133 134 public void onPackagesChanged(boolean queryReplace, String[] pkgList) { 135 if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace 136 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)) 137 + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames); 138 boolean anyServicesInvolved = false; 139 if (pkgList != null && (pkgList.length > 0)) { 140 for (String pkgName : pkgList) { 141 if (mEnabledServicesPackageNames.contains(pkgName)) { 142 anyServicesInvolved = true; 143 } 144 } 145 } 146 147 if (anyServicesInvolved) { 148 // if we're not replacing a package, clean up orphaned bits 149 if (!queryReplace) { 150 disableNonexistentServices(); 151 } 152 // make sure we're still bound to any of our services who may have just upgraded 153 rebindServices(); 154 } 155 } 156 157 public ManagedServiceInfo checkServiceTokenLocked(IInterface service) { 158 checkNotNull(service); 159 final IBinder token = service.asBinder(); 160 final int N = mServices.size(); 161 for (int i=0; i<N; i++) { 162 final ManagedServiceInfo info = mServices.get(i); 163 if (info.service.asBinder() == token) return info; 164 } 165 throw new SecurityException("Disallowed call from unknown " + getCaption() + ": " 166 + service); 167 } 168 169 public void unregisterService(IInterface service, int userid) { 170 checkNotNull(service); 171 // no need to check permissions; if your service binder is in the list, 172 // that's proof that you had permission to add it in the first place 173 unregisterServiceImpl(service, userid); 174 } 175 176 public void registerService(IInterface service, ComponentName component, int userid) { 177 checkNotNull(service); 178 registerServiceImpl(service, component, userid); 179 } 180 181 /** 182 * Remove access for any services that no longer exist. 183 */ 184 private void disableNonexistentServices() { 185 int[] userIds = mUserProfiles.getCurrentProfileIds(); 186 final int N = userIds.length; 187 for (int i = 0 ; i < N; ++i) { 188 disableNonexistentServices(userIds[i]); 189 } 190 } 191 192 private void disableNonexistentServices(int userId) { 193 String flatIn = Settings.Secure.getStringForUser( 194 mContext.getContentResolver(), 195 mConfig.secureSettingName, 196 userId); 197 if (!TextUtils.isEmpty(flatIn)) { 198 if (DEBUG) Slog.v(TAG, "flat before: " + flatIn); 199 PackageManager pm = mContext.getPackageManager(); 200 List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( 201 new Intent(mConfig.serviceInterface), 202 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, 203 userId); 204 if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices); 205 Set<ComponentName> installed = new ArraySet<ComponentName>(); 206 for (int i = 0, count = installedServices.size(); i < count; i++) { 207 ResolveInfo resolveInfo = installedServices.get(i); 208 ServiceInfo info = resolveInfo.serviceInfo; 209 210 if (!mConfig.bindPermission.equals(info.permission)) { 211 Slog.w(TAG, "Skipping " + getCaption() + " service " 212 + info.packageName + "/" + info.name 213 + ": it does not require the permission " 214 + mConfig.bindPermission); 215 continue; 216 } 217 installed.add(new ComponentName(info.packageName, info.name)); 218 } 219 220 String flatOut = ""; 221 if (!installed.isEmpty()) { 222 String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR); 223 ArrayList<String> remaining = new ArrayList<String>(enabled.length); 224 for (int i = 0; i < enabled.length; i++) { 225 ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]); 226 if (installed.contains(enabledComponent)) { 227 remaining.add(enabled[i]); 228 } 229 } 230 flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining); 231 } 232 if (DEBUG) Slog.v(TAG, "flat after: " + flatOut); 233 if (!flatIn.equals(flatOut)) { 234 Settings.Secure.putStringForUser(mContext.getContentResolver(), 235 mConfig.secureSettingName, 236 flatOut, userId); 237 } 238 } 239 } 240 241 /** 242 * Called whenever packages change, the user switches, or the secure setting 243 * is altered. (For example in response to USER_SWITCHED in our broadcast receiver) 244 */ 245 private void rebindServices() { 246 if (DEBUG) Slog.d(TAG, "rebindServices"); 247 final int[] userIds = mUserProfiles.getCurrentProfileIds(); 248 final int nUserIds = userIds.length; 249 250 final SparseArray<String> flat = new SparseArray<String>(); 251 252 for (int i = 0; i < nUserIds; ++i) { 253 flat.put(userIds[i], Settings.Secure.getStringForUser( 254 mContext.getContentResolver(), 255 mConfig.secureSettingName, 256 userIds[i])); 257 } 258 259 ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()]; 260 final SparseArray<ArrayList<ComponentName>> toAdd 261 = new SparseArray<ArrayList<ComponentName>>(); 262 263 synchronized (mMutex) { 264 // unbind and remove all existing services 265 toRemove = mServices.toArray(toRemove); 266 267 final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>(); 268 final ArraySet<String> newPackages = new ArraySet<String>(); 269 270 for (int i = 0; i < nUserIds; ++i) { 271 final ArrayList<ComponentName> add = new ArrayList<ComponentName>(); 272 toAdd.put(userIds[i], add); 273 274 // decode the list of components 275 String toDecode = flat.get(userIds[i]); 276 if (toDecode != null) { 277 String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR); 278 for (int j = 0; j < components.length; j++) { 279 final ComponentName component 280 = ComponentName.unflattenFromString(components[j]); 281 if (component != null) { 282 newEnabled.add(component); 283 add.add(component); 284 newPackages.add(component.getPackageName()); 285 } 286 } 287 288 } 289 } 290 mEnabledServicesForCurrentProfiles = newEnabled; 291 mEnabledServicesPackageNames = newPackages; 292 } 293 294 for (ManagedServiceInfo info : toRemove) { 295 final ComponentName component = info.component; 296 final int oldUser = info.userid; 297 Slog.v(TAG, "disabling " + getCaption() + " for user " 298 + oldUser + ": " + component); 299 unregisterService(component, info.userid); 300 } 301 302 for (int i = 0; i < nUserIds; ++i) { 303 final ArrayList<ComponentName> add = toAdd.get(userIds[i]); 304 final int N = add.size(); 305 for (int j = 0; j < N; j++) { 306 final ComponentName component = add.get(j); 307 Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": " 308 + component); 309 registerService(component, userIds[i]); 310 } 311 } 312 } 313 314 /** 315 * Version of registerService that takes the name of a service component to bind to. 316 */ 317 private void registerService(final ComponentName name, final int userid) { 318 if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid); 319 320 synchronized (mMutex) { 321 final String servicesBindingTag = name.toString() + "/" + userid; 322 if (mServicesBinding.contains(servicesBindingTag)) { 323 // stop registering this thing already! we're working on it 324 return; 325 } 326 mServicesBinding.add(servicesBindingTag); 327 328 final int N = mServices.size(); 329 for (int i=N-1; i>=0; i--) { 330 final ManagedServiceInfo info = mServices.get(i); 331 if (name.equals(info.component) 332 && info.userid == userid) { 333 // cut old connections 334 if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": " 335 + info.service); 336 removeServiceLocked(i); 337 if (info.connection != null) { 338 mContext.unbindService(info.connection); 339 } 340 } 341 } 342 343 Intent intent = new Intent(mConfig.serviceInterface); 344 intent.setComponent(name); 345 346 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel); 347 348 final PendingIntent pendingIntent = PendingIntent.getActivity( 349 mContext, 0, new Intent(mConfig.settingsAction), 0); 350 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); 351 352 ApplicationInfo appInfo = null; 353 try { 354 appInfo = mContext.getPackageManager().getApplicationInfo( 355 name.getPackageName(), 0); 356 } catch (NameNotFoundException e) { 357 // Ignore if the package doesn't exist we won't be able to bind to the service. 358 } 359 final int targetSdkVersion = 360 appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE; 361 362 try { 363 if (DEBUG) Slog.v(TAG, "binding: " + intent); 364 if (!mContext.bindServiceAsUser(intent, 365 new ServiceConnection() { 366 IInterface mService; 367 368 @Override 369 public void onServiceConnected(ComponentName name, IBinder binder) { 370 boolean added = false; 371 ManagedServiceInfo info = null; 372 synchronized (mMutex) { 373 mServicesBinding.remove(servicesBindingTag); 374 try { 375 mService = asInterface(binder); 376 info = newServiceInfo(mService, name, 377 userid, false /*isSystem*/, this, targetSdkVersion); 378 binder.linkToDeath(info, 0); 379 added = mServices.add(info); 380 } catch (RemoteException e) { 381 // already dead 382 } 383 } 384 if (added) { 385 onServiceAdded(info); 386 } 387 } 388 389 @Override 390 public void onServiceDisconnected(ComponentName name) { 391 Slog.v(TAG, getCaption() + " connection lost: " + name); 392 } 393 }, 394 Context.BIND_AUTO_CREATE, 395 new UserHandle(userid))) 396 { 397 mServicesBinding.remove(servicesBindingTag); 398 Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); 399 return; 400 } 401 } catch (SecurityException ex) { 402 Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex); 403 return; 404 } 405 } 406 } 407 408 /** 409 * Remove a service for the given user by ComponentName 410 */ 411 private void unregisterService(ComponentName name, int userid) { 412 synchronized (mMutex) { 413 final int N = mServices.size(); 414 for (int i=N-1; i>=0; i--) { 415 final ManagedServiceInfo info = mServices.get(i); 416 if (name.equals(info.component) 417 && info.userid == userid) { 418 removeServiceLocked(i); 419 if (info.connection != null) { 420 try { 421 mContext.unbindService(info.connection); 422 } catch (IllegalArgumentException ex) { 423 // something happened to the service: we think we have a connection 424 // but it's bogus. 425 Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex); 426 } 427 } 428 } 429 } 430 } 431 } 432 433 /** 434 * Removes a service from the list but does not unbind 435 * 436 * @return the removed service. 437 */ 438 private ManagedServiceInfo removeServiceImpl(IInterface service, final int userid) { 439 if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid); 440 ManagedServiceInfo serviceInfo = null; 441 synchronized (mMutex) { 442 final int N = mServices.size(); 443 for (int i=N-1; i>=0; i--) { 444 final ManagedServiceInfo info = mServices.get(i); 445 if (info.service.asBinder() == service.asBinder() 446 && info.userid == userid) { 447 if (DEBUG) Slog.d(TAG, "Removing active service " + info.component); 448 serviceInfo = removeServiceLocked(i); 449 } 450 } 451 } 452 return serviceInfo; 453 } 454 455 private ManagedServiceInfo removeServiceLocked(int i) { 456 final ManagedServiceInfo info = mServices.remove(i); 457 onServiceRemovedLocked(info); 458 return info; 459 } 460 461 private void checkNotNull(IInterface service) { 462 if (service == null) { 463 throw new IllegalArgumentException(getCaption() + " must not be null"); 464 } 465 } 466 467 private void registerServiceImpl(final IInterface service, 468 final ComponentName component, final int userid) { 469 synchronized (mMutex) { 470 try { 471 ManagedServiceInfo info = newServiceInfo(service, component, userid, 472 true /*isSystem*/, null, Build.VERSION_CODES.L); 473 service.asBinder().linkToDeath(info, 0); 474 mServices.add(info); 475 } catch (RemoteException e) { 476 // already dead 477 } 478 } 479 } 480 481 /** 482 * Removes a service from the list and unbinds. 483 */ 484 private void unregisterServiceImpl(IInterface service, int userid) { 485 ManagedServiceInfo info = removeServiceImpl(service, userid); 486 if (info != null && info.connection != null) { 487 mContext.unbindService(info.connection); 488 } 489 } 490 491 private class SettingsObserver extends ContentObserver { 492 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName); 493 494 private SettingsObserver(Handler handler) { 495 super(handler); 496 } 497 498 private void observe() { 499 ContentResolver resolver = mContext.getContentResolver(); 500 resolver.registerContentObserver(mSecureSettingsUri, 501 false, this, UserHandle.USER_ALL); 502 update(null); 503 } 504 505 @Override 506 public void onChange(boolean selfChange, Uri uri) { 507 update(uri); 508 } 509 510 private void update(Uri uri) { 511 if (uri == null || mSecureSettingsUri.equals(uri)) { 512 rebindServices(); 513 } 514 } 515 } 516 517 public class ManagedServiceInfo implements IBinder.DeathRecipient { 518 public IInterface service; 519 public ComponentName component; 520 public int userid; 521 public boolean isSystem; 522 public ServiceConnection connection; 523 public int targetSdkVersion; 524 525 public ManagedServiceInfo(IInterface service, ComponentName component, 526 int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) { 527 this.service = service; 528 this.component = component; 529 this.userid = userid; 530 this.isSystem = isSystem; 531 this.connection = connection; 532 this.targetSdkVersion = targetSdkVersion; 533 } 534 535 @Override 536 public String toString() { 537 return new StringBuilder("ManagedServiceInfo[") 538 .append("component=").append(component) 539 .append(",userid=").append(userid) 540 .append(",isSystem=").append(isSystem) 541 .append(",targetSdkVersion=").append(targetSdkVersion) 542 .append(",connection=").append(connection == null ? null : "<connection>") 543 .append(",service=").append(service) 544 .append(']').toString(); 545 } 546 547 public boolean enabledAndUserMatches(int nid) { 548 if (!isEnabledForCurrentProfiles()) { 549 return false; 550 } 551 if (this.userid == UserHandle.USER_ALL) return true; 552 if (nid == UserHandle.USER_ALL || nid == this.userid) return true; 553 return supportsProfiles() && mUserProfiles.isCurrentProfile(nid); 554 } 555 556 public boolean supportsProfiles() { 557 return targetSdkVersion >= Build.VERSION_CODES.L; 558 } 559 560 @Override 561 public void binderDied() { 562 if (DEBUG) Slog.d(TAG, "binderDied"); 563 // Remove the service, but don't unbind from the service. The system will bring the 564 // service back up, and the onServiceConnected handler will readd the service with the 565 // new binding. If this isn't a bound service, and is just a registered 566 // service, just removing it from the list is all we need to do anyway. 567 removeServiceImpl(this.service, this.userid); 568 } 569 570 /** convenience method for looking in mEnabledServicesForCurrentProfiles */ 571 public boolean isEnabledForCurrentProfiles() { 572 if (this.isSystem) return true; 573 if (this.connection == null) return false; 574 return mEnabledServicesForCurrentProfiles.contains(this.component); 575 } 576 } 577 578 public static class UserProfiles { 579 // Profiles of the current user. 580 private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); 581 582 public void updateCache(Context context) { 583 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 584 if (userManager != null) { 585 int currentUserId = ActivityManager.getCurrentUser(); 586 List<UserInfo> profiles = userManager.getProfiles(currentUserId); 587 synchronized (mCurrentProfiles) { 588 mCurrentProfiles.clear(); 589 for (UserInfo user : profiles) { 590 mCurrentProfiles.put(user.id, user); 591 } 592 } 593 } 594 } 595 596 public int[] getCurrentProfileIds() { 597 synchronized (mCurrentProfiles) { 598 int[] users = new int[mCurrentProfiles.size()]; 599 final int N = mCurrentProfiles.size(); 600 for (int i = 0; i < N; ++i) { 601 users[i] = mCurrentProfiles.keyAt(i); 602 } 603 return users; 604 } 605 } 606 607 public boolean isCurrentProfile(int userId) { 608 synchronized (mCurrentProfiles) { 609 return mCurrentProfiles.get(userId) != null; 610 } 611 } 612 } 613 614 protected static class Config { 615 String caption; 616 String serviceInterface; 617 String secureSettingName; 618 String bindPermission; 619 String settingsAction; 620 int clientLabel; 621 } 622} 623