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