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