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