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