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