ContentService.java revision 0363c3eb089afd4474bfd6ae6ee8a500d6e97614
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 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); 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 if (TextUtils.isEmpty(providerName)) { 541 throw new IllegalArgumentException("Authority must be non-empty"); 542 } 543 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 544 "no permission to write the sync settings"); 545 546 int userId = UserHandle.getCallingUserId(); 547 long identityToken = clearCallingIdentity(); 548 try { 549 SyncManager syncManager = getSyncManager(); 550 if (syncManager != null) { 551 syncManager.getSyncStorageEngine() 552 .setSyncAutomatically(account, userId, providerName, sync); 553 } 554 } finally { 555 restoreCallingIdentity(identityToken); 556 } 557 } 558 559 /** Old API. Schedule periodic sync with default flex time. */ 560 @Override 561 public void addPeriodicSync(Account account, String authority, Bundle extras, 562 long pollFrequency) { 563 if (account == null) { 564 throw new IllegalArgumentException("Account must not be null"); 565 } 566 if (TextUtils.isEmpty(authority)) { 567 throw new IllegalArgumentException("Authority must not be empty."); 568 } 569 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 570 "no permission to write the sync settings"); 571 572 int userId = UserHandle.getCallingUserId(); 573 if (pollFrequency < 60) { 574 Slog.w(TAG, "Requested poll frequency of " + pollFrequency 575 + " seconds being rounded up to 60 seconds."); 576 pollFrequency = 60; 577 } 578 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency); 579 580 long identityToken = clearCallingIdentity(); 581 try { 582 SyncStorageEngine.EndPoint info = 583 new SyncStorageEngine.EndPoint(account, authority, userId); 584 getSyncManager().getSyncStorageEngine() 585 .updateOrAddPeriodicSync(info, 586 pollFrequency, 587 defaultFlex, 588 extras); 589 } finally { 590 restoreCallingIdentity(identityToken); 591 } 592 } 593 594 public void removePeriodicSync(Account account, String authority, Bundle extras) { 595 if (account == null) { 596 throw new IllegalArgumentException("Account must not be null"); 597 } 598 if (TextUtils.isEmpty(authority)) { 599 throw new IllegalArgumentException("Authority must not be empty"); 600 } 601 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 602 "no permission to write the sync settings"); 603 604 int userId = UserHandle.getCallingUserId(); 605 long identityToken = clearCallingIdentity(); 606 try { 607 getSyncManager().getSyncStorageEngine() 608 .removePeriodicSync( 609 new SyncStorageEngine.EndPoint(account, authority, userId), 610 extras); 611 } finally { 612 restoreCallingIdentity(identityToken); 613 } 614 } 615 616 617 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName, 618 ComponentName cname) { 619 if (account == null) { 620 throw new IllegalArgumentException("Account must not be null"); 621 } 622 if (TextUtils.isEmpty(providerName)) { 623 throw new IllegalArgumentException("Authority must not be empty"); 624 } 625 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 626 "no permission to read the sync settings"); 627 628 int userId = UserHandle.getCallingUserId(); 629 long identityToken = clearCallingIdentity(); 630 try { 631 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( 632 new SyncStorageEngine.EndPoint(account, providerName, userId)); 633 } finally { 634 restoreCallingIdentity(identityToken); 635 } 636 } 637 638 public int getIsSyncable(Account account, String providerName) { 639 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId()); 640 } 641 642 /** 643 * If the user id supplied is different to the calling user, the caller must hold the 644 * INTERACT_ACROSS_USERS_FULL permission. 645 */ 646 public int getIsSyncableAsUser(Account account, String providerName, int userId) { 647 enforceCrossUserPermission(userId, 648 "no permission to read the sync settings for user " + userId); 649 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 650 "no permission to read the sync settings"); 651 652 long identityToken = clearCallingIdentity(); 653 try { 654 SyncManager syncManager = getSyncManager(); 655 if (syncManager != null) { 656 return syncManager.getIsSyncable( 657 account, userId, providerName); 658 } 659 } finally { 660 restoreCallingIdentity(identityToken); 661 } 662 return -1; 663 } 664 665 public void setIsSyncable(Account account, String providerName, int syncable) { 666 if (TextUtils.isEmpty(providerName)) { 667 throw new IllegalArgumentException("Authority must not be empty"); 668 } 669 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 670 "no permission to write the sync settings"); 671 672 int userId = UserHandle.getCallingUserId(); 673 long identityToken = clearCallingIdentity(); 674 try { 675 SyncManager syncManager = getSyncManager(); 676 if (syncManager != null) { 677 syncManager.getSyncStorageEngine().setIsSyncable( 678 account, userId, providerName, syncable); 679 } 680 } finally { 681 restoreCallingIdentity(identityToken); 682 } 683 } 684 685 @Override 686 public boolean getMasterSyncAutomatically() { 687 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId()); 688 } 689 690 /** 691 * If the user id supplied is different to the calling user, the caller must hold the 692 * INTERACT_ACROSS_USERS_FULL permission. 693 */ 694 @Override 695 public boolean getMasterSyncAutomaticallyAsUser(int userId) { 696 enforceCrossUserPermission(userId, 697 "no permission to read the sync settings for user " + userId); 698 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 699 "no permission to read the sync settings"); 700 701 long identityToken = clearCallingIdentity(); 702 try { 703 SyncManager syncManager = getSyncManager(); 704 if (syncManager != null) { 705 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); 706 } 707 } finally { 708 restoreCallingIdentity(identityToken); 709 } 710 return false; 711 } 712 713 @Override 714 public void setMasterSyncAutomatically(boolean flag) { 715 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 716 "no permission to write the sync settings"); 717 718 int userId = UserHandle.getCallingUserId(); 719 long identityToken = clearCallingIdentity(); 720 try { 721 SyncManager syncManager = getSyncManager(); 722 if (syncManager != null) { 723 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); 724 } 725 } finally { 726 restoreCallingIdentity(identityToken); 727 } 728 } 729 730 public boolean isSyncActive(Account account, String authority, ComponentName cname) { 731 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 732 "no permission to read the sync stats"); 733 int userId = UserHandle.getCallingUserId(); 734 int callingUid = Binder.getCallingUid(); 735 long identityToken = clearCallingIdentity(); 736 try { 737 SyncManager syncManager = getSyncManager(); 738 if (syncManager == null) { 739 return false; 740 } 741 return syncManager.getSyncStorageEngine().isSyncActive( 742 new SyncStorageEngine.EndPoint(account, authority, userId)); 743 } finally { 744 restoreCallingIdentity(identityToken); 745 } 746 } 747 748 public List<SyncInfo> getCurrentSyncs() { 749 return getCurrentSyncsAsUser(UserHandle.getCallingUserId()); 750 } 751 752 /** 753 * If the user id supplied is different to the calling user, the caller must hold the 754 * INTERACT_ACROSS_USERS_FULL permission. 755 */ 756 public List<SyncInfo> getCurrentSyncsAsUser(int userId) { 757 enforceCrossUserPermission(userId, 758 "no permission to read the sync settings for user " + userId); 759 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 760 "no permission to read the sync stats"); 761 762 long identityToken = clearCallingIdentity(); 763 try { 764 return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId); 765 } finally { 766 restoreCallingIdentity(identityToken); 767 } 768 } 769 770 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) { 771 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId()); 772 } 773 774 /** 775 * If the user id supplied is different to the calling user, the caller must hold the 776 * INTERACT_ACROSS_USERS_FULL permission. 777 */ 778 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority, 779 ComponentName cname, int userId) { 780 if (TextUtils.isEmpty(authority)) { 781 throw new IllegalArgumentException("Authority must not be empty"); 782 } 783 784 enforceCrossUserPermission(userId, 785 "no permission to read the sync stats for user " + userId); 786 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 787 "no permission to read the sync stats"); 788 789 int callerUid = Binder.getCallingUid(); 790 long identityToken = clearCallingIdentity(); 791 try { 792 SyncManager syncManager = getSyncManager(); 793 if (syncManager == null) { 794 return null; 795 } 796 SyncStorageEngine.EndPoint info; 797 if (!(account == null || authority == null)) { 798 info = new SyncStorageEngine.EndPoint(account, authority, userId); 799 } else { 800 throw new IllegalArgumentException("Must call sync status with valid authority"); 801 } 802 return syncManager.getSyncStorageEngine().getStatusByAuthority(info); 803 } finally { 804 restoreCallingIdentity(identityToken); 805 } 806 } 807 808 public boolean isSyncPending(Account account, String authority, ComponentName cname) { 809 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 810 "no permission to read the sync stats"); 811 812 int callerUid = Binder.getCallingUid(); 813 int userId = UserHandle.getCallingUserId(); 814 long identityToken = clearCallingIdentity(); 815 SyncManager syncManager = getSyncManager(); 816 if (syncManager == null) return false; 817 818 try { 819 SyncStorageEngine.EndPoint info; 820 if (!(account == null || authority == null)) { 821 info = new SyncStorageEngine.EndPoint(account, authority, userId); 822 } else { 823 throw new IllegalArgumentException("Invalid authority specified"); 824 } 825 return syncManager.getSyncStorageEngine().isSyncPending(info); 826 } finally { 827 restoreCallingIdentity(identityToken); 828 } 829 } 830 831 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { 832 long identityToken = clearCallingIdentity(); 833 try { 834 SyncManager syncManager = getSyncManager(); 835 if (syncManager != null && callback != null) { 836 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); 837 } 838 } finally { 839 restoreCallingIdentity(identityToken); 840 } 841 } 842 843 public void removeStatusChangeListener(ISyncStatusObserver callback) { 844 long identityToken = clearCallingIdentity(); 845 try { 846 SyncManager syncManager = getSyncManager(); 847 if (syncManager != null && callback != null) { 848 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); 849 } 850 } finally { 851 restoreCallingIdentity(identityToken); 852 } 853 } 854 855 public static ContentService main(Context context, boolean factoryTest) { 856 ContentService service = new ContentService(context, factoryTest); 857 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); 858 return service; 859 } 860 861 /** 862 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL 863 * permission, if the userHandle is not for the caller. 864 * 865 * @param userHandle the user handle of the user we want to act on behalf of. 866 * @param message the message to log on security exception. 867 */ 868 private void enforceCrossUserPermission(int userHandle, String message) { 869 final int callingUser = UserHandle.getCallingUserId(); 870 if (callingUser != userHandle) { 871 mContext.enforceCallingOrSelfPermission( 872 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); 873 } 874 } 875 876 /** 877 * Hide this class since it is not part of api, 878 * but current unittest framework requires it to be public 879 * @hide 880 */ 881 public static final class ObserverNode { 882 private class ObserverEntry implements IBinder.DeathRecipient { 883 public final IContentObserver observer; 884 public final int uid; 885 public final int pid; 886 public final boolean notifyForDescendants; 887 private final int userHandle; 888 private final Object observersLock; 889 890 public ObserverEntry(IContentObserver o, boolean n, Object observersLock, 891 int _uid, int _pid, int _userHandle) { 892 this.observersLock = observersLock; 893 observer = o; 894 uid = _uid; 895 pid = _pid; 896 userHandle = _userHandle; 897 notifyForDescendants = n; 898 try { 899 observer.asBinder().linkToDeath(this, 0); 900 } catch (RemoteException e) { 901 binderDied(); 902 } 903 } 904 905 public void binderDied() { 906 synchronized (observersLock) { 907 removeObserverLocked(observer); 908 } 909 } 910 911 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, 912 String name, String prefix, SparseIntArray pidCounts) { 913 pidCounts.put(pid, pidCounts.get(pid)+1); 914 pw.print(prefix); pw.print(name); pw.print(": pid="); 915 pw.print(pid); pw.print(" uid="); 916 pw.print(uid); pw.print(" user="); 917 pw.print(userHandle); pw.print(" target="); 918 pw.println(Integer.toHexString(System.identityHashCode( 919 observer != null ? observer.asBinder() : null))); 920 } 921 } 922 923 public static final int INSERT_TYPE = 0; 924 public static final int UPDATE_TYPE = 1; 925 public static final int DELETE_TYPE = 2; 926 927 private String mName; 928 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); 929 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); 930 931 public ObserverNode(String name) { 932 mName = name; 933 } 934 935 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, 936 String name, String prefix, int[] counts, SparseIntArray pidCounts) { 937 String innerName = null; 938 if (mObservers.size() > 0) { 939 if ("".equals(name)) { 940 innerName = mName; 941 } else { 942 innerName = name + "/" + mName; 943 } 944 for (int i=0; i<mObservers.size(); i++) { 945 counts[1]++; 946 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix, 947 pidCounts); 948 } 949 } 950 if (mChildren.size() > 0) { 951 if (innerName == null) { 952 if ("".equals(name)) { 953 innerName = mName; 954 } else { 955 innerName = name + "/" + mName; 956 } 957 } 958 for (int i=0; i<mChildren.size(); i++) { 959 counts[0]++; 960 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix, 961 counts, pidCounts); 962 } 963 } 964 } 965 966 private String getUriSegment(Uri uri, int index) { 967 if (uri != null) { 968 if (index == 0) { 969 return uri.getAuthority(); 970 } else { 971 return uri.getPathSegments().get(index - 1); 972 } 973 } else { 974 return null; 975 } 976 } 977 978 private int countUriSegments(Uri uri) { 979 if (uri == null) { 980 return 0; 981 } 982 return uri.getPathSegments().size() + 1; 983 } 984 985 // Invariant: userHandle is either a hard user number or is USER_ALL 986 public void addObserverLocked(Uri uri, IContentObserver observer, 987 boolean notifyForDescendants, Object observersLock, 988 int uid, int pid, int userHandle) { 989 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, 990 uid, pid, userHandle); 991 } 992 993 private void addObserverLocked(Uri uri, int index, IContentObserver observer, 994 boolean notifyForDescendants, Object observersLock, 995 int uid, int pid, int userHandle) { 996 // If this is the leaf node add the observer 997 if (index == countUriSegments(uri)) { 998 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, 999 uid, pid, userHandle)); 1000 return; 1001 } 1002 1003 // Look to see if the proper child already exists 1004 String segment = getUriSegment(uri, index); 1005 if (segment == null) { 1006 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer"); 1007 } 1008 int N = mChildren.size(); 1009 for (int i = 0; i < N; i++) { 1010 ObserverNode node = mChildren.get(i); 1011 if (node.mName.equals(segment)) { 1012 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, 1013 observersLock, uid, pid, userHandle); 1014 return; 1015 } 1016 } 1017 1018 // No child found, create one 1019 ObserverNode node = new ObserverNode(segment); 1020 mChildren.add(node); 1021 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, 1022 observersLock, uid, pid, userHandle); 1023 } 1024 1025 public boolean removeObserverLocked(IContentObserver observer) { 1026 int size = mChildren.size(); 1027 for (int i = 0; i < size; i++) { 1028 boolean empty = mChildren.get(i).removeObserverLocked(observer); 1029 if (empty) { 1030 mChildren.remove(i); 1031 i--; 1032 size--; 1033 } 1034 } 1035 1036 IBinder observerBinder = observer.asBinder(); 1037 size = mObservers.size(); 1038 for (int i = 0; i < size; i++) { 1039 ObserverEntry entry = mObservers.get(i); 1040 if (entry.observer.asBinder() == observerBinder) { 1041 mObservers.remove(i); 1042 // We no longer need to listen for death notifications. Remove it. 1043 observerBinder.unlinkToDeath(entry, 0); 1044 break; 1045 } 1046 } 1047 1048 if (mChildren.size() == 0 && mObservers.size() == 0) { 1049 return true; 1050 } 1051 return false; 1052 } 1053 1054 private void collectMyObserversLocked(boolean leaf, IContentObserver observer, 1055 boolean observerWantsSelfNotifications, int targetUserHandle, 1056 ArrayList<ObserverCall> calls) { 1057 int N = mObservers.size(); 1058 IBinder observerBinder = observer == null ? null : observer.asBinder(); 1059 for (int i = 0; i < N; i++) { 1060 ObserverEntry entry = mObservers.get(i); 1061 1062 // Don't notify the observer if it sent the notification and isn't interested 1063 // in self notifications 1064 boolean selfChange = (entry.observer.asBinder() == observerBinder); 1065 if (selfChange && !observerWantsSelfNotifications) { 1066 continue; 1067 } 1068 1069 // Does this observer match the target user? 1070 if (targetUserHandle == UserHandle.USER_ALL 1071 || entry.userHandle == UserHandle.USER_ALL 1072 || targetUserHandle == entry.userHandle) { 1073 // Make sure the observer is interested in the notification 1074 if (leaf || (!leaf && entry.notifyForDescendants)) { 1075 calls.add(new ObserverCall(this, entry.observer, selfChange)); 1076 } 1077 } 1078 } 1079 } 1080 1081 /** 1082 * targetUserHandle is either a hard user handle or is USER_ALL 1083 */ 1084 public void collectObserversLocked(Uri uri, int index, IContentObserver observer, 1085 boolean observerWantsSelfNotifications, int targetUserHandle, 1086 ArrayList<ObserverCall> calls) { 1087 String segment = null; 1088 int segmentCount = countUriSegments(uri); 1089 if (index >= segmentCount) { 1090 // This is the leaf node, notify all observers 1091 collectMyObserversLocked(true, observer, observerWantsSelfNotifications, 1092 targetUserHandle, calls); 1093 } else if (index < segmentCount){ 1094 segment = getUriSegment(uri, index); 1095 // Notify any observers at this level who are interested in descendants 1096 collectMyObserversLocked(false, observer, observerWantsSelfNotifications, 1097 targetUserHandle, calls); 1098 } 1099 1100 int N = mChildren.size(); 1101 for (int i = 0; i < N; i++) { 1102 ObserverNode node = mChildren.get(i); 1103 if (segment == null || node.mName.equals(segment)) { 1104 // We found the child, 1105 node.collectObserversLocked(uri, index + 1, 1106 observer, observerWantsSelfNotifications, targetUserHandle, calls); 1107 if (segment != null) { 1108 break; 1109 } 1110 } 1111 } 1112 } 1113 } 1114} 1115