1/* 2 * Copyright (C) 2006 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 android.content; 18 19import android.accounts.Account; 20import android.app.ActivityManager; 21import android.database.IContentObserver; 22import android.database.sqlite.SQLiteException; 23import android.net.Uri; 24import android.os.Binder; 25import android.os.Bundle; 26import android.os.IBinder; 27import android.os.Parcel; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.os.UserHandle; 31import android.util.Log; 32import android.util.SparseIntArray; 33import android.Manifest; 34 35import java.io.FileDescriptor; 36import java.io.PrintWriter; 37import java.security.InvalidParameterException; 38import java.util.ArrayList; 39import java.util.Collections; 40import java.util.Comparator; 41import java.util.List; 42 43/** 44 * {@hide} 45 */ 46public final class ContentService extends IContentService.Stub { 47 private static final String TAG = "ContentService"; 48 private Context mContext; 49 private boolean mFactoryTest; 50 private final ObserverNode mRootNode = new ObserverNode(""); 51 private SyncManager mSyncManager = null; 52 private final Object mSyncManagerLock = new Object(); 53 54 private SyncManager getSyncManager() { 55 synchronized(mSyncManagerLock) { 56 try { 57 // Try to create the SyncManager, return null if it fails (e.g. the disk is full). 58 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest); 59 } catch (SQLiteException e) { 60 Log.e(TAG, "Can't create SyncManager", e); 61 } 62 return mSyncManager; 63 } 64 } 65 66 @Override 67 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 68 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP, 69 "caller doesn't have the DUMP permission"); 70 71 // This makes it so that future permission checks will be in the context of this 72 // process rather than the caller's process. We will restore this before returning. 73 long identityToken = clearCallingIdentity(); 74 try { 75 if (mSyncManager == null) { 76 pw.println("No SyncManager created! (Disk full?)"); 77 } else { 78 mSyncManager.dump(fd, pw); 79 } 80 pw.println(); 81 pw.println("Observer tree:"); 82 synchronized (mRootNode) { 83 int[] counts = new int[2]; 84 final SparseIntArray pidCounts = new SparseIntArray(); 85 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts); 86 pw.println(); 87 ArrayList<Integer> sorted = new ArrayList<Integer>(); 88 for (int i=0; i<pidCounts.size(); i++) { 89 sorted.add(pidCounts.keyAt(i)); 90 } 91 Collections.sort(sorted, new Comparator<Integer>() { 92 @Override 93 public int compare(Integer lhs, Integer rhs) { 94 int lc = pidCounts.get(lhs); 95 int rc = pidCounts.get(rhs); 96 if (lc < rc) { 97 return 1; 98 } else if (lc > rc) { 99 return -1; 100 } 101 return 0; 102 } 103 104 }); 105 for (int i=0; i<sorted.size(); i++) { 106 int pid = sorted.get(i); 107 pw.print(" pid "); pw.print(pid); pw.print(": "); 108 pw.print(pidCounts.get(pid)); pw.println(" observers"); 109 } 110 pw.println(); 111 pw.print(" Total number of nodes: "); pw.println(counts[0]); 112 pw.print(" Total number of observers: "); pw.println(counts[1]); 113 } 114 } finally { 115 restoreCallingIdentity(identityToken); 116 } 117 } 118 119 @Override 120 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 121 throws RemoteException { 122 try { 123 return super.onTransact(code, data, reply, flags); 124 } catch (RuntimeException e) { 125 // The content service only throws security exceptions, so let's 126 // log all others. 127 if (!(e instanceof SecurityException)) { 128 Log.e(TAG, "Content Service Crash", e); 129 } 130 throw e; 131 } 132 } 133 134 /*package*/ ContentService(Context context, boolean factoryTest) { 135 mContext = context; 136 mFactoryTest = factoryTest; 137 } 138 139 public void systemReady() { 140 getSyncManager(); 141 } 142 143 /** 144 * Register a content observer tied to a specific user's view of the provider. 145 * @param userHandle the user whose view of the provider is to be observed. May be 146 * the calling user without requiring any permission, otherwise the caller needs to 147 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and 148 * USER_CURRENT are properly handled; all other pseudousers are forbidden. 149 */ 150 @Override 151 public void registerContentObserver(Uri uri, boolean notifyForDescendants, 152 IContentObserver observer, int userHandle) { 153 if (observer == null || uri == null) { 154 throw new IllegalArgumentException("You must pass a valid uri and observer"); 155 } 156 157 final int callingUser = UserHandle.getCallingUserId(); 158 if (callingUser != userHandle) { 159 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, 160 "no permission to observe other users' provider view"); 161 } 162 163 if (userHandle < 0) { 164 if (userHandle == UserHandle.USER_CURRENT) { 165 userHandle = ActivityManager.getCurrentUser(); 166 } else if (userHandle != UserHandle.USER_ALL) { 167 throw new InvalidParameterException("Bad user handle for registerContentObserver: " 168 + userHandle); 169 } 170 } 171 172 synchronized (mRootNode) { 173 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, 174 Binder.getCallingUid(), Binder.getCallingPid(), userHandle); 175 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + 176 " with notifyForDescendants " + notifyForDescendants); 177 } 178 } 179 180 public void registerContentObserver(Uri uri, boolean notifyForDescendants, 181 IContentObserver observer) { 182 registerContentObserver(uri, notifyForDescendants, observer, 183 UserHandle.getCallingUserId()); 184 } 185 186 public void unregisterContentObserver(IContentObserver observer) { 187 if (observer == null) { 188 throw new IllegalArgumentException("You must pass a valid observer"); 189 } 190 synchronized (mRootNode) { 191 mRootNode.removeObserverLocked(observer); 192 if (false) Log.v(TAG, "Unregistered observer " + observer); 193 } 194 } 195 196 /** 197 * Notify observers of a particular user's view of the provider. 198 * @param userHandle the user whose view of the provider is to be notified. May be 199 * the calling user without requiring any permission, otherwise the caller needs to 200 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and 201 * USER_CURRENT are properly interpreted; no other pseudousers are allowed. 202 */ 203 @Override 204 public void notifyChange(Uri uri, IContentObserver observer, 205 boolean observerWantsSelfNotifications, boolean syncToNetwork, 206 int userHandle) { 207 if (Log.isLoggable(TAG, Log.VERBOSE)) { 208 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle 209 + " from observer " + observer + ", syncToNetwork " + syncToNetwork); 210 } 211 212 // Notify for any user other than the caller's own requires permission. 213 final int callingUserHandle = UserHandle.getCallingUserId(); 214 if (userHandle != callingUserHandle) { 215 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, 216 "no permission to notify other users"); 217 } 218 219 // We passed the permission check; resolve pseudouser targets as appropriate 220 if (userHandle < 0) { 221 if (userHandle == UserHandle.USER_CURRENT) { 222 userHandle = ActivityManager.getCurrentUser(); 223 } else if (userHandle != UserHandle.USER_ALL) { 224 throw new InvalidParameterException("Bad user handle for notifyChange: " 225 + userHandle); 226 } 227 } 228 229 // This makes it so that future permission checks will be in the context of this 230 // process rather than the caller's process. We will restore this before returning. 231 long identityToken = clearCallingIdentity(); 232 try { 233 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); 234 synchronized (mRootNode) { 235 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, 236 userHandle, calls); 237 } 238 final int numCalls = calls.size(); 239 for (int i=0; i<numCalls; i++) { 240 ObserverCall oc = calls.get(i); 241 try { 242 oc.mObserver.onChange(oc.mSelfChange, uri); 243 if (Log.isLoggable(TAG, Log.VERBOSE)) { 244 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri); 245 } 246 } catch (RemoteException ex) { 247 synchronized (mRootNode) { 248 Log.w(TAG, "Found dead observer, removing"); 249 IBinder binder = oc.mObserver.asBinder(); 250 final ArrayList<ObserverNode.ObserverEntry> list 251 = oc.mNode.mObservers; 252 int numList = list.size(); 253 for (int j=0; j<numList; j++) { 254 ObserverNode.ObserverEntry oe = list.get(j); 255 if (oe.observer.asBinder() == binder) { 256 list.remove(j); 257 j--; 258 numList--; 259 } 260 } 261 } 262 } 263 } 264 if (syncToNetwork) { 265 SyncManager syncManager = getSyncManager(); 266 if (syncManager != null) { 267 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, 268 uri.getAuthority()); 269 } 270 } 271 } finally { 272 restoreCallingIdentity(identityToken); 273 } 274 } 275 276 public void notifyChange(Uri uri, IContentObserver observer, 277 boolean observerWantsSelfNotifications, boolean syncToNetwork) { 278 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, 279 UserHandle.getCallingUserId()); 280 } 281 282 /** 283 * Hide this class since it is not part of api, 284 * but current unittest framework requires it to be public 285 * @hide 286 * 287 */ 288 public static final class ObserverCall { 289 final ObserverNode mNode; 290 final IContentObserver mObserver; 291 final boolean mSelfChange; 292 293 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) { 294 mNode = node; 295 mObserver = observer; 296 mSelfChange = selfChange; 297 } 298 } 299 300 public void requestSync(Account account, String authority, Bundle extras) { 301 ContentResolver.validateSyncExtrasBundle(extras); 302 int userId = UserHandle.getCallingUserId(); 303 304 // This makes it so that future permission checks will be in the context of this 305 // process rather than the caller's process. We will restore this before returning. 306 long identityToken = clearCallingIdentity(); 307 try { 308 SyncManager syncManager = getSyncManager(); 309 if (syncManager != null) { 310 syncManager.scheduleSync(account, userId, authority, extras, 0 /* no delay */, 311 false /* onlyThoseWithUnkownSyncableState */); 312 } 313 } finally { 314 restoreCallingIdentity(identityToken); 315 } 316 } 317 318 /** 319 * Clear all scheduled sync operations that match the uri and cancel the active sync 320 * if they match the authority and account, if they are present. 321 * @param account filter the pending and active syncs to cancel using this account 322 * @param authority filter the pending and active syncs to cancel using this authority 323 */ 324 public void cancelSync(Account account, String authority) { 325 int userId = UserHandle.getCallingUserId(); 326 327 // This makes it so that future permission checks will be in the context of this 328 // process rather than the caller's process. We will restore this before returning. 329 long identityToken = clearCallingIdentity(); 330 try { 331 SyncManager syncManager = getSyncManager(); 332 if (syncManager != null) { 333 syncManager.clearScheduledSyncOperations(account, userId, authority); 334 syncManager.cancelActiveSync(account, userId, authority); 335 } 336 } finally { 337 restoreCallingIdentity(identityToken); 338 } 339 } 340 341 /** 342 * Get information about the SyncAdapters that are known to the system. 343 * @return an array of SyncAdapters that have registered with the system 344 */ 345 public SyncAdapterType[] getSyncAdapterTypes() { 346 // This makes it so that future permission checks will be in the context of this 347 // process rather than the caller's process. We will restore this before returning. 348 final int userId = UserHandle.getCallingUserId(); 349 final long identityToken = clearCallingIdentity(); 350 try { 351 SyncManager syncManager = getSyncManager(); 352 return syncManager.getSyncAdapterTypes(userId); 353 } finally { 354 restoreCallingIdentity(identityToken); 355 } 356 } 357 358 public boolean getSyncAutomatically(Account account, String providerName) { 359 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 360 "no permission to read the sync settings"); 361 int userId = UserHandle.getCallingUserId(); 362 363 long identityToken = clearCallingIdentity(); 364 try { 365 SyncManager syncManager = getSyncManager(); 366 if (syncManager != null) { 367 return syncManager.getSyncStorageEngine().getSyncAutomatically( 368 account, userId, providerName); 369 } 370 } finally { 371 restoreCallingIdentity(identityToken); 372 } 373 return false; 374 } 375 376 public void setSyncAutomatically(Account account, String providerName, boolean sync) { 377 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 378 "no permission to write the sync settings"); 379 int userId = UserHandle.getCallingUserId(); 380 381 long identityToken = clearCallingIdentity(); 382 try { 383 SyncManager syncManager = getSyncManager(); 384 if (syncManager != null) { 385 syncManager.getSyncStorageEngine().setSyncAutomatically( 386 account, userId, providerName, sync); 387 } 388 } finally { 389 restoreCallingIdentity(identityToken); 390 } 391 } 392 393 public void addPeriodicSync(Account account, String authority, Bundle extras, 394 long pollFrequency) { 395 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 396 "no permission to write the sync settings"); 397 int userId = UserHandle.getCallingUserId(); 398 399 long identityToken = clearCallingIdentity(); 400 try { 401 getSyncManager().getSyncStorageEngine().addPeriodicSync( 402 account, userId, authority, extras, pollFrequency); 403 } finally { 404 restoreCallingIdentity(identityToken); 405 } 406 } 407 408 public void removePeriodicSync(Account account, String authority, Bundle extras) { 409 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 410 "no permission to write the sync settings"); 411 int userId = UserHandle.getCallingUserId(); 412 413 long identityToken = clearCallingIdentity(); 414 try { 415 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority, 416 extras); 417 } finally { 418 restoreCallingIdentity(identityToken); 419 } 420 } 421 422 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) { 423 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 424 "no permission to read the sync settings"); 425 int userId = UserHandle.getCallingUserId(); 426 427 long identityToken = clearCallingIdentity(); 428 try { 429 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( 430 account, userId, providerName); 431 } finally { 432 restoreCallingIdentity(identityToken); 433 } 434 } 435 436 public int getIsSyncable(Account account, String providerName) { 437 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 438 "no permission to read the sync settings"); 439 int userId = UserHandle.getCallingUserId(); 440 441 long identityToken = clearCallingIdentity(); 442 try { 443 SyncManager syncManager = getSyncManager(); 444 if (syncManager != null) { 445 return syncManager.getSyncStorageEngine().getIsSyncable( 446 account, userId, providerName); 447 } 448 } finally { 449 restoreCallingIdentity(identityToken); 450 } 451 return -1; 452 } 453 454 public void setIsSyncable(Account account, String providerName, int syncable) { 455 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 456 "no permission to write the sync settings"); 457 int userId = UserHandle.getCallingUserId(); 458 459 long identityToken = clearCallingIdentity(); 460 try { 461 SyncManager syncManager = getSyncManager(); 462 if (syncManager != null) { 463 syncManager.getSyncStorageEngine().setIsSyncable( 464 account, userId, providerName, syncable); 465 } 466 } finally { 467 restoreCallingIdentity(identityToken); 468 } 469 } 470 471 public boolean getMasterSyncAutomatically() { 472 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 473 "no permission to read the sync settings"); 474 int userId = UserHandle.getCallingUserId(); 475 476 long identityToken = clearCallingIdentity(); 477 try { 478 SyncManager syncManager = getSyncManager(); 479 if (syncManager != null) { 480 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); 481 } 482 } finally { 483 restoreCallingIdentity(identityToken); 484 } 485 return false; 486 } 487 488 public void setMasterSyncAutomatically(boolean flag) { 489 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 490 "no permission to write the sync settings"); 491 int userId = UserHandle.getCallingUserId(); 492 493 long identityToken = clearCallingIdentity(); 494 try { 495 SyncManager syncManager = getSyncManager(); 496 if (syncManager != null) { 497 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); 498 } 499 } finally { 500 restoreCallingIdentity(identityToken); 501 } 502 } 503 504 public boolean isSyncActive(Account account, String authority) { 505 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 506 "no permission to read the sync stats"); 507 int userId = UserHandle.getCallingUserId(); 508 509 long identityToken = clearCallingIdentity(); 510 try { 511 SyncManager syncManager = getSyncManager(); 512 if (syncManager != null) { 513 return syncManager.getSyncStorageEngine().isSyncActive( 514 account, userId, authority); 515 } 516 } finally { 517 restoreCallingIdentity(identityToken); 518 } 519 return false; 520 } 521 522 public List<SyncInfo> getCurrentSyncs() { 523 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 524 "no permission to read the sync stats"); 525 int userId = UserHandle.getCallingUserId(); 526 527 long identityToken = clearCallingIdentity(); 528 try { 529 return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId); 530 } finally { 531 restoreCallingIdentity(identityToken); 532 } 533 } 534 535 public SyncStatusInfo getSyncStatus(Account account, String authority) { 536 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 537 "no permission to read the sync stats"); 538 int userId = UserHandle.getCallingUserId(); 539 540 long identityToken = clearCallingIdentity(); 541 try { 542 SyncManager syncManager = getSyncManager(); 543 if (syncManager != null) { 544 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( 545 account, userId, authority); 546 } 547 } finally { 548 restoreCallingIdentity(identityToken); 549 } 550 return null; 551 } 552 553 public boolean isSyncPending(Account account, String authority) { 554 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 555 "no permission to read the sync stats"); 556 int userId = UserHandle.getCallingUserId(); 557 558 long identityToken = clearCallingIdentity(); 559 try { 560 SyncManager syncManager = getSyncManager(); 561 if (syncManager != null) { 562 return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); 563 } 564 } finally { 565 restoreCallingIdentity(identityToken); 566 } 567 return false; 568 } 569 570 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { 571 long identityToken = clearCallingIdentity(); 572 try { 573 SyncManager syncManager = getSyncManager(); 574 if (syncManager != null && callback != null) { 575 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); 576 } 577 } finally { 578 restoreCallingIdentity(identityToken); 579 } 580 } 581 582 public void removeStatusChangeListener(ISyncStatusObserver callback) { 583 long identityToken = clearCallingIdentity(); 584 try { 585 SyncManager syncManager = getSyncManager(); 586 if (syncManager != null && callback != null) { 587 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); 588 } 589 } finally { 590 restoreCallingIdentity(identityToken); 591 } 592 } 593 594 public static ContentService main(Context context, boolean factoryTest) { 595 ContentService service = new ContentService(context, factoryTest); 596 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); 597 return service; 598 } 599 600 /** 601 * Hide this class since it is not part of api, 602 * but current unittest framework requires it to be public 603 * @hide 604 */ 605 public static final class ObserverNode { 606 private class ObserverEntry implements IBinder.DeathRecipient { 607 public final IContentObserver observer; 608 public final int uid; 609 public final int pid; 610 public final boolean notifyForDescendants; 611 private final int userHandle; 612 private final Object observersLock; 613 614 public ObserverEntry(IContentObserver o, boolean n, Object observersLock, 615 int _uid, int _pid, int _userHandle) { 616 this.observersLock = observersLock; 617 observer = o; 618 uid = _uid; 619 pid = _pid; 620 userHandle = _userHandle; 621 notifyForDescendants = n; 622 try { 623 observer.asBinder().linkToDeath(this, 0); 624 } catch (RemoteException e) { 625 binderDied(); 626 } 627 } 628 629 public void binderDied() { 630 synchronized (observersLock) { 631 removeObserverLocked(observer); 632 } 633 } 634 635 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, 636 String name, String prefix, SparseIntArray pidCounts) { 637 pidCounts.put(pid, pidCounts.get(pid)+1); 638 pw.print(prefix); pw.print(name); pw.print(": pid="); 639 pw.print(pid); pw.print(" uid="); 640 pw.print(uid); pw.print(" user="); 641 pw.print(userHandle); pw.print(" target="); 642 pw.println(Integer.toHexString(System.identityHashCode( 643 observer != null ? observer.asBinder() : null))); 644 } 645 } 646 647 public static final int INSERT_TYPE = 0; 648 public static final int UPDATE_TYPE = 1; 649 public static final int DELETE_TYPE = 2; 650 651 private String mName; 652 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); 653 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); 654 655 public ObserverNode(String name) { 656 mName = name; 657 } 658 659 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, 660 String name, String prefix, int[] counts, SparseIntArray pidCounts) { 661 String innerName = null; 662 if (mObservers.size() > 0) { 663 if ("".equals(name)) { 664 innerName = mName; 665 } else { 666 innerName = name + "/" + mName; 667 } 668 for (int i=0; i<mObservers.size(); i++) { 669 counts[1]++; 670 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix, 671 pidCounts); 672 } 673 } 674 if (mChildren.size() > 0) { 675 if (innerName == null) { 676 if ("".equals(name)) { 677 innerName = mName; 678 } else { 679 innerName = name + "/" + mName; 680 } 681 } 682 for (int i=0; i<mChildren.size(); i++) { 683 counts[0]++; 684 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix, 685 counts, pidCounts); 686 } 687 } 688 } 689 690 private String getUriSegment(Uri uri, int index) { 691 if (uri != null) { 692 if (index == 0) { 693 return uri.getAuthority(); 694 } else { 695 return uri.getPathSegments().get(index - 1); 696 } 697 } else { 698 return null; 699 } 700 } 701 702 private int countUriSegments(Uri uri) { 703 if (uri == null) { 704 return 0; 705 } 706 return uri.getPathSegments().size() + 1; 707 } 708 709 // Invariant: userHandle is either a hard user number or is USER_ALL 710 public void addObserverLocked(Uri uri, IContentObserver observer, 711 boolean notifyForDescendants, Object observersLock, 712 int uid, int pid, int userHandle) { 713 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, 714 uid, pid, userHandle); 715 } 716 717 private void addObserverLocked(Uri uri, int index, IContentObserver observer, 718 boolean notifyForDescendants, Object observersLock, 719 int uid, int pid, int userHandle) { 720 // If this is the leaf node add the observer 721 if (index == countUriSegments(uri)) { 722 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, 723 uid, pid, userHandle)); 724 return; 725 } 726 727 // Look to see if the proper child already exists 728 String segment = getUriSegment(uri, index); 729 if (segment == null) { 730 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer"); 731 } 732 int N = mChildren.size(); 733 for (int i = 0; i < N; i++) { 734 ObserverNode node = mChildren.get(i); 735 if (node.mName.equals(segment)) { 736 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, 737 observersLock, uid, pid, userHandle); 738 return; 739 } 740 } 741 742 // No child found, create one 743 ObserverNode node = new ObserverNode(segment); 744 mChildren.add(node); 745 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, 746 observersLock, uid, pid, userHandle); 747 } 748 749 public boolean removeObserverLocked(IContentObserver observer) { 750 int size = mChildren.size(); 751 for (int i = 0; i < size; i++) { 752 boolean empty = mChildren.get(i).removeObserverLocked(observer); 753 if (empty) { 754 mChildren.remove(i); 755 i--; 756 size--; 757 } 758 } 759 760 IBinder observerBinder = observer.asBinder(); 761 size = mObservers.size(); 762 for (int i = 0; i < size; i++) { 763 ObserverEntry entry = mObservers.get(i); 764 if (entry.observer.asBinder() == observerBinder) { 765 mObservers.remove(i); 766 // We no longer need to listen for death notifications. Remove it. 767 observerBinder.unlinkToDeath(entry, 0); 768 break; 769 } 770 } 771 772 if (mChildren.size() == 0 && mObservers.size() == 0) { 773 return true; 774 } 775 return false; 776 } 777 778 private void collectMyObserversLocked(boolean leaf, IContentObserver observer, 779 boolean observerWantsSelfNotifications, int targetUserHandle, 780 ArrayList<ObserverCall> calls) { 781 int N = mObservers.size(); 782 IBinder observerBinder = observer == null ? null : observer.asBinder(); 783 for (int i = 0; i < N; i++) { 784 ObserverEntry entry = mObservers.get(i); 785 786 // Don't notify the observer if it sent the notification and isn't interested 787 // in self notifications 788 boolean selfChange = (entry.observer.asBinder() == observerBinder); 789 if (selfChange && !observerWantsSelfNotifications) { 790 continue; 791 } 792 793 // Does this observer match the target user? 794 if (targetUserHandle == UserHandle.USER_ALL 795 || entry.userHandle == UserHandle.USER_ALL 796 || targetUserHandle == entry.userHandle) { 797 // Make sure the observer is interested in the notification 798 if (leaf || (!leaf && entry.notifyForDescendants)) { 799 calls.add(new ObserverCall(this, entry.observer, selfChange)); 800 } 801 } 802 } 803 } 804 805 /** 806 * targetUserHandle is either a hard user handle or is USER_ALL 807 */ 808 public void collectObserversLocked(Uri uri, int index, IContentObserver observer, 809 boolean observerWantsSelfNotifications, int targetUserHandle, 810 ArrayList<ObserverCall> calls) { 811 String segment = null; 812 int segmentCount = countUriSegments(uri); 813 if (index >= segmentCount) { 814 // This is the leaf node, notify all observers 815 collectMyObserversLocked(true, observer, observerWantsSelfNotifications, 816 targetUserHandle, calls); 817 } else if (index < segmentCount){ 818 segment = getUriSegment(uri, index); 819 // Notify any observers at this level who are interested in descendants 820 collectMyObserversLocked(false, observer, observerWantsSelfNotifications, 821 targetUserHandle, calls); 822 } 823 824 int N = mChildren.size(); 825 for (int i = 0; i < N; i++) { 826 ObserverNode node = mChildren.get(i); 827 if (segment == null || node.mName.equals(segment)) { 828 // We found the child, 829 node.collectObserversLocked(uri, index + 1, 830 observer, observerWantsSelfNotifications, targetUserHandle, calls); 831 if (segment != null) { 832 break; 833 } 834 } 835 } 836 } 837 } 838} 839