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