• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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