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