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