2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.content;
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;
32 import java.io.FileDescriptor;
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.List;
40 public 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();
48 private SyncManager getSyncManager() {
49 synchronized(mSyncManagerLock) {
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);
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");
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();
69 if (mSyncManager == null) {
70 pw.println("No SyncManager created! (Disk full?)");
72 mSyncManager.dump(fd, pw);
75 restoreCallingIdentity(identityToken);
80 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
81 throws RemoteException {
83 return super.onTransact(code, data, reply, flags);
84 } catch (RuntimeException e) {
85 // The content service only throws security exceptions, so let's
87 if (!(e instanceof SecurityException)) {
88 Log.e(TAG, "Content Service Crash", e);
94 /*package*/ ContentService(Context context, boolean factoryTest) {
96 mFactoryTest = factoryTest;
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");
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);
112 public void unregisterContentObserver(IContentObserver observer) {
113 if (observer == null) {
114 throw new IllegalArgumentException("You must pass a valid observer");
116 synchronized (mRootNode) {
117 mRootNode.removeObserverLocked(observer);
118 if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer);
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);
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();
132 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
133 synchronized (mRootNode) {
134 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
137 final int numCalls = calls.size();
138 for (int i=0; i<numCalls; i++) {
139 ObserverCall oc = calls.get(i);
141 oc.mObserver.onChange(oc.mSelfNotify);
142 if (Log.isLoggable(TAG, Log.VERBOSE)) {
143 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
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) {
164 SyncManager syncManager = getSyncManager();
165 if (syncManager != null) {
166 syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority());
170 restoreCallingIdentity(identityToken);
175 * Hide this class since it is not part of api,
176 * but current unittest framework requires it to be public
180 public static final class ObserverCall {
181 final ObserverNode mNode;
182 final IContentObserver mObserver;
183 final boolean mSelfNotify;
185 ObserverCall(ObserverNode node, IContentObserver observer,
186 boolean selfNotify) {
188 mObserver = observer;
189 mSelfNotify = selfNotify;
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();
199 SyncManager syncManager = getSyncManager();
200 if (syncManager != null) {
201 syncManager.scheduleSync(account, authority, extras, 0 /* no delay */,
202 false /* onlyThoseWithUnkownSyncableState */);
205 restoreCallingIdentity(identityToken);
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
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();
220 SyncManager syncManager = getSyncManager();
221 if (syncManager != null) {
222 syncManager.clearScheduledSyncOperations(account, authority);
223 syncManager.cancelActiveSync(account, authority);
226 restoreCallingIdentity(identityToken);
231 * Get information about the SyncAdapters that are known to the system.
232 * @return an array of SyncAdapters that have registered with the system
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();
239 SyncManager syncManager = getSyncManager();
240 return syncManager.getSyncAdapterTypes();
242 restoreCallingIdentity(identityToken);
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();
251 SyncManager syncManager = getSyncManager();
252 if (syncManager != null) {
253 return syncManager.getSyncStorageEngine().getSyncAutomatically(
254 account, providerName);
257 restoreCallingIdentity(identityToken);
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();
267 SyncManager syncManager = getSyncManager();
268 if (syncManager != null) {
269 syncManager.getSyncStorageEngine().setSyncAutomatically(
270 account, providerName, sync);
273 restoreCallingIdentity(identityToken);
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();
283 getSyncManager().getSyncStorageEngine().addPeriodicSync(
284 account, authority, extras, pollFrequency);
286 restoreCallingIdentity(identityToken);
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();
295 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras);
297 restoreCallingIdentity(identityToken);
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();
306 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
307 account, providerName);
309 restoreCallingIdentity(identityToken);
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();
318 SyncManager syncManager = getSyncManager();
319 if (syncManager != null) {
320 return syncManager.getSyncStorageEngine().getIsSyncable(
321 account, providerName);
324 restoreCallingIdentity(identityToken);
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();
334 SyncManager syncManager = getSyncManager();
335 if (syncManager != null) {
336 syncManager.getSyncStorageEngine().setIsSyncable(
337 account, providerName, syncable);
340 restoreCallingIdentity(identityToken);
344 public boolean getMasterSyncAutomatically() {
345 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
346 "no permission to read the sync settings");
347 long identityToken = clearCallingIdentity();
349 SyncManager syncManager = getSyncManager();
350 if (syncManager != null) {
351 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically();
354 restoreCallingIdentity(identityToken);
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();
364 SyncManager syncManager = getSyncManager();
365 if (syncManager != null) {
366 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag);
369 restoreCallingIdentity(identityToken);
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();
378 SyncManager syncManager = getSyncManager();
379 if (syncManager != null) {
380 return syncManager.getSyncStorageEngine().isSyncActive(
384 restoreCallingIdentity(identityToken);
389 public SyncInfo getCurrentSync() {
390 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
391 "no permission to read the sync stats");
392 long identityToken = clearCallingIdentity();
394 SyncManager syncManager = getSyncManager();
395 if (syncManager != null) {
396 return syncManager.getSyncStorageEngine().getCurrentSync();
399 restoreCallingIdentity(identityToken);
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();
409 SyncManager syncManager = getSyncManager();
410 if (syncManager != null) {
411 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
415 restoreCallingIdentity(identityToken);
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();
425 SyncManager syncManager = getSyncManager();
426 if (syncManager != null) {
427 return syncManager.getSyncStorageEngine().isSyncPending(account, authority);
430 restoreCallingIdentity(identityToken);
435 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
436 long identityToken = clearCallingIdentity();
438 SyncManager syncManager = getSyncManager();
439 if (syncManager != null && callback != null) {
440 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
443 restoreCallingIdentity(identityToken);
447 public void removeStatusChangeListener(ISyncStatusObserver callback) {
448 long identityToken = clearCallingIdentity();
450 SyncManager syncManager = getSyncManager();
451 if (syncManager != null && callback != null) {
452 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
455 restoreCallingIdentity(identityToken);
459 public static IContentService main(Context context, boolean factoryTest) {
460 ContentService service = new ContentService(context, factoryTest);
461 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
466 * Hide this class since it is not part of api,
467 * but current unittest framework requires it to be public
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;
476 public ObserverEntry(IContentObserver o, boolean n, Object observersLock) {
477 this.observersLock = observersLock;
479 notifyForDescendents = n;
481 observer.asBinder().linkToDeath(this, 0);
482 } catch (RemoteException e) {
487 public void binderDied() {
488 synchronized (observersLock) {
489 removeObserverLocked(observer);
494 public static final int INSERT_TYPE = 0;
495 public static final int UPDATE_TYPE = 1;
496 public static final int DELETE_TYPE = 2;
498 private String mName;
499 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
500 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
502 public ObserverNode(String name) {
506 private String getUriSegment(Uri uri, int index) {
509 return uri.getAuthority();
511 return uri.getPathSegments().get(index - 1);
518 private int countUriSegments(Uri uri) {
522 return uri.getPathSegments().size() + 1;
525 public void addObserverLocked(Uri uri, IContentObserver observer,
526 boolean notifyForDescendents, Object observersLock) {
527 addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
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));
538 // Look to see if the proper child already exists
539 String segment = getUriSegment(uri, index);
540 if (segment == null) {
541 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
543 int N = mChildren.size();
544 for (int i = 0; i < N; i++) {
545 ObserverNode node = mChildren.get(i);
546 if (node.mName.equals(segment)) {
547 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
552 // No child found, create one
553 ObserverNode node = new ObserverNode(segment);
555 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
558 public boolean removeObserverLocked(IContentObserver observer) {
559 int size = mChildren.size();
560 for (int i = 0; i < size; i++) {
561 boolean empty = mChildren.get(i).removeObserverLocked(observer);
569 IBinder observerBinder = observer.asBinder();
570 size = mObservers.size();
571 for (int i = 0; i < size; i++) {
572 ObserverEntry entry = mObservers.get(i);
573 if (entry.observer.asBinder() == observerBinder) {
574 mObservers.remove(i);
575 // We no longer need to listen for death notifications. Remove it.
576 observerBinder.unlinkToDeath(entry, 0);
581 if (mChildren.size() == 0 && mObservers.size() == 0) {
587 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
588 boolean selfNotify, ArrayList<ObserverCall> calls) {
589 int N = mObservers.size();
590 IBinder observerBinder = observer == null ? null : observer.asBinder();
591 for (int i = 0; i < N; i++) {
592 ObserverEntry entry = mObservers.get(i);
594 // Don't notify the observer if it sent the notification and isn't interesed
595 // in self notifications
596 if (entry.observer.asBinder() == observerBinder && !selfNotify) {
600 // Make sure the observer is interested in the notification
601 if (leaf || (!leaf && entry.notifyForDescendents)) {
602 calls.add(new ObserverCall(this, entry.observer, selfNotify));
607 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
608 boolean selfNotify, ArrayList<ObserverCall> calls) {
609 String segment = null;
610 int segmentCount = countUriSegments(uri);
611 if (index >= segmentCount) {
612 // This is the leaf node, notify all observers
613 collectMyObserversLocked(true, observer, selfNotify, calls);
614 } else if (index < segmentCount){
615 segment = getUriSegment(uri, index);
616 // Notify any observers at this level who are interested in descendents
617 collectMyObserversLocked(false, observer, selfNotify, calls);
620 int N = mChildren.size();
621 for (int i = 0; i < N; i++) {
622 ObserverNode node = mChildren.get(i);
623 if (segment == null || node.mName.equals(segment)) {
624 // We found the child,
625 node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls);
626 if (segment != null) {