ManagedServices.java revision 3e144d3dfcf209349ead9995877f53feb0b5a66e
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 ManagedServiceInfo info = registerServiceImpl(service, component, userid); 179 if (info != null) { 180 onServiceAdded(info); 181 } 182 } 183 184 /** 185 * Remove access for any services that no longer exist. 186 */ 187 private void disableNonexistentServices() { 188 int[] userIds = mUserProfiles.getCurrentProfileIds(); 189 final int N = userIds.length; 190 for (int i = 0 ; i < N; ++i) { 191 disableNonexistentServices(userIds[i]); 192 } 193 } 194 195 private void disableNonexistentServices(int userId) { 196 String flatIn = Settings.Secure.getStringForUser( 197 mContext.getContentResolver(), 198 mConfig.secureSettingName, 199 userId); 200 if (!TextUtils.isEmpty(flatIn)) { 201 if (DEBUG) Slog.v(TAG, "flat before: " + flatIn); 202 PackageManager pm = mContext.getPackageManager(); 203 List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( 204 new Intent(mConfig.serviceInterface), 205 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, 206 userId); 207 if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices); 208 Set<ComponentName> installed = new ArraySet<ComponentName>(); 209 for (int i = 0, count = installedServices.size(); i < count; i++) { 210 ResolveInfo resolveInfo = installedServices.get(i); 211 ServiceInfo info = resolveInfo.serviceInfo; 212 213 if (!mConfig.bindPermission.equals(info.permission)) { 214 Slog.w(TAG, "Skipping " + getCaption() + " service " 215 + info.packageName + "/" + info.name 216 + ": it does not require the permission " 217 + mConfig.bindPermission); 218 continue; 219 } 220 installed.add(new ComponentName(info.packageName, info.name)); 221 } 222 223 String flatOut = ""; 224 if (!installed.isEmpty()) { 225 String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR); 226 ArrayList<String> remaining = new ArrayList<String>(enabled.length); 227 for (int i = 0; i < enabled.length; i++) { 228 ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]); 229 if (installed.contains(enabledComponent)) { 230 remaining.add(enabled[i]); 231 } 232 } 233 flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining); 234 } 235 if (DEBUG) Slog.v(TAG, "flat after: " + flatOut); 236 if (!flatIn.equals(flatOut)) { 237 Settings.Secure.putStringForUser(mContext.getContentResolver(), 238 mConfig.secureSettingName, 239 flatOut, userId); 240 } 241 } 242 } 243 244 /** 245 * Called whenever packages change, the user switches, or the secure setting 246 * is altered. (For example in response to USER_SWITCHED in our broadcast receiver) 247 */ 248 private void rebindServices() { 249 if (DEBUG) Slog.d(TAG, "rebindServices"); 250 final int[] userIds = mUserProfiles.getCurrentProfileIds(); 251 final int nUserIds = userIds.length; 252 253 final SparseArray<String> flat = new SparseArray<String>(); 254 255 for (int i = 0; i < nUserIds; ++i) { 256 flat.put(userIds[i], Settings.Secure.getStringForUser( 257 mContext.getContentResolver(), 258 mConfig.secureSettingName, 259 userIds[i])); 260 } 261 262 ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()]; 263 final SparseArray<ArrayList<ComponentName>> toAdd 264 = new SparseArray<ArrayList<ComponentName>>(); 265 266 synchronized (mMutex) { 267 // unbind and remove all existing services 268 toRemove = mServices.toArray(toRemove); 269 270 final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>(); 271 final ArraySet<String> newPackages = new ArraySet<String>(); 272 273 for (int i = 0; i < nUserIds; ++i) { 274 final ArrayList<ComponentName> add = new ArrayList<ComponentName>(); 275 toAdd.put(userIds[i], add); 276 277 // decode the list of components 278 String toDecode = flat.get(userIds[i]); 279 if (toDecode != null) { 280 String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR); 281 for (int j = 0; j < components.length; j++) { 282 final ComponentName component 283 = ComponentName.unflattenFromString(components[j]); 284 if (component != null) { 285 newEnabled.add(component); 286 add.add(component); 287 newPackages.add(component.getPackageName()); 288 } 289 } 290 291 } 292 } 293 mEnabledServicesForCurrentProfiles = newEnabled; 294 mEnabledServicesPackageNames = newPackages; 295 } 296 297 for (ManagedServiceInfo info : toRemove) { 298 final ComponentName component = info.component; 299 final int oldUser = info.userid; 300 Slog.v(TAG, "disabling " + getCaption() + " for user " 301 + oldUser + ": " + component); 302 unregisterService(component, info.userid); 303 } 304 305 for (int i = 0; i < nUserIds; ++i) { 306 final ArrayList<ComponentName> add = toAdd.get(userIds[i]); 307 final int N = add.size(); 308 for (int j = 0; j < N; j++) { 309 final ComponentName component = add.get(j); 310 Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": " 311 + component); 312 registerService(component, userIds[i]); 313 } 314 } 315 } 316 317 /** 318 * Version of registerService that takes the name of a service component to bind to. 319 */ 320 private void registerService(final ComponentName name, final int userid) { 321 if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid); 322 323 synchronized (mMutex) { 324 final String servicesBindingTag = name.toString() + "/" + userid; 325 if (mServicesBinding.contains(servicesBindingTag)) { 326 // stop registering this thing already! we're working on it 327 return; 328 } 329 mServicesBinding.add(servicesBindingTag); 330 331 final int N = mServices.size(); 332 for (int i=N-1; i>=0; i--) { 333 final ManagedServiceInfo info = mServices.get(i); 334 if (name.equals(info.component) 335 && info.userid == userid) { 336 // cut old connections 337 if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": " 338 + info.service); 339 removeServiceLocked(i); 340 if (info.connection != null) { 341 mContext.unbindService(info.connection); 342 } 343 } 344 } 345 346 Intent intent = new Intent(mConfig.serviceInterface); 347 intent.setComponent(name); 348 349 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel); 350 351 final PendingIntent pendingIntent = PendingIntent.getActivity( 352 mContext, 0, new Intent(mConfig.settingsAction), 0); 353 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); 354 355 ApplicationInfo appInfo = null; 356 try { 357 appInfo = mContext.getPackageManager().getApplicationInfo( 358 name.getPackageName(), 0); 359 } catch (NameNotFoundException e) { 360 // Ignore if the package doesn't exist we won't be able to bind to the service. 361 } 362 final int targetSdkVersion = 363 appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE; 364 365 try { 366 if (DEBUG) Slog.v(TAG, "binding: " + intent); 367 if (!mContext.bindServiceAsUser(intent, 368 new ServiceConnection() { 369 IInterface mService; 370 371 @Override 372 public void onServiceConnected(ComponentName name, IBinder binder) { 373 boolean added = false; 374 ManagedServiceInfo info = null; 375 synchronized (mMutex) { 376 mServicesBinding.remove(servicesBindingTag); 377 try { 378 mService = asInterface(binder); 379 info = newServiceInfo(mService, name, 380 userid, false /*isSystem*/, this, targetSdkVersion); 381 binder.linkToDeath(info, 0); 382 added = mServices.add(info); 383 } catch (RemoteException e) { 384 // already dead 385 } 386 } 387 if (added) { 388 onServiceAdded(info); 389 } 390 } 391 392 @Override 393 public void onServiceDisconnected(ComponentName name) { 394 Slog.v(TAG, getCaption() + " connection lost: " + name); 395 } 396 }, 397 Context.BIND_AUTO_CREATE, 398 new UserHandle(userid))) 399 { 400 mServicesBinding.remove(servicesBindingTag); 401 Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); 402 return; 403 } 404 } catch (SecurityException ex) { 405 Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex); 406 return; 407 } 408 } 409 } 410 411 /** 412 * Remove a service for the given user by ComponentName 413 */ 414 private void unregisterService(ComponentName name, int userid) { 415 synchronized (mMutex) { 416 final int N = mServices.size(); 417 for (int i=N-1; i>=0; i--) { 418 final ManagedServiceInfo info = mServices.get(i); 419 if (name.equals(info.component) 420 && info.userid == userid) { 421 removeServiceLocked(i); 422 if (info.connection != null) { 423 try { 424 mContext.unbindService(info.connection); 425 } catch (IllegalArgumentException ex) { 426 // something happened to the service: we think we have a connection 427 // but it's bogus. 428 Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex); 429 } 430 } 431 } 432 } 433 } 434 } 435 436 /** 437 * Removes a service from the list but does not unbind 438 * 439 * @return the removed service. 440 */ 441 private ManagedServiceInfo removeServiceImpl(IInterface service, final int userid) { 442 if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid); 443 ManagedServiceInfo serviceInfo = null; 444 synchronized (mMutex) { 445 final int N = mServices.size(); 446 for (int i=N-1; i>=0; i--) { 447 final ManagedServiceInfo info = mServices.get(i); 448 if (info.service.asBinder() == service.asBinder() 449 && info.userid == userid) { 450 if (DEBUG) Slog.d(TAG, "Removing active service " + info.component); 451 serviceInfo = removeServiceLocked(i); 452 } 453 } 454 } 455 return serviceInfo; 456 } 457 458 private ManagedServiceInfo removeServiceLocked(int i) { 459 final ManagedServiceInfo info = mServices.remove(i); 460 onServiceRemovedLocked(info); 461 return info; 462 } 463 464 private void checkNotNull(IInterface service) { 465 if (service == null) { 466 throw new IllegalArgumentException(getCaption() + " must not be null"); 467 } 468 } 469 470 private ManagedServiceInfo registerServiceImpl(final IInterface service, 471 final ComponentName component, final int userid) { 472 synchronized (mMutex) { 473 try { 474 ManagedServiceInfo info = newServiceInfo(service, component, userid, 475 true /*isSystem*/, null, Build.VERSION_CODES.L); 476 service.asBinder().linkToDeath(info, 0); 477 mServices.add(info); 478 return info; 479 } catch (RemoteException e) { 480 // already dead 481 } 482 } 483 return null; 484 } 485 486 /** 487 * Removes a service from the list and unbinds. 488 */ 489 private void unregisterServiceImpl(IInterface service, int userid) { 490 ManagedServiceInfo info = removeServiceImpl(service, userid); 491 if (info != null && info.connection != null) { 492 mContext.unbindService(info.connection); 493 } 494 } 495 496 private class SettingsObserver extends ContentObserver { 497 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName); 498 499 private SettingsObserver(Handler handler) { 500 super(handler); 501 } 502 503 private void observe() { 504 ContentResolver resolver = mContext.getContentResolver(); 505 resolver.registerContentObserver(mSecureSettingsUri, 506 false, this, UserHandle.USER_ALL); 507 update(null); 508 } 509 510 @Override 511 public void onChange(boolean selfChange, Uri uri) { 512 update(uri); 513 } 514 515 private void update(Uri uri) { 516 if (uri == null || mSecureSettingsUri.equals(uri)) { 517 rebindServices(); 518 } 519 } 520 } 521 522 public class ManagedServiceInfo implements IBinder.DeathRecipient { 523 public IInterface service; 524 public ComponentName component; 525 public int userid; 526 public boolean isSystem; 527 public ServiceConnection connection; 528 public int targetSdkVersion; 529 530 public ManagedServiceInfo(IInterface service, ComponentName component, 531 int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) { 532 this.service = service; 533 this.component = component; 534 this.userid = userid; 535 this.isSystem = isSystem; 536 this.connection = connection; 537 this.targetSdkVersion = targetSdkVersion; 538 } 539 540 @Override 541 public String toString() { 542 return new StringBuilder("ManagedServiceInfo[") 543 .append("component=").append(component) 544 .append(",userid=").append(userid) 545 .append(",isSystem=").append(isSystem) 546 .append(",targetSdkVersion=").append(targetSdkVersion) 547 .append(",connection=").append(connection == null ? null : "<connection>") 548 .append(",service=").append(service) 549 .append(']').toString(); 550 } 551 552 public boolean enabledAndUserMatches(int nid) { 553 if (!isEnabledForCurrentProfiles()) { 554 return false; 555 } 556 if (this.userid == UserHandle.USER_ALL) return true; 557 if (nid == UserHandle.USER_ALL || nid == this.userid) return true; 558 return supportsProfiles() && mUserProfiles.isCurrentProfile(nid); 559 } 560 561 public boolean supportsProfiles() { 562 return targetSdkVersion >= Build.VERSION_CODES.L; 563 } 564 565 @Override 566 public void binderDied() { 567 if (DEBUG) Slog.d(TAG, "binderDied"); 568 // Remove the service, but don't unbind from the service. The system will bring the 569 // service back up, and the onServiceConnected handler will readd the service with the 570 // new binding. If this isn't a bound service, and is just a registered 571 // service, just removing it from the list is all we need to do anyway. 572 removeServiceImpl(this.service, this.userid); 573 } 574 575 /** convenience method for looking in mEnabledServicesForCurrentProfiles */ 576 public boolean isEnabledForCurrentProfiles() { 577 if (this.isSystem) return true; 578 if (this.connection == null) return false; 579 return mEnabledServicesForCurrentProfiles.contains(this.component); 580 } 581 } 582 583 public static class UserProfiles { 584 // Profiles of the current user. 585 private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); 586 587 public void updateCache(Context context) { 588 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 589 if (userManager != null) { 590 int currentUserId = ActivityManager.getCurrentUser(); 591 List<UserInfo> profiles = userManager.getProfiles(currentUserId); 592 synchronized (mCurrentProfiles) { 593 mCurrentProfiles.clear(); 594 for (UserInfo user : profiles) { 595 mCurrentProfiles.put(user.id, user); 596 } 597 } 598 } 599 } 600 601 public int[] getCurrentProfileIds() { 602 synchronized (mCurrentProfiles) { 603 int[] users = new int[mCurrentProfiles.size()]; 604 final int N = mCurrentProfiles.size(); 605 for (int i = 0; i < N; ++i) { 606 users[i] = mCurrentProfiles.keyAt(i); 607 } 608 return users; 609 } 610 } 611 612 public boolean isCurrentProfile(int userId) { 613 synchronized (mCurrentProfiles) { 614 return mCurrentProfiles.get(userId) != null; 615 } 616 } 617 } 618 619 protected static class Config { 620 String caption; 621 String serviceInterface; 622 String secureSettingName; 623 String bindPermission; 624 String settingsAction; 625 int clientLabel; 626 } 627} 628