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