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