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.ComponentName; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.IContentService; 26 import android.content.ISyncStatusObserver; 27 import android.content.PeriodicSync; 28 import android.content.pm.PackageManager; 29 import android.content.SyncAdapterType; 30 import android.content.SyncInfo; 31 import android.content.SyncRequest; 32 import android.content.SyncStatusInfo; 33 import android.database.IContentObserver; 34 import android.database.sqlite.SQLiteException; 35 import android.net.Uri; 36 import android.os.Binder; 37 import android.os.Bundle; 38 import android.os.IBinder; 39 import android.os.Parcel; 40 import android.os.RemoteException; 41 import android.os.ServiceManager; 42 import android.os.SystemProperties; 43 import android.os.UserHandle; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.util.Slog; 47 import android.util.SparseIntArray; 48 49 import java.io.FileDescriptor; 50 import java.io.PrintWriter; 51 import java.security.InvalidParameterException; 52 import java.util.ArrayList; 53 import java.util.Collections; 54 import java.util.Comparator; 55 import java.util.List; 56 57 /** 58 * {@hide} 59 */ 60 public 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 enforceCrossUserPermission(userHandle, 176 "no permission to observe other users' provider view"); 177 178 if (userHandle < 0) { 179 if (userHandle == UserHandle.USER_CURRENT) { 180 userHandle = ActivityManager.getCurrentUser(); 181 } else if (userHandle != UserHandle.USER_ALL) { 182 throw new InvalidParameterException("Bad user handle for registerContentObserver: " 183 + userHandle); 184 } 185 } 186 187 synchronized (mRootNode) { 188 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, 189 Binder.getCallingUid(), Binder.getCallingPid(), userHandle); 190 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + 191 " with notifyForDescendants " + notifyForDescendants); 192 } 193 } 194 195 public void registerContentObserver(Uri uri, boolean notifyForDescendants, 196 IContentObserver observer) { 197 registerContentObserver(uri, notifyForDescendants, observer, 198 UserHandle.getCallingUserId()); 199 } 200 201 public void unregisterContentObserver(IContentObserver observer) { 202 if (observer == null) { 203 throw new IllegalArgumentException("You must pass a valid observer"); 204 } 205 synchronized (mRootNode) { 206 mRootNode.removeObserverLocked(observer); 207 if (false) Log.v(TAG, "Unregistered observer " + observer); 208 } 209 } 210 211 /** 212 * Notify observers of a particular user's view of the provider. 213 * @param userHandle the user whose view of the provider is to be notified. May be 214 * the calling user without requiring any permission, otherwise the caller needs to 215 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and 216 * USER_CURRENT are properly interpreted; no other pseudousers are allowed. 217 */ 218 @Override 219 public void notifyChange(Uri uri, IContentObserver observer, 220 boolean observerWantsSelfNotifications, boolean syncToNetwork, 221 int userHandle) { 222 if (Log.isLoggable(TAG, Log.VERBOSE)) { 223 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle 224 + " from observer " + observer + ", syncToNetwork " + syncToNetwork); 225 } 226 227 // Notify for any user other than the caller's own requires permission. 228 final int callingUserHandle = UserHandle.getCallingUserId(); 229 if (userHandle != callingUserHandle) { 230 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, 231 "no permission to notify other users"); 232 } 233 234 // We passed the permission check; resolve pseudouser targets as appropriate 235 if (userHandle < 0) { 236 if (userHandle == UserHandle.USER_CURRENT) { 237 userHandle = ActivityManager.getCurrentUser(); 238 } else if (userHandle != UserHandle.USER_ALL) { 239 throw new InvalidParameterException("Bad user handle for notifyChange: " 240 + userHandle); 241 } 242 } 243 244 final int uid = Binder.getCallingUid(); 245 // This makes it so that future permission checks will be in the context of this 246 // process rather than the caller's process. We will restore this before returning. 247 long identityToken = clearCallingIdentity(); 248 try { 249 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); 250 synchronized (mRootNode) { 251 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, 252 userHandle, calls); 253 } 254 final int numCalls = calls.size(); 255 for (int i=0; i<numCalls; i++) { 256 ObserverCall oc = calls.get(i); 257 try { 258 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle); 259 if (Log.isLoggable(TAG, Log.VERBOSE)) { 260 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri); 261 } 262 } catch (RemoteException ex) { 263 synchronized (mRootNode) { 264 Log.w(TAG, "Found dead observer, removing"); 265 IBinder binder = oc.mObserver.asBinder(); 266 final ArrayList<ObserverNode.ObserverEntry> list 267 = oc.mNode.mObservers; 268 int numList = list.size(); 269 for (int j=0; j<numList; j++) { 270 ObserverNode.ObserverEntry oe = list.get(j); 271 if (oe.observer.asBinder() == binder) { 272 list.remove(j); 273 j--; 274 numList--; 275 } 276 } 277 } 278 } 279 } 280 if (syncToNetwork) { 281 SyncManager syncManager = getSyncManager(); 282 if (syncManager != null) { 283 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid, 284 uri.getAuthority()); 285 } 286 } 287 } finally { 288 restoreCallingIdentity(identityToken); 289 } 290 } 291 292 public void notifyChange(Uri uri, IContentObserver observer, 293 boolean observerWantsSelfNotifications, boolean syncToNetwork) { 294 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, 295 UserHandle.getCallingUserId()); 296 } 297 298 /** 299 * Hide this class since it is not part of api, 300 * but current unittest framework requires it to be public 301 * @hide 302 * 303 */ 304 public static final class ObserverCall { 305 final ObserverNode mNode; 306 final IContentObserver mObserver; 307 final boolean mSelfChange; 308 309 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) { 310 mNode = node; 311 mObserver = observer; 312 mSelfChange = selfChange; 313 } 314 } 315 316 public void requestSync(Account account, String authority, Bundle extras) { 317 ContentResolver.validateSyncExtrasBundle(extras); 318 int userId = UserHandle.getCallingUserId(); 319 int uId = Binder.getCallingUid(); 320 321 // This makes it so that future permission checks will be in the context of this 322 // process rather than the caller's process. We will restore this before returning. 323 long identityToken = clearCallingIdentity(); 324 try { 325 SyncManager syncManager = getSyncManager(); 326 if (syncManager != null) { 327 syncManager.scheduleSync(account, userId, uId, authority, extras, 328 0 /* no delay */, 0 /* no delay */, 329 false /* onlyThoseWithUnkownSyncableState */); 330 } 331 } finally { 332 restoreCallingIdentity(identityToken); 333 } 334 } 335 336 /** 337 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be 338 * either: 339 * periodic OR one-off sync. 340 * and 341 * anonymous OR provider sync. 342 * Depending on the request, we enqueue to suit in the SyncManager. 343 * @param request The request object. Validation of this object is done by its builder. 344 */ 345 public void sync(SyncRequest request) { 346 syncAsUser(request, UserHandle.getCallingUserId()); 347 } 348 349 /** 350 * If the user id supplied is different to the calling user, the caller must hold the 351 * INTERACT_ACROSS_USERS_FULL permission. 352 */ 353 public void syncAsUser(SyncRequest request, int userId) { 354 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId); 355 int callerUid = Binder.getCallingUid(); 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 return; 363 } 364 365 Bundle extras = request.getBundle(); 366 long flextime = request.getSyncFlexTime(); 367 long runAtTime = request.getSyncRunTime(); 368 if (request.isPeriodic()) { 369 mContext.enforceCallingOrSelfPermission( 370 Manifest.permission.WRITE_SYNC_SETTINGS, 371 "no permission to write the sync settings"); 372 SyncStorageEngine.EndPoint info; 373 info = new SyncStorageEngine.EndPoint( 374 request.getAccount(), request.getProvider(), userId); 375 if (runAtTime < 60) { 376 Slog.w(TAG, "Requested poll frequency of " + runAtTime 377 + " seconds being rounded up to 60 seconds."); 378 runAtTime = 60; 379 } 380 // Schedule periodic sync. 381 getSyncManager().getSyncStorageEngine() 382 .updateOrAddPeriodicSync(info, runAtTime, flextime, extras); 383 } else { 384 long beforeRuntimeMillis = (flextime) * 1000; 385 long runtimeMillis = runAtTime * 1000; 386 syncManager.scheduleSync( 387 request.getAccount(), userId, callerUid, request.getProvider(), extras, 388 beforeRuntimeMillis, runtimeMillis, 389 false /* onlyThoseWithUnknownSyncableState */); 390 } 391 } finally { 392 restoreCallingIdentity(identityToken); 393 } 394 } 395 396 /** 397 * Clear all scheduled sync operations that match the uri and cancel the active sync 398 * if they match the authority and account, if they are present. 399 * 400 * @param account filter the pending and active syncs to cancel using this account, or null. 401 * @param authority filter the pending and active syncs to cancel using this authority, or 402 * null. 403 * @param cname cancel syncs running on this service, or null for provider/account. 404 */ 405 @Override 406 public void cancelSync(Account account, String authority, ComponentName cname) { 407 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId()); 408 } 409 410 /** 411 * Clear all scheduled sync operations that match the uri and cancel the active sync 412 * if they match the authority and account, if they are present. 413 * 414 * <p> If the user id supplied is different to the calling user, the caller must hold the 415 * INTERACT_ACROSS_USERS_FULL permission. 416 * 417 * @param account filter the pending and active syncs to cancel using this account, or null. 418 * @param authority filter the pending and active syncs to cancel using this authority, or 419 * null. 420 * @param userId the user id for which to cancel sync operations. 421 * @param cname cancel syncs running on this service, or null for provider/account. 422 */ 423 @Override 424 public void cancelSyncAsUser(Account account, String authority, ComponentName cname, 425 int userId) { 426 if (authority != null && authority.length() == 0) { 427 throw new IllegalArgumentException("Authority must be non-empty"); 428 } 429 enforceCrossUserPermission(userId, 430 "no permission to modify the sync settings for user " + userId); 431 // This makes it so that future permission checks will be in the context of this 432 // process rather than the caller's process. We will restore this before returning. 433 long identityToken = clearCallingIdentity(); 434 try { 435 SyncManager syncManager = getSyncManager(); 436 if (syncManager != null) { 437 SyncStorageEngine.EndPoint info; 438 if (cname == null) { 439 info = new SyncStorageEngine.EndPoint(account, authority, userId); 440 } else { 441 info = new SyncStorageEngine.EndPoint(cname, userId); 442 } 443 syncManager.clearScheduledSyncOperations(info); 444 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */); 445 } 446 } finally { 447 restoreCallingIdentity(identityToken); 448 } 449 } 450 451 public void cancelRequest(SyncRequest request) { 452 SyncManager syncManager = getSyncManager(); 453 if (syncManager == null) return; 454 int userId = UserHandle.getCallingUserId(); 455 456 long identityToken = clearCallingIdentity(); 457 try { 458 SyncStorageEngine.EndPoint info; 459 Bundle extras = new Bundle(request.getBundle()); 460 Account account = request.getAccount(); 461 String provider = request.getProvider(); 462 info = new SyncStorageEngine.EndPoint(account, provider, userId); 463 if (request.isPeriodic()) { 464 // Remove periodic sync. 465 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 466 "no permission to write the sync settings"); 467 getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras); 468 } 469 // Cancel active syncs and clear pending syncs from the queue. 470 syncManager.cancelScheduledSyncOperation(info, extras); 471 syncManager.cancelActiveSync(info, extras); 472 } finally { 473 restoreCallingIdentity(identityToken); 474 } 475 } 476 477 /** 478 * Get information about the SyncAdapters that are known to the system. 479 * @return an array of SyncAdapters that have registered with the system 480 */ 481 @Override 482 public SyncAdapterType[] getSyncAdapterTypes() { 483 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId()); 484 } 485 486 /** 487 * Get information about the SyncAdapters that are known to the system for a particular user. 488 * 489 * <p> If the user id supplied is different to the calling user, the caller must hold the 490 * INTERACT_ACROSS_USERS_FULL permission. 491 * 492 * @return an array of SyncAdapters that have registered with the system 493 */ 494 @Override 495 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) { 496 enforceCrossUserPermission(userId, 497 "no permission to read sync settings for user " + userId); 498 // This makes it so that future permission checks will be in the context of this 499 // process rather than the caller's process. We will restore this before returning. 500 final long identityToken = clearCallingIdentity(); 501 try { 502 SyncManager syncManager = getSyncManager(); 503 return syncManager.getSyncAdapterTypes(userId); 504 } finally { 505 restoreCallingIdentity(identityToken); 506 } 507 } 508 509 @Override 510 public boolean getSyncAutomatically(Account account, String providerName) { 511 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId()); 512 } 513 514 /** 515 * If the user id supplied is different to the calling user, the caller must hold the 516 * INTERACT_ACROSS_USERS_FULL permission. 517 */ 518 @Override 519 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) { 520 enforceCrossUserPermission(userId, 521 "no permission to read the sync settings for user " + userId); 522 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 523 "no permission to read the sync settings"); 524 525 long identityToken = clearCallingIdentity(); 526 try { 527 SyncManager syncManager = getSyncManager(); 528 if (syncManager != null) { 529 return syncManager.getSyncStorageEngine() 530 .getSyncAutomatically(account, userId, providerName); 531 } 532 } finally { 533 restoreCallingIdentity(identityToken); 534 } 535 return false; 536 } 537 538 @Override 539 public void setSyncAutomatically(Account account, String providerName, boolean sync) { 540 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId()); 541 } 542 543 @Override 544 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync, 545 int userId) { 546 if (TextUtils.isEmpty(providerName)) { 547 throw new IllegalArgumentException("Authority must be non-empty"); 548 } 549 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 550 "no permission to write the sync settings"); 551 enforceCrossUserPermission(userId, 552 "no permission to modify the sync settings for user " + userId); 553 554 long identityToken = clearCallingIdentity(); 555 try { 556 SyncManager syncManager = getSyncManager(); 557 if (syncManager != null) { 558 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId, 559 providerName, sync); 560 } 561 } finally { 562 restoreCallingIdentity(identityToken); 563 } 564 } 565 566 /** Old API. Schedule periodic sync with default flex time. */ 567 @Override 568 public void addPeriodicSync(Account account, String authority, Bundle extras, 569 long pollFrequency) { 570 if (account == null) { 571 throw new IllegalArgumentException("Account must not be null"); 572 } 573 if (TextUtils.isEmpty(authority)) { 574 throw new IllegalArgumentException("Authority must not be empty."); 575 } 576 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 577 "no permission to write the sync settings"); 578 579 int userId = UserHandle.getCallingUserId(); 580 if (pollFrequency < 60) { 581 Slog.w(TAG, "Requested poll frequency of " + pollFrequency 582 + " seconds being rounded up to 60 seconds."); 583 pollFrequency = 60; 584 } 585 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency); 586 587 long identityToken = clearCallingIdentity(); 588 try { 589 SyncStorageEngine.EndPoint info = 590 new SyncStorageEngine.EndPoint(account, authority, userId); 591 getSyncManager().getSyncStorageEngine() 592 .updateOrAddPeriodicSync(info, 593 pollFrequency, 594 defaultFlex, 595 extras); 596 } finally { 597 restoreCallingIdentity(identityToken); 598 } 599 } 600 601 public void removePeriodicSync(Account account, String authority, Bundle extras) { 602 if (account == null) { 603 throw new IllegalArgumentException("Account must not be null"); 604 } 605 if (TextUtils.isEmpty(authority)) { 606 throw new IllegalArgumentException("Authority must not be empty"); 607 } 608 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 609 "no permission to write the sync settings"); 610 611 int userId = UserHandle.getCallingUserId(); 612 long identityToken = clearCallingIdentity(); 613 try { 614 getSyncManager().getSyncStorageEngine() 615 .removePeriodicSync( 616 new SyncStorageEngine.EndPoint(account, authority, userId), 617 extras); 618 } finally { 619 restoreCallingIdentity(identityToken); 620 } 621 } 622 623 624 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName, 625 ComponentName cname) { 626 if (account == null) { 627 throw new IllegalArgumentException("Account must not be null"); 628 } 629 if (TextUtils.isEmpty(providerName)) { 630 throw new IllegalArgumentException("Authority must not be empty"); 631 } 632 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 633 "no permission to read the sync settings"); 634 635 int userId = UserHandle.getCallingUserId(); 636 long identityToken = clearCallingIdentity(); 637 try { 638 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( 639 new SyncStorageEngine.EndPoint(account, providerName, userId)); 640 } finally { 641 restoreCallingIdentity(identityToken); 642 } 643 } 644 645 public int getIsSyncable(Account account, String providerName) { 646 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId()); 647 } 648 649 /** 650 * If the user id supplied is different to the calling user, the caller must hold the 651 * INTERACT_ACROSS_USERS_FULL permission. 652 */ 653 public int getIsSyncableAsUser(Account account, String providerName, int userId) { 654 enforceCrossUserPermission(userId, 655 "no permission to read the sync settings for user " + userId); 656 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 657 "no permission to read the sync settings"); 658 659 long identityToken = clearCallingIdentity(); 660 try { 661 SyncManager syncManager = getSyncManager(); 662 if (syncManager != null) { 663 return syncManager.getIsSyncable( 664 account, userId, providerName); 665 } 666 } finally { 667 restoreCallingIdentity(identityToken); 668 } 669 return -1; 670 } 671 672 public void setIsSyncable(Account account, String providerName, int syncable) { 673 if (TextUtils.isEmpty(providerName)) { 674 throw new IllegalArgumentException("Authority must not be empty"); 675 } 676 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 677 "no permission to write the sync settings"); 678 679 int userId = UserHandle.getCallingUserId(); 680 long identityToken = clearCallingIdentity(); 681 try { 682 SyncManager syncManager = getSyncManager(); 683 if (syncManager != null) { 684 syncManager.getSyncStorageEngine().setIsSyncable( 685 account, userId, providerName, syncable); 686 } 687 } finally { 688 restoreCallingIdentity(identityToken); 689 } 690 } 691 692 @Override 693 public boolean getMasterSyncAutomatically() { 694 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId()); 695 } 696 697 /** 698 * If the user id supplied is different to the calling user, the caller must hold the 699 * INTERACT_ACROSS_USERS_FULL permission. 700 */ 701 @Override 702 public boolean getMasterSyncAutomaticallyAsUser(int userId) { 703 enforceCrossUserPermission(userId, 704 "no permission to read the sync settings for user " + userId); 705 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 706 "no permission to read the sync settings"); 707 708 long identityToken = clearCallingIdentity(); 709 try { 710 SyncManager syncManager = getSyncManager(); 711 if (syncManager != null) { 712 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); 713 } 714 } finally { 715 restoreCallingIdentity(identityToken); 716 } 717 return false; 718 } 719 720 @Override 721 public void setMasterSyncAutomatically(boolean flag) { 722 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId()); 723 } 724 725 @Override 726 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) { 727 enforceCrossUserPermission(userId, 728 "no permission to set the sync status for user " + userId); 729 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 730 "no permission to write the sync settings"); 731 732 long identityToken = clearCallingIdentity(); 733 try { 734 SyncManager syncManager = getSyncManager(); 735 if (syncManager != null) { 736 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); 737 } 738 } finally { 739 restoreCallingIdentity(identityToken); 740 } 741 } 742 743 public boolean isSyncActive(Account account, String authority, ComponentName cname) { 744 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 745 "no permission to read the sync stats"); 746 int userId = UserHandle.getCallingUserId(); 747 int callingUid = Binder.getCallingUid(); 748 long identityToken = clearCallingIdentity(); 749 try { 750 SyncManager syncManager = getSyncManager(); 751 if (syncManager == null) { 752 return false; 753 } 754 return syncManager.getSyncStorageEngine().isSyncActive( 755 new SyncStorageEngine.EndPoint(account, authority, userId)); 756 } finally { 757 restoreCallingIdentity(identityToken); 758 } 759 } 760 761 public List<SyncInfo> getCurrentSyncs() { 762 return getCurrentSyncsAsUser(UserHandle.getCallingUserId()); 763 } 764 765 /** 766 * If the user id supplied is different to the calling user, the caller must hold the 767 * INTERACT_ACROSS_USERS_FULL permission. 768 */ 769 public List<SyncInfo> getCurrentSyncsAsUser(int userId) { 770 enforceCrossUserPermission(userId, 771 "no permission to read the sync settings for user " + userId); 772 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 773 "no permission to read the sync stats"); 774 775 long identityToken = clearCallingIdentity(); 776 try { 777 return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId); 778 } finally { 779 restoreCallingIdentity(identityToken); 780 } 781 } 782 783 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) { 784 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId()); 785 } 786 787 /** 788 * If the user id supplied is different to the calling user, the caller must hold the 789 * INTERACT_ACROSS_USERS_FULL permission. 790 */ 791 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority, 792 ComponentName cname, int userId) { 793 if (TextUtils.isEmpty(authority)) { 794 throw new IllegalArgumentException("Authority must not be empty"); 795 } 796 797 enforceCrossUserPermission(userId, 798 "no permission to read the sync stats for user " + userId); 799 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 800 "no permission to read the sync stats"); 801 802 int callerUid = Binder.getCallingUid(); 803 long identityToken = clearCallingIdentity(); 804 try { 805 SyncManager syncManager = getSyncManager(); 806 if (syncManager == null) { 807 return null; 808 } 809 SyncStorageEngine.EndPoint info; 810 if (!(account == null || authority == null)) { 811 info = new SyncStorageEngine.EndPoint(account, authority, userId); 812 } else { 813 throw new IllegalArgumentException("Must call sync status with valid authority"); 814 } 815 return syncManager.getSyncStorageEngine().getStatusByAuthority(info); 816 } finally { 817 restoreCallingIdentity(identityToken); 818 } 819 } 820 821 public boolean isSyncPending(Account account, String authority, ComponentName cname) { 822 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId()); 823 } 824 825 @Override 826 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname, 827 int userId) { 828 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 829 "no permission to read the sync stats"); 830 enforceCrossUserPermission(userId, 831 "no permission to retrieve the sync settings for user " + userId); 832 int callerUid = Binder.getCallingUid(); 833 long identityToken = clearCallingIdentity(); 834 SyncManager syncManager = getSyncManager(); 835 if (syncManager == null) return false; 836 837 try { 838 SyncStorageEngine.EndPoint info; 839 if (!(account == null || authority == null)) { 840 info = new SyncStorageEngine.EndPoint(account, authority, userId); 841 } else { 842 throw new IllegalArgumentException("Invalid authority specified"); 843 } 844 return syncManager.getSyncStorageEngine().isSyncPending(info); 845 } finally { 846 restoreCallingIdentity(identityToken); 847 } 848 } 849 850 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { 851 long identityToken = clearCallingIdentity(); 852 try { 853 SyncManager syncManager = getSyncManager(); 854 if (syncManager != null && callback != null) { 855 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); 856 } 857 } finally { 858 restoreCallingIdentity(identityToken); 859 } 860 } 861 862 public void removeStatusChangeListener(ISyncStatusObserver callback) { 863 long identityToken = clearCallingIdentity(); 864 try { 865 SyncManager syncManager = getSyncManager(); 866 if (syncManager != null && callback != null) { 867 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); 868 } 869 } finally { 870 restoreCallingIdentity(identityToken); 871 } 872 } 873 874 public static ContentService main(Context context, boolean factoryTest) { 875 ContentService service = new ContentService(context, factoryTest); 876 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); 877 return service; 878 } 879 880 /** 881 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL 882 * permission, if the userHandle is not for the caller. 883 * 884 * @param userHandle the user handle of the user we want to act on behalf of. 885 * @param message the message to log on security exception. 886 */ 887 private void enforceCrossUserPermission(int userHandle, String message) { 888 final int callingUser = UserHandle.getCallingUserId(); 889 if (callingUser != userHandle) { 890 mContext.enforceCallingOrSelfPermission( 891 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); 892 } 893 } 894 895 /** 896 * Hide this class since it is not part of api, 897 * but current unittest framework requires it to be public 898 * @hide 899 */ 900 public static final class ObserverNode { 901 private class ObserverEntry implements IBinder.DeathRecipient { 902 public final IContentObserver observer; 903 public final int uid; 904 public final int pid; 905 public final boolean notifyForDescendants; 906 private final int userHandle; 907 private final Object observersLock; 908 909 public ObserverEntry(IContentObserver o, boolean n, Object observersLock, 910 int _uid, int _pid, int _userHandle) { 911 this.observersLock = observersLock; 912 observer = o; 913 uid = _uid; 914 pid = _pid; 915 userHandle = _userHandle; 916 notifyForDescendants = n; 917 try { 918 observer.asBinder().linkToDeath(this, 0); 919 } catch (RemoteException e) { 920 binderDied(); 921 } 922 } 923 924 public void binderDied() { 925 synchronized (observersLock) { 926 removeObserverLocked(observer); 927 } 928 } 929 930 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, 931 String name, String prefix, SparseIntArray pidCounts) { 932 pidCounts.put(pid, pidCounts.get(pid)+1); 933 pw.print(prefix); pw.print(name); pw.print(": pid="); 934 pw.print(pid); pw.print(" uid="); 935 pw.print(uid); pw.print(" user="); 936 pw.print(userHandle); pw.print(" target="); 937 pw.println(Integer.toHexString(System.identityHashCode( 938 observer != null ? observer.asBinder() : null))); 939 } 940 } 941 942 public static final int INSERT_TYPE = 0; 943 public static final int UPDATE_TYPE = 1; 944 public static final int DELETE_TYPE = 2; 945 946 private String mName; 947 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); 948 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); 949 950 public ObserverNode(String name) { 951 mName = name; 952 } 953 954 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, 955 String name, String prefix, int[] counts, SparseIntArray pidCounts) { 956 String innerName = null; 957 if (mObservers.size() > 0) { 958 if ("".equals(name)) { 959 innerName = mName; 960 } else { 961 innerName = name + "/" + mName; 962 } 963 for (int i=0; i<mObservers.size(); i++) { 964 counts[1]++; 965 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix, 966 pidCounts); 967 } 968 } 969 if (mChildren.size() > 0) { 970 if (innerName == null) { 971 if ("".equals(name)) { 972 innerName = mName; 973 } else { 974 innerName = name + "/" + mName; 975 } 976 } 977 for (int i=0; i<mChildren.size(); i++) { 978 counts[0]++; 979 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix, 980 counts, pidCounts); 981 } 982 } 983 } 984 985 private String getUriSegment(Uri uri, int index) { 986 if (uri != null) { 987 if (index == 0) { 988 return uri.getAuthority(); 989 } else { 990 return uri.getPathSegments().get(index - 1); 991 } 992 } else { 993 return null; 994 } 995 } 996 997 private int countUriSegments(Uri uri) { 998 if (uri == null) { 999 return 0; 1000 } 1001 return uri.getPathSegments().size() + 1; 1002 } 1003 1004 // Invariant: userHandle is either a hard user number or is USER_ALL 1005 public void addObserverLocked(Uri uri, IContentObserver observer, 1006 boolean notifyForDescendants, Object observersLock, 1007 int uid, int pid, int userHandle) { 1008 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, 1009 uid, pid, userHandle); 1010 } 1011 1012 private void addObserverLocked(Uri uri, int index, IContentObserver observer, 1013 boolean notifyForDescendants, Object observersLock, 1014 int uid, int pid, int userHandle) { 1015 // If this is the leaf node add the observer 1016 if (index == countUriSegments(uri)) { 1017 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, 1018 uid, pid, userHandle)); 1019 return; 1020 } 1021 1022 // Look to see if the proper child already exists 1023 String segment = getUriSegment(uri, index); 1024 if (segment == null) { 1025 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer"); 1026 } 1027 int N = mChildren.size(); 1028 for (int i = 0; i < N; i++) { 1029 ObserverNode node = mChildren.get(i); 1030 if (node.mName.equals(segment)) { 1031 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, 1032 observersLock, uid, pid, userHandle); 1033 return; 1034 } 1035 } 1036 1037 // No child found, create one 1038 ObserverNode node = new ObserverNode(segment); 1039 mChildren.add(node); 1040 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, 1041 observersLock, uid, pid, userHandle); 1042 } 1043 1044 public boolean removeObserverLocked(IContentObserver observer) { 1045 int size = mChildren.size(); 1046 for (int i = 0; i < size; i++) { 1047 boolean empty = mChildren.get(i).removeObserverLocked(observer); 1048 if (empty) { 1049 mChildren.remove(i); 1050 i--; 1051 size--; 1052 } 1053 } 1054 1055 IBinder observerBinder = observer.asBinder(); 1056 size = mObservers.size(); 1057 for (int i = 0; i < size; i++) { 1058 ObserverEntry entry = mObservers.get(i); 1059 if (entry.observer.asBinder() == observerBinder) { 1060 mObservers.remove(i); 1061 // We no longer need to listen for death notifications. Remove it. 1062 observerBinder.unlinkToDeath(entry, 0); 1063 break; 1064 } 1065 } 1066 1067 if (mChildren.size() == 0 && mObservers.size() == 0) { 1068 return true; 1069 } 1070 return false; 1071 } 1072 1073 private void collectMyObserversLocked(boolean leaf, IContentObserver observer, 1074 boolean observerWantsSelfNotifications, int targetUserHandle, 1075 ArrayList<ObserverCall> calls) { 1076 int N = mObservers.size(); 1077 IBinder observerBinder = observer == null ? null : observer.asBinder(); 1078 for (int i = 0; i < N; i++) { 1079 ObserverEntry entry = mObservers.get(i); 1080 1081 // Don't notify the observer if it sent the notification and isn't interested 1082 // in self notifications 1083 boolean selfChange = (entry.observer.asBinder() == observerBinder); 1084 if (selfChange && !observerWantsSelfNotifications) { 1085 continue; 1086 } 1087 1088 // Does this observer match the target user? 1089 if (targetUserHandle == UserHandle.USER_ALL 1090 || entry.userHandle == UserHandle.USER_ALL 1091 || targetUserHandle == entry.userHandle) { 1092 // Make sure the observer is interested in the notification 1093 if (leaf || (!leaf && entry.notifyForDescendants)) { 1094 calls.add(new ObserverCall(this, entry.observer, selfChange)); 1095 } 1096 } 1097 } 1098 } 1099 1100 /** 1101 * targetUserHandle is either a hard user handle or is USER_ALL 1102 */ 1103 public void collectObserversLocked(Uri uri, int index, IContentObserver observer, 1104 boolean observerWantsSelfNotifications, int targetUserHandle, 1105 ArrayList<ObserverCall> calls) { 1106 String segment = null; 1107 int segmentCount = countUriSegments(uri); 1108 if (index >= segmentCount) { 1109 // This is the leaf node, notify all observers 1110 collectMyObserversLocked(true, observer, observerWantsSelfNotifications, 1111 targetUserHandle, calls); 1112 } else if (index < segmentCount){ 1113 segment = getUriSegment(uri, index); 1114 // Notify any observers at this level who are interested in descendants 1115 collectMyObserversLocked(false, observer, observerWantsSelfNotifications, 1116 targetUserHandle, calls); 1117 } 1118 1119 int N = mChildren.size(); 1120 for (int i = 0; i < N; i++) { 1121 ObserverNode node = mChildren.get(i); 1122 if (segment == null || node.mName.equals(segment)) { 1123 // We found the child, 1124 node.collectObserversLocked(uri, index + 1, 1125 observer, observerWantsSelfNotifications, targetUserHandle, calls); 1126 if (segment != null) { 1127 break; 1128 } 1129 } 1130 } 1131 } 1132 } 1133 } 1134