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