OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / content / ContentService.java
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 import java.util.List;
36
37 /**
38  * {@hide}
39  */
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();
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             if (segment == null) {
541                 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
542             }
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);
548                     return;
549                 }
550             }
551
552             // No child found, create one
553             ObserverNode node = new ObserverNode(segment);
554             mChildren.add(node);
555             node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
556         }
557
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);
562                 if (empty) {
563                     mChildren.remove(i);
564                     i--;
565                     size--;
566                 }
567             }
568
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);
577                     break;
578                 }
579             }
580
581             if (mChildren.size() == 0 && mObservers.size() == 0) {
582                 return true;
583             }
584             return false;
585         }
586
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);
593
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) {
597                     continue;
598                 }
599
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));
603                 }
604             }
605         }
606
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);
618             }
619
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) {
627                         break;
628                     }
629                 }
630             }
631         }
632     }
633 }