ContentService.java revision d5e4fdc8a4743abc0d9fe3cb952a78f9ad078c6b
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 android.content; 18 19import android.accounts.Account; 20import android.database.IContentObserver; 21import android.database.sqlite.SQLiteException; 22import android.net.Uri; 23import android.os.Bundle; 24import android.os.IBinder; 25import android.os.Parcel; 26import android.os.RemoteException; 27import android.os.ServiceManager; 28import android.util.Config; 29import android.util.Log; 30import android.Manifest; 31 32import java.io.FileDescriptor; 33import java.io.PrintWriter; 34import java.util.ArrayList; 35import java.util.List; 36 37/** 38 * {@hide} 39 */ 40public final class ContentService extends IContentService.Stub { 41 private static final String TAG = "ContentService"; 42 private Context mContext; 43 private boolean mFactoryTest; 44 private final ObserverNode mRootNode = new ObserverNode(""); 45 private SyncManager mSyncManager = null; 46 private final Object mSyncManagerLock = new Object(); 47 48 private SyncManager getSyncManager() { 49 synchronized(mSyncManagerLock) { 50 try { 51 // Try to create the SyncManager, return null if it fails (e.g. the disk is full). 52 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest); 53 } catch (SQLiteException e) { 54 Log.e(TAG, "Can't create SyncManager", e); 55 } 56 return mSyncManager; 57 } 58 } 59 60 @Override 61 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 62 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP, 63 "caller doesn't have the DUMP permission"); 64 65 // This makes it so that future permission checks will be in the context of this 66 // process rather than the caller's process. We will restore this before returning. 67 long identityToken = clearCallingIdentity(); 68 try { 69 if (mSyncManager == null) { 70 pw.println("No SyncManager created! (Disk full?)"); 71 } else { 72 mSyncManager.dump(fd, pw); 73 } 74 } finally { 75 restoreCallingIdentity(identityToken); 76 } 77 } 78 79 @Override 80 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 81 throws RemoteException { 82 try { 83 return super.onTransact(code, data, reply, flags); 84 } catch (RuntimeException e) { 85 // The content service only throws security exceptions, so let's 86 // log all others. 87 if (!(e instanceof SecurityException)) { 88 Log.e(TAG, "Content Service Crash", e); 89 } 90 throw e; 91 } 92 } 93 94 /*package*/ ContentService(Context context, boolean factoryTest) { 95 mContext = context; 96 mFactoryTest = factoryTest; 97 getSyncManager(); 98 } 99 100 public void registerContentObserver(Uri uri, boolean notifyForDescendents, 101 IContentObserver observer) { 102 if (observer == null || uri == null) { 103 throw new IllegalArgumentException("You must pass a valid uri and observer"); 104 } 105 synchronized (mRootNode) { 106 mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode); 107 if (Config.LOGV) Log.v(TAG, "Registered observer " + observer + " at " + uri + 108 " with notifyForDescendents " + notifyForDescendents); 109 } 110 } 111 112 public void unregisterContentObserver(IContentObserver observer) { 113 if (observer == null) { 114 throw new IllegalArgumentException("You must pass a valid observer"); 115 } 116 synchronized (mRootNode) { 117 mRootNode.removeObserverLocked(observer); 118 if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer); 119 } 120 } 121 122 public void notifyChange(Uri uri, IContentObserver observer, 123 boolean observerWantsSelfNotifications, boolean syncToNetwork) { 124 if (Log.isLoggable(TAG, Log.VERBOSE)) { 125 Log.v(TAG, "Notifying update of " + uri + " from observer " + observer 126 + ", syncToNetwork " + syncToNetwork); 127 } 128 // This makes it so that future permission checks will be in the context of this 129 // process rather than the caller's process. We will restore this before returning. 130 long identityToken = clearCallingIdentity(); 131 try { 132 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); 133 synchronized (mRootNode) { 134 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, 135 calls); 136 } 137 final int numCalls = calls.size(); 138 for (int i=0; i<numCalls; i++) { 139 ObserverCall oc = calls.get(i); 140 try { 141 oc.mObserver.onChange(oc.mSelfNotify); 142 if (Log.isLoggable(TAG, Log.VERBOSE)) { 143 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri); 144 } 145 } catch (RemoteException ex) { 146 synchronized (mRootNode) { 147 Log.w(TAG, "Found dead observer, removing"); 148 IBinder binder = oc.mObserver.asBinder(); 149 final ArrayList<ObserverNode.ObserverEntry> list 150 = oc.mNode.mObservers; 151 int numList = list.size(); 152 for (int j=0; j<numList; j++) { 153 ObserverNode.ObserverEntry oe = list.get(j); 154 if (oe.observer.asBinder() == binder) { 155 list.remove(j); 156 j--; 157 numList--; 158 } 159 } 160 } 161 } 162 } 163 if (syncToNetwork) { 164 SyncManager syncManager = getSyncManager(); 165 if (syncManager != null) { 166 syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority()); 167 } 168 } 169 } finally { 170 restoreCallingIdentity(identityToken); 171 } 172 } 173 174 /** 175 * Hide this class since it is not part of api, 176 * but current unittest framework requires it to be public 177 * @hide 178 * 179 */ 180 public static final class ObserverCall { 181 final ObserverNode mNode; 182 final IContentObserver mObserver; 183 final boolean mSelfNotify; 184 185 ObserverCall(ObserverNode node, IContentObserver observer, 186 boolean selfNotify) { 187 mNode = node; 188 mObserver = observer; 189 mSelfNotify = selfNotify; 190 } 191 } 192 193 public void requestSync(Account account, String authority, Bundle extras) { 194 ContentResolver.validateSyncExtrasBundle(extras); 195 // This makes it so that future permission checks will be in the context of this 196 // process rather than the caller's process. We will restore this before returning. 197 long identityToken = clearCallingIdentity(); 198 try { 199 SyncManager syncManager = getSyncManager(); 200 if (syncManager != null) { 201 syncManager.scheduleSync(account, authority, extras, 0 /* no delay */, 202 false /* onlyThoseWithUnkownSyncableState */); 203 } 204 } finally { 205 restoreCallingIdentity(identityToken); 206 } 207 } 208 209 /** 210 * Clear all scheduled sync operations that match the uri and cancel the active sync 211 * if they match the authority and account, if they are present. 212 * @param account filter the pending and active syncs to cancel using this account 213 * @param authority filter the pending and active syncs to cancel using this authority 214 */ 215 public void cancelSync(Account account, String authority) { 216 // This makes it so that future permission checks will be in the context of this 217 // process rather than the caller's process. We will restore this before returning. 218 long identityToken = clearCallingIdentity(); 219 try { 220 SyncManager syncManager = getSyncManager(); 221 if (syncManager != null) { 222 syncManager.clearScheduledSyncOperations(account, authority); 223 syncManager.cancelActiveSync(account, authority); 224 } 225 } finally { 226 restoreCallingIdentity(identityToken); 227 } 228 } 229 230 /** 231 * Get information about the SyncAdapters that are known to the system. 232 * @return an array of SyncAdapters that have registered with the system 233 */ 234 public SyncAdapterType[] getSyncAdapterTypes() { 235 // This makes it so that future permission checks will be in the context of this 236 // process rather than the caller's process. We will restore this before returning. 237 long identityToken = clearCallingIdentity(); 238 try { 239 SyncManager syncManager = getSyncManager(); 240 return syncManager.getSyncAdapterTypes(); 241 } finally { 242 restoreCallingIdentity(identityToken); 243 } 244 } 245 246 public boolean getSyncAutomatically(Account account, String providerName) { 247 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 248 "no permission to read the sync settings"); 249 long identityToken = clearCallingIdentity(); 250 try { 251 SyncManager syncManager = getSyncManager(); 252 if (syncManager != null) { 253 return syncManager.getSyncStorageEngine().getSyncAutomatically( 254 account, providerName); 255 } 256 } finally { 257 restoreCallingIdentity(identityToken); 258 } 259 return false; 260 } 261 262 public void setSyncAutomatically(Account account, String providerName, boolean sync) { 263 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 264 "no permission to write the sync settings"); 265 long identityToken = clearCallingIdentity(); 266 try { 267 SyncManager syncManager = getSyncManager(); 268 if (syncManager != null) { 269 syncManager.getSyncStorageEngine().setSyncAutomatically( 270 account, providerName, sync); 271 } 272 } finally { 273 restoreCallingIdentity(identityToken); 274 } 275 } 276 277 public void addPeriodicSync(Account account, String authority, Bundle extras, 278 long pollFrequency) { 279 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 280 "no permission to write the sync settings"); 281 long identityToken = clearCallingIdentity(); 282 try { 283 getSyncManager().getSyncStorageEngine().addPeriodicSync( 284 account, authority, extras, pollFrequency); 285 } finally { 286 restoreCallingIdentity(identityToken); 287 } 288 } 289 290 public void removePeriodicSync(Account account, String authority, Bundle extras) { 291 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 292 "no permission to write the sync settings"); 293 long identityToken = clearCallingIdentity(); 294 try { 295 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras); 296 } finally { 297 restoreCallingIdentity(identityToken); 298 } 299 } 300 301 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) { 302 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 303 "no permission to read the sync settings"); 304 long identityToken = clearCallingIdentity(); 305 try { 306 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( 307 account, providerName); 308 } finally { 309 restoreCallingIdentity(identityToken); 310 } 311 } 312 313 public int getIsSyncable(Account account, String providerName) { 314 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 315 "no permission to read the sync settings"); 316 long identityToken = clearCallingIdentity(); 317 try { 318 SyncManager syncManager = getSyncManager(); 319 if (syncManager != null) { 320 return syncManager.getSyncStorageEngine().getIsSyncable( 321 account, providerName); 322 } 323 } finally { 324 restoreCallingIdentity(identityToken); 325 } 326 return -1; 327 } 328 329 public void setIsSyncable(Account account, String providerName, int syncable) { 330 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 331 "no permission to write the sync settings"); 332 long identityToken = clearCallingIdentity(); 333 try { 334 SyncManager syncManager = getSyncManager(); 335 if (syncManager != null) { 336 syncManager.getSyncStorageEngine().setIsSyncable( 337 account, providerName, syncable); 338 } 339 } finally { 340 restoreCallingIdentity(identityToken); 341 } 342 } 343 344 public boolean getMasterSyncAutomatically() { 345 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, 346 "no permission to read the sync settings"); 347 long identityToken = clearCallingIdentity(); 348 try { 349 SyncManager syncManager = getSyncManager(); 350 if (syncManager != null) { 351 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(); 352 } 353 } finally { 354 restoreCallingIdentity(identityToken); 355 } 356 return false; 357 } 358 359 public void setMasterSyncAutomatically(boolean flag) { 360 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, 361 "no permission to write the sync settings"); 362 long identityToken = clearCallingIdentity(); 363 try { 364 SyncManager syncManager = getSyncManager(); 365 if (syncManager != null) { 366 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag); 367 } 368 } finally { 369 restoreCallingIdentity(identityToken); 370 } 371 } 372 373 public boolean isSyncActive(Account account, String authority) { 374 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 375 "no permission to read the sync stats"); 376 long identityToken = clearCallingIdentity(); 377 try { 378 SyncManager syncManager = getSyncManager(); 379 if (syncManager != null) { 380 return syncManager.getSyncStorageEngine().isSyncActive( 381 account, authority); 382 } 383 } finally { 384 restoreCallingIdentity(identityToken); 385 } 386 return false; 387 } 388 389 public SyncInfo getCurrentSync() { 390 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 391 "no permission to read the sync stats"); 392 long identityToken = clearCallingIdentity(); 393 try { 394 SyncManager syncManager = getSyncManager(); 395 if (syncManager != null) { 396 return syncManager.getSyncStorageEngine().getCurrentSync(); 397 } 398 } finally { 399 restoreCallingIdentity(identityToken); 400 } 401 return null; 402 } 403 404 public SyncStatusInfo getSyncStatus(Account account, String authority) { 405 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 406 "no permission to read the sync stats"); 407 long identityToken = clearCallingIdentity(); 408 try { 409 SyncManager syncManager = getSyncManager(); 410 if (syncManager != null) { 411 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( 412 account, authority); 413 } 414 } finally { 415 restoreCallingIdentity(identityToken); 416 } 417 return null; 418 } 419 420 public boolean isSyncPending(Account account, String authority) { 421 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, 422 "no permission to read the sync stats"); 423 long identityToken = clearCallingIdentity(); 424 try { 425 SyncManager syncManager = getSyncManager(); 426 if (syncManager != null) { 427 return syncManager.getSyncStorageEngine().isSyncPending(account, authority); 428 } 429 } finally { 430 restoreCallingIdentity(identityToken); 431 } 432 return false; 433 } 434 435 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { 436 long identityToken = clearCallingIdentity(); 437 try { 438 SyncManager syncManager = getSyncManager(); 439 if (syncManager != null && callback != null) { 440 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); 441 } 442 } finally { 443 restoreCallingIdentity(identityToken); 444 } 445 } 446 447 public void removeStatusChangeListener(ISyncStatusObserver callback) { 448 long identityToken = clearCallingIdentity(); 449 try { 450 SyncManager syncManager = getSyncManager(); 451 if (syncManager != null && callback != null) { 452 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); 453 } 454 } finally { 455 restoreCallingIdentity(identityToken); 456 } 457 } 458 459 public static IContentService main(Context context, boolean factoryTest) { 460 ContentService service = new ContentService(context, factoryTest); 461 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); 462 return service; 463 } 464 465 /** 466 * Hide this class since it is not part of api, 467 * but current unittest framework requires it to be public 468 * @hide 469 */ 470 public static final class ObserverNode { 471 private class ObserverEntry implements IBinder.DeathRecipient { 472 public final IContentObserver observer; 473 public final boolean notifyForDescendents; 474 private final Object observersLock; 475 476 public ObserverEntry(IContentObserver o, boolean n, Object observersLock) { 477 this.observersLock = observersLock; 478 observer = o; 479 notifyForDescendents = n; 480 try { 481 observer.asBinder().linkToDeath(this, 0); 482 } catch (RemoteException e) { 483 binderDied(); 484 } 485 } 486 487 public void binderDied() { 488 synchronized (observersLock) { 489 removeObserverLocked(observer); 490 } 491 } 492 } 493 494 public static final int INSERT_TYPE = 0; 495 public static final int UPDATE_TYPE = 1; 496 public static final int DELETE_TYPE = 2; 497 498 private String mName; 499 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); 500 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); 501 502 public ObserverNode(String name) { 503 mName = name; 504 } 505 506 private String getUriSegment(Uri uri, int index) { 507 if (uri != null) { 508 if (index == 0) { 509 return uri.getAuthority(); 510 } else { 511 return uri.getPathSegments().get(index - 1); 512 } 513 } else { 514 return null; 515 } 516 } 517 518 private int countUriSegments(Uri uri) { 519 if (uri == null) { 520 return 0; 521 } 522 return uri.getPathSegments().size() + 1; 523 } 524 525 public void addObserverLocked(Uri uri, IContentObserver observer, 526 boolean notifyForDescendents, Object observersLock) { 527 addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock); 528 } 529 530 private void addObserverLocked(Uri uri, int index, IContentObserver observer, 531 boolean notifyForDescendents, Object observersLock) { 532 // If this is the leaf node add the observer 533 if (index == countUriSegments(uri)) { 534 mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock)); 535 return; 536 } 537 538 // Look to see if the proper child already exists 539 String segment = getUriSegment(uri, index); 540 int N = mChildren.size(); 541 for (int i = 0; i < N; i++) { 542 ObserverNode node = mChildren.get(i); 543 if (node.mName.equals(segment)) { 544 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock); 545 return; 546 } 547 } 548 549 // No child found, create one 550 ObserverNode node = new ObserverNode(segment); 551 mChildren.add(node); 552 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock); 553 } 554 555 public boolean removeObserverLocked(IContentObserver observer) { 556 int size = mChildren.size(); 557 for (int i = 0; i < size; i++) { 558 boolean empty = mChildren.get(i).removeObserverLocked(observer); 559 if (empty) { 560 mChildren.remove(i); 561 i--; 562 size--; 563 } 564 } 565 566 IBinder observerBinder = observer.asBinder(); 567 size = mObservers.size(); 568 for (int i = 0; i < size; i++) { 569 ObserverEntry entry = mObservers.get(i); 570 if (entry.observer.asBinder() == observerBinder) { 571 mObservers.remove(i); 572 // We no longer need to listen for death notifications. Remove it. 573 observerBinder.unlinkToDeath(entry, 0); 574 break; 575 } 576 } 577 578 if (mChildren.size() == 0 && mObservers.size() == 0) { 579 return true; 580 } 581 return false; 582 } 583 584 private void collectMyObserversLocked(boolean leaf, IContentObserver observer, 585 boolean selfNotify, ArrayList<ObserverCall> calls) { 586 int N = mObservers.size(); 587 IBinder observerBinder = observer == null ? null : observer.asBinder(); 588 for (int i = 0; i < N; i++) { 589 ObserverEntry entry = mObservers.get(i); 590 591 // Don't notify the observer if it sent the notification and isn't interesed 592 // in self notifications 593 if (entry.observer.asBinder() == observerBinder && !selfNotify) { 594 continue; 595 } 596 597 // Make sure the observer is interested in the notification 598 if (leaf || (!leaf && entry.notifyForDescendents)) { 599 calls.add(new ObserverCall(this, entry.observer, selfNotify)); 600 } 601 } 602 } 603 604 public void collectObserversLocked(Uri uri, int index, IContentObserver observer, 605 boolean selfNotify, ArrayList<ObserverCall> calls) { 606 String segment = null; 607 int segmentCount = countUriSegments(uri); 608 if (index >= segmentCount) { 609 // This is the leaf node, notify all observers 610 collectMyObserversLocked(true, observer, selfNotify, calls); 611 } else if (index < segmentCount){ 612 segment = getUriSegment(uri, index); 613 // Notify any observers at this level who are interested in descendents 614 collectMyObserversLocked(false, observer, selfNotify, calls); 615 } 616 617 int N = mChildren.size(); 618 for (int i = 0; i < N; i++) { 619 ObserverNode node = mChildren.get(i); 620 if (segment == null || node.mName.equals(segment)) { 621 // We found the child, 622 node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls); 623 if (segment != null) { 624 break; 625 } 626 } 627 } 628 } 629 } 630} 631