OSDN Git Service

SyncManager now returns copy on getCurrentSyncs()
[android-x86/frameworks-base.git] / services / java / com / android / server / 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 com.android.server.content;
18
19 import android.Manifest;
20 import android.accounts.Account;
21 import android.app.ActivityManager;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.IContentService;
25 import android.content.ISyncStatusObserver;
26 import android.content.PeriodicSync;
27 import android.content.SyncAdapterType;
28 import android.content.SyncInfo;
29 import android.content.SyncRequest;
30 import android.content.SyncStatusInfo;
31 import android.database.IContentObserver;
32 import android.database.sqlite.SQLiteException;
33 import android.net.Uri;
34 import android.os.Binder;
35 import android.os.Bundle;
36 import android.os.IBinder;
37 import android.os.Parcel;
38 import android.os.RemoteException;
39 import android.os.ServiceManager;
40 import android.os.SystemProperties;
41 import android.os.UserHandle;
42 import android.text.TextUtils;
43 import android.util.Log;
44 import android.util.Pair;
45 import android.util.Slog;
46 import android.util.SparseIntArray;
47
48 import java.io.FileDescriptor;
49 import java.io.PrintWriter;
50 import java.security.InvalidParameterException;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.List;
55
56 /**
57  * {@hide}
58  */
59 public final class ContentService extends IContentService.Stub {
60     private static final String TAG = "ContentService";
61     private Context mContext;
62     private boolean mFactoryTest;
63     private final ObserverNode mRootNode = new ObserverNode("");
64     private SyncManager mSyncManager = null;
65     private final Object mSyncManagerLock = new Object();
66
67     private SyncManager getSyncManager() {
68         if (SystemProperties.getBoolean("config.disable_network", false)) {
69             return null;
70         }
71
72         synchronized(mSyncManagerLock) {
73             try {
74                 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
75                 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
76             } catch (SQLiteException e) {
77                 Log.e(TAG, "Can't create SyncManager", e);
78             }
79             return mSyncManager;
80         }
81     }
82
83     @Override
84     protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
85         mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
86                 "caller doesn't have the DUMP permission");
87
88         // This makes it so that future permission checks will be in the context of this
89         // process rather than the caller's process. We will restore this before returning.
90         long identityToken = clearCallingIdentity();
91         try {
92             if (mSyncManager == null) {
93                 pw.println("No SyncManager created!  (Disk full?)");
94             } else {
95                 mSyncManager.dump(fd, pw);
96             }
97             pw.println();
98             pw.println("Observer tree:");
99             synchronized (mRootNode) {
100                 int[] counts = new int[2];
101                 final SparseIntArray pidCounts = new SparseIntArray();
102                 mRootNode.dumpLocked(fd, pw, args, "", "  ", counts, pidCounts);
103                 pw.println();
104                 ArrayList<Integer> sorted = new ArrayList<Integer>();
105                 for (int i=0; i<pidCounts.size(); i++) {
106                     sorted.add(pidCounts.keyAt(i));
107                 }
108                 Collections.sort(sorted, new Comparator<Integer>() {
109                     @Override
110                     public int compare(Integer lhs, Integer rhs) {
111                         int lc = pidCounts.get(lhs);
112                         int rc = pidCounts.get(rhs);
113                         if (lc < rc) {
114                             return 1;
115                         } else if (lc > rc) {
116                             return -1;
117                         }
118                         return 0;
119                     }
120
121                 });
122                 for (int i=0; i<sorted.size(); i++) {
123                     int pid = sorted.get(i);
124                     pw.print("  pid "); pw.print(pid); pw.print(": ");
125                             pw.print(pidCounts.get(pid)); pw.println(" observers");
126                 }
127                 pw.println();
128                 pw.print(" Total number of nodes: "); pw.println(counts[0]);
129                 pw.print(" Total number of observers: "); pw.println(counts[1]);
130             }
131         } finally {
132             restoreCallingIdentity(identityToken);
133         }
134     }
135
136     @Override
137     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
138             throws RemoteException {
139         try {
140             return super.onTransact(code, data, reply, flags);
141         } catch (RuntimeException e) {
142             // The content service only throws security exceptions, so let's
143             // log all others.
144             if (!(e instanceof SecurityException)) {
145                 Slog.wtf(TAG, "Content Service Crash", e);
146             }
147             throw e;
148         }
149     }
150
151     /*package*/ ContentService(Context context, boolean factoryTest) {
152         mContext = context;
153         mFactoryTest = factoryTest;
154     }
155
156     public void systemReady() {
157         getSyncManager();
158     }
159
160     /**
161      * Register a content observer tied to a specific user's view of the provider.
162      * @param userHandle the user whose view of the provider is to be observed.  May be
163      *     the calling user without requiring any permission, otherwise the caller needs to
164      *     hold the INTERACT_ACROSS_USERS_FULL permission.  Pseudousers USER_ALL and
165      *     USER_CURRENT are properly handled; all other pseudousers are forbidden.
166      */
167     @Override
168     public void registerContentObserver(Uri uri, boolean notifyForDescendants,
169             IContentObserver observer, int userHandle) {
170         if (observer == null || uri == null) {
171             throw new IllegalArgumentException("You must pass a valid uri and observer");
172         }
173
174         final int callingUser = UserHandle.getCallingUserId();
175         if (callingUser != userHandle) {
176             mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
177                     "no permission to observe other users' provider view");
178         }
179
180         if (userHandle < 0) {
181             if (userHandle == UserHandle.USER_CURRENT) {
182                 userHandle = ActivityManager.getCurrentUser();
183             } else if (userHandle != UserHandle.USER_ALL) {
184                 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
185                         + userHandle);
186             }
187         }
188
189         synchronized (mRootNode) {
190             mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
191                     Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
192             if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
193                     " with notifyForDescendants " + notifyForDescendants);
194         }
195     }
196
197     public void registerContentObserver(Uri uri, boolean notifyForDescendants,
198             IContentObserver observer) {
199         registerContentObserver(uri, notifyForDescendants, observer,
200                 UserHandle.getCallingUserId());
201     }
202
203     public void unregisterContentObserver(IContentObserver observer) {
204         if (observer == null) {
205             throw new IllegalArgumentException("You must pass a valid observer");
206         }
207         synchronized (mRootNode) {
208             mRootNode.removeObserverLocked(observer);
209             if (false) Log.v(TAG, "Unregistered observer " + observer);
210         }
211     }
212
213     /**
214      * Notify observers of a particular user's view of the provider.
215      * @param userHandle the user whose view of the provider is to be notified.  May be
216      *     the calling user without requiring any permission, otherwise the caller needs to
217      *     hold the INTERACT_ACROSS_USERS_FULL permission.  Pseudousers USER_ALL and
218      *     USER_CURRENT are properly interpreted; no other pseudousers are allowed.
219      */
220     @Override
221     public void notifyChange(Uri uri, IContentObserver observer,
222             boolean observerWantsSelfNotifications, boolean syncToNetwork,
223             int userHandle) {
224         if (Log.isLoggable(TAG, Log.VERBOSE)) {
225             Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
226                     + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
227         }
228
229         // Notify for any user other than the caller's own requires permission.
230         final int callingUserHandle = UserHandle.getCallingUserId();
231         if (userHandle != callingUserHandle) {
232             mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
233                     "no permission to notify other users");
234         }
235
236         // We passed the permission check; resolve pseudouser targets as appropriate
237         if (userHandle < 0) {
238             if (userHandle == UserHandle.USER_CURRENT) {
239                 userHandle = ActivityManager.getCurrentUser();
240             } else if (userHandle != UserHandle.USER_ALL) {
241                 throw new InvalidParameterException("Bad user handle for notifyChange: "
242                         + userHandle);
243             }
244         }
245
246         final int uid = Binder.getCallingUid();
247         // This makes it so that future permission checks will be in the context of this
248         // process rather than the caller's process. We will restore this before returning.
249         long identityToken = clearCallingIdentity();
250         try {
251             ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
252             synchronized (mRootNode) {
253                 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
254                         userHandle, calls);
255             }
256             final int numCalls = calls.size();
257             for (int i=0; i<numCalls; i++) {
258                 ObserverCall oc = calls.get(i);
259                 try {
260                     oc.mObserver.onChange(oc.mSelfChange, uri);
261                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
262                         Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
263                     }
264                 } catch (RemoteException ex) {
265                     synchronized (mRootNode) {
266                         Log.w(TAG, "Found dead observer, removing");
267                         IBinder binder = oc.mObserver.asBinder();
268                         final ArrayList<ObserverNode.ObserverEntry> list
269                                 = oc.mNode.mObservers;
270                         int numList = list.size();
271                         for (int j=0; j<numList; j++) {
272                             ObserverNode.ObserverEntry oe = list.get(j);
273                             if (oe.observer.asBinder() == binder) {
274                                 list.remove(j);
275                                 j--;
276                                 numList--;
277                             }
278                         }
279                     }
280                 }
281             }
282             if (syncToNetwork) {
283                 SyncManager syncManager = getSyncManager();
284                 if (syncManager != null) {
285                     syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
286                             uri.getAuthority());
287                 }
288             }
289         } finally {
290             restoreCallingIdentity(identityToken);
291         }
292     }
293
294     public void notifyChange(Uri uri, IContentObserver observer,
295             boolean observerWantsSelfNotifications, boolean syncToNetwork) {
296         notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
297                 UserHandle.getCallingUserId());
298     }
299
300     /**
301      * Hide this class since it is not part of api,
302      * but current unittest framework requires it to be public
303      * @hide
304      *
305      */
306     public static final class ObserverCall {
307         final ObserverNode mNode;
308         final IContentObserver mObserver;
309         final boolean mSelfChange;
310
311         ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
312             mNode = node;
313             mObserver = observer;
314             mSelfChange = selfChange;
315         }
316     }
317
318     @Override
319     public void requestSync(Account account, String authority, Bundle extras) {
320         ContentResolver.validateSyncExtrasBundle(extras);
321         int userId = UserHandle.getCallingUserId();
322         int uId = Binder.getCallingUid();
323
324         // This makes it so that future permission checks will be in the context of this
325         // process rather than the caller's process. We will restore this before returning.
326         long identityToken = clearCallingIdentity();
327         try {
328             SyncManager syncManager = getSyncManager();
329             if (syncManager != null) {
330                 syncManager.scheduleSync(account, userId, uId, authority, extras,
331                         0 /* no delay */, 0 /* no delay */,
332                         false /* onlyThoseWithUnkownSyncableState */);
333             }
334         } finally {
335             restoreCallingIdentity(identityToken);
336         }
337     }
338
339     /**
340      * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
341      * either:
342      *   periodic OR one-off sync.
343      * and
344      *   anonymous OR provider sync.
345      * Depending on the request, we enqueue to suit in the SyncManager.
346      * @param request The request object. Validation of this object is done by its builder.
347      */
348     @Override
349     public void sync(SyncRequest request) {
350         Bundle extras = request.getBundle();
351         long flextime = request.getSyncFlexTime();
352         long runAtTime = request.getSyncRunTime();
353         int userId = UserHandle.getCallingUserId();
354         int uId = Binder.getCallingUid();
355
356         // This makes it so that future permission checks will be in the context of this
357         // process rather than the caller's process. We will restore this before returning.
358         long identityToken = clearCallingIdentity();
359         try {
360             SyncManager syncManager = getSyncManager();
361             if (syncManager != null) {
362                 if (request.hasAuthority()) {
363                     // Sync Adapter registered with the system - old API.
364                     final  Account account = request.getAccount();
365                     final String provider = request.getProvider();
366                     if (request.isPeriodic()) {
367                         mContext.enforceCallingOrSelfPermission(
368                                 Manifest.permission.WRITE_SYNC_SETTINGS,
369                                 "no permission to write the sync settings");
370                         if (runAtTime < 60) {
371                             Slog.w(TAG, "Requested poll frequency of " + runAtTime
372                                     + " seconds being rounded up to 60 seconds.");
373                             runAtTime = 60;
374                         }
375                         PeriodicSync syncToAdd =
376                                 new PeriodicSync(account, provider, extras, runAtTime, flextime);
377                         getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId);
378                     } else {
379                         long beforeRuntimeMillis = (flextime) * 1000;
380                         long runtimeMillis = runAtTime * 1000;
381                         syncManager.scheduleSync(
382                                 account, userId, uId, provider, extras,
383                                 beforeRuntimeMillis, runtimeMillis,
384                                 false /* onlyThoseWithUnknownSyncableState */);
385                     }
386                 } else {
387                     Log.w(TAG, "Unrecognised sync parameters, doing nothing.");
388                 }
389             }
390         } finally {
391             restoreCallingIdentity(identityToken);
392         }
393     }
394
395     /**
396      * Clear all scheduled sync operations that match the uri and cancel the active sync
397      * if they match the authority and account, if they are present.
398      * @param account filter the pending and active syncs to cancel using this account
399      * @param authority filter the pending and active syncs to cancel using this authority
400      */
401     @Override
402     public void cancelSync(Account account, String authority) {
403         if (authority != null && authority.length() == 0) {
404             throw new IllegalArgumentException("Authority must be non-empty");
405         }
406
407         int userId = UserHandle.getCallingUserId();
408         // This makes it so that future permission checks will be in the context of this
409         // process rather than the caller's process. We will restore this before returning.
410         long identityToken = clearCallingIdentity();
411         try {
412             SyncManager syncManager = getSyncManager();
413             if (syncManager != null) {
414                 syncManager.clearScheduledSyncOperations(account, userId, authority);
415                 syncManager.cancelActiveSync(account, userId, authority);
416             }
417         } finally {
418             restoreCallingIdentity(identityToken);
419         }
420     }
421
422     /**
423      * Get information about the SyncAdapters that are known to the system.
424      * @return an array of SyncAdapters that have registered with the system
425      */
426     @Override
427     public SyncAdapterType[] getSyncAdapterTypes() {
428         // This makes it so that future permission checks will be in the context of this
429         // process rather than the caller's process. We will restore this before returning.
430         final int userId = UserHandle.getCallingUserId();
431         final long identityToken = clearCallingIdentity();
432         try {
433             SyncManager syncManager = getSyncManager();
434             return syncManager.getSyncAdapterTypes(userId);
435         } finally {
436             restoreCallingIdentity(identityToken);
437         }
438     }
439
440     @Override
441     public boolean getSyncAutomatically(Account account, String providerName) {
442         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
443                 "no permission to read the sync settings");
444
445         int userId = UserHandle.getCallingUserId();
446         long identityToken = clearCallingIdentity();
447         try {
448             SyncManager syncManager = getSyncManager();
449             if (syncManager != null) {
450                 return syncManager.getSyncStorageEngine().getSyncAutomatically(
451                         account, userId, providerName);
452             }
453         } finally {
454             restoreCallingIdentity(identityToken);
455         }
456         return false;
457     }
458
459     @Override
460     public void setSyncAutomatically(Account account, String providerName, boolean sync) {
461         if (TextUtils.isEmpty(providerName)) {
462             throw new IllegalArgumentException("Authority must be non-empty");
463         }
464         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
465                 "no permission to write the sync settings");
466
467         int userId = UserHandle.getCallingUserId();
468         long identityToken = clearCallingIdentity();
469         try {
470             SyncManager syncManager = getSyncManager();
471             if (syncManager != null) {
472                 syncManager.getSyncStorageEngine().setSyncAutomatically(
473                         account, userId, providerName, sync);
474             }
475         } finally {
476             restoreCallingIdentity(identityToken);
477         }
478     }
479
480     /** Old API. Schedule periodic sync with default flex time. */
481     @Override
482     public void addPeriodicSync(Account account, String authority, Bundle extras,
483             long pollFrequency) {
484         if (account == null) {
485             throw new IllegalArgumentException("Account must not be null");
486         }
487         if (TextUtils.isEmpty(authority)) {
488             throw new IllegalArgumentException("Authority must not be empty.");
489         }
490         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
491                 "no permission to write the sync settings");
492
493         int userId = UserHandle.getCallingUserId();
494         if (pollFrequency < 60) {
495             Slog.w(TAG, "Requested poll frequency of " + pollFrequency
496                     + " seconds being rounded up to 60 seconds.");
497             pollFrequency = 60;
498         }
499
500         long identityToken = clearCallingIdentity();
501         try {
502             // Add default flex time to this sync.
503             PeriodicSync syncToAdd =
504                     new PeriodicSync(account, authority, extras,
505                             pollFrequency,
506                             SyncStorageEngine.calculateDefaultFlexTime(pollFrequency));
507             getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId);
508         } finally {
509             restoreCallingIdentity(identityToken);
510         }
511     }
512
513     @Override
514     public void removePeriodicSync(Account account, String authority, Bundle extras) {
515         if (account == null) {
516             throw new IllegalArgumentException("Account must not be null");
517         }
518         if (TextUtils.isEmpty(authority)) {
519             throw new IllegalArgumentException("Authority must not be empty");
520         }
521         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
522                 "no permission to write the sync settings");
523
524         int userId = UserHandle.getCallingUserId();
525         long identityToken = clearCallingIdentity();
526         try {
527             PeriodicSync syncToRemove = new PeriodicSync(account, authority, extras,
528                     0 /* Not read for removal */, 0 /* Not read for removal */);
529             getSyncManager().getSyncStorageEngine().removePeriodicSync(syncToRemove, userId);
530         } finally {
531             restoreCallingIdentity(identityToken);
532         }
533     }
534
535     /**
536      * TODO: Implement.
537      * @param request Sync to remove.
538      */
539     public void removeSync(SyncRequest request) {
540
541     }
542
543     @Override
544     public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
545         if (account == null) {
546             throw new IllegalArgumentException("Account must not be null");
547         }
548         if (TextUtils.isEmpty(providerName)) {
549             throw new IllegalArgumentException("Authority must not be empty");
550         }
551         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
552                 "no permission to read the sync settings");
553
554         int userId = UserHandle.getCallingUserId();
555         long identityToken = clearCallingIdentity();
556         try {
557             return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
558                     account, userId, providerName);
559         } finally {
560             restoreCallingIdentity(identityToken);
561         }
562     }
563
564     public int getIsSyncable(Account account, String providerName) {
565         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
566                 "no permission to read the sync settings");
567         int userId = UserHandle.getCallingUserId();
568
569         long identityToken = clearCallingIdentity();
570         try {
571             SyncManager syncManager = getSyncManager();
572             if (syncManager != null) {
573                 return syncManager.getIsSyncable(
574                         account, userId, providerName);
575             }
576         } finally {
577             restoreCallingIdentity(identityToken);
578         }
579         return -1;
580     }
581
582     @Override
583     public void setIsSyncable(Account account, String providerName, int syncable) {
584         if (TextUtils.isEmpty(providerName)) {
585             throw new IllegalArgumentException("Authority must not be empty");
586         }
587         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
588                 "no permission to write the sync settings");
589
590         int userId = UserHandle.getCallingUserId();
591         long identityToken = clearCallingIdentity();
592         try {
593             SyncManager syncManager = getSyncManager();
594             if (syncManager != null) {
595                 syncManager.getSyncStorageEngine().setIsSyncable(
596                         account, userId, providerName, syncable);
597             }
598         } finally {
599             restoreCallingIdentity(identityToken);
600         }
601     }
602
603     @Override
604     public boolean getMasterSyncAutomatically() {
605         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
606                 "no permission to read the sync settings");
607
608         int userId = UserHandle.getCallingUserId();
609         long identityToken = clearCallingIdentity();
610         try {
611             SyncManager syncManager = getSyncManager();
612             if (syncManager != null) {
613                 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
614             }
615         } finally {
616             restoreCallingIdentity(identityToken);
617         }
618         return false;
619     }
620
621     @Override
622     public void setMasterSyncAutomatically(boolean flag) {
623         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
624                 "no permission to write the sync settings");
625
626         int userId = UserHandle.getCallingUserId();
627         long identityToken = clearCallingIdentity();
628         try {
629             SyncManager syncManager = getSyncManager();
630             if (syncManager != null) {
631                 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
632             }
633         } finally {
634             restoreCallingIdentity(identityToken);
635         }
636     }
637
638     public boolean isSyncActive(Account account, String authority) {
639         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
640                 "no permission to read the sync stats");
641         int userId = UserHandle.getCallingUserId();
642
643         long identityToken = clearCallingIdentity();
644         try {
645             SyncManager syncManager = getSyncManager();
646             if (syncManager != null) {
647                 return syncManager.getSyncStorageEngine().isSyncActive(
648                         account, userId, authority);
649             }
650         } finally {
651             restoreCallingIdentity(identityToken);
652         }
653         return false;
654     }
655
656     public List<SyncInfo> getCurrentSyncs() {
657         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
658                 "no permission to read the sync stats");
659
660         int userId = UserHandle.getCallingUserId();
661         long identityToken = clearCallingIdentity();
662         try {
663             return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId);
664         } finally {
665             restoreCallingIdentity(identityToken);
666         }
667     }
668
669     public SyncStatusInfo getSyncStatus(Account account, String authority) {
670         if (TextUtils.isEmpty(authority)) {
671             throw new IllegalArgumentException("Authority must not be empty");
672         }
673         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
674                 "no permission to read the sync stats");
675
676         int userId = UserHandle.getCallingUserId();
677         long identityToken = clearCallingIdentity();
678         try {
679             SyncManager syncManager = getSyncManager();
680             if (syncManager != null) {
681                 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
682                         account, userId, authority);
683             }
684         } finally {
685             restoreCallingIdentity(identityToken);
686         }
687         return null;
688     }
689
690     public boolean isSyncPending(Account account, String authority) {
691         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
692                 "no permission to read the sync stats");
693
694         int userId = UserHandle.getCallingUserId();
695         long identityToken = clearCallingIdentity();
696         try {
697             SyncManager syncManager = getSyncManager();
698             if (syncManager != null) {
699                 return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority);
700             }
701         } finally {
702             restoreCallingIdentity(identityToken);
703         }
704         return false;
705     }
706
707     public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
708         long identityToken = clearCallingIdentity();
709         try {
710             SyncManager syncManager = getSyncManager();
711             if (syncManager != null && callback != null) {
712                 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
713             }
714         } finally {
715             restoreCallingIdentity(identityToken);
716         }
717     }
718
719     public void removeStatusChangeListener(ISyncStatusObserver callback) {
720         long identityToken = clearCallingIdentity();
721         try {
722             SyncManager syncManager = getSyncManager();
723             if (syncManager != null && callback != null) {
724                 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
725             }
726         } finally {
727             restoreCallingIdentity(identityToken);
728         }
729     }
730
731     public static ContentService main(Context context, boolean factoryTest) {
732         ContentService service = new ContentService(context, factoryTest);
733         ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
734         return service;
735     }
736
737     /**
738      * Hide this class since it is not part of api,
739      * but current unittest framework requires it to be public
740      * @hide
741      */
742     public static final class ObserverNode {
743         private class ObserverEntry implements IBinder.DeathRecipient {
744             public final IContentObserver observer;
745             public final int uid;
746             public final int pid;
747             public final boolean notifyForDescendants;
748             private final int userHandle;
749             private final Object observersLock;
750
751             public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
752                     int _uid, int _pid, int _userHandle) {
753                 this.observersLock = observersLock;
754                 observer = o;
755                 uid = _uid;
756                 pid = _pid;
757                 userHandle = _userHandle;
758                 notifyForDescendants = n;
759                 try {
760                     observer.asBinder().linkToDeath(this, 0);
761                 } catch (RemoteException e) {
762                     binderDied();
763                 }
764             }
765
766             public void binderDied() {
767                 synchronized (observersLock) {
768                     removeObserverLocked(observer);
769                 }
770             }
771
772             public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
773                     String name, String prefix, SparseIntArray pidCounts) {
774                 pidCounts.put(pid, pidCounts.get(pid)+1);
775                 pw.print(prefix); pw.print(name); pw.print(": pid=");
776                         pw.print(pid); pw.print(" uid=");
777                         pw.print(uid); pw.print(" user=");
778                         pw.print(userHandle); pw.print(" target=");
779                         pw.println(Integer.toHexString(System.identityHashCode(
780                                 observer != null ? observer.asBinder() : null)));
781             }
782         }
783
784         public static final int INSERT_TYPE = 0;
785         public static final int UPDATE_TYPE = 1;
786         public static final int DELETE_TYPE = 2;
787
788         private String mName;
789         private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
790         private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
791
792         public ObserverNode(String name) {
793             mName = name;
794         }
795
796         public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
797                 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
798             String innerName = null;
799             if (mObservers.size() > 0) {
800                 if ("".equals(name)) {
801                     innerName = mName;
802                 } else {
803                     innerName = name + "/" + mName;
804                 }
805                 for (int i=0; i<mObservers.size(); i++) {
806                     counts[1]++;
807                     mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
808                             pidCounts);
809                 }
810             }
811             if (mChildren.size() > 0) {
812                 if (innerName == null) {
813                     if ("".equals(name)) {
814                         innerName = mName;
815                     } else {
816                         innerName = name + "/" + mName;
817                     }
818                 }
819                 for (int i=0; i<mChildren.size(); i++) {
820                     counts[0]++;
821                     mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
822                             counts, pidCounts);
823                 }
824             }
825         }
826
827         private String getUriSegment(Uri uri, int index) {
828             if (uri != null) {
829                 if (index == 0) {
830                     return uri.getAuthority();
831                 } else {
832                     return uri.getPathSegments().get(index - 1);
833                 }
834             } else {
835                 return null;
836             }
837         }
838
839         private int countUriSegments(Uri uri) {
840             if (uri == null) {
841                 return 0;
842             }
843             return uri.getPathSegments().size() + 1;
844         }
845
846         // Invariant:  userHandle is either a hard user number or is USER_ALL
847         public void addObserverLocked(Uri uri, IContentObserver observer,
848                 boolean notifyForDescendants, Object observersLock,
849                 int uid, int pid, int userHandle) {
850             addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
851                     uid, pid, userHandle);
852         }
853
854         private void addObserverLocked(Uri uri, int index, IContentObserver observer,
855                 boolean notifyForDescendants, Object observersLock,
856                 int uid, int pid, int userHandle) {
857             // If this is the leaf node add the observer
858             if (index == countUriSegments(uri)) {
859                 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
860                         uid, pid, userHandle));
861                 return;
862             }
863
864             // Look to see if the proper child already exists
865             String segment = getUriSegment(uri, index);
866             if (segment == null) {
867                 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
868             }
869             int N = mChildren.size();
870             for (int i = 0; i < N; i++) {
871                 ObserverNode node = mChildren.get(i);
872                 if (node.mName.equals(segment)) {
873                     node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
874                             observersLock, uid, pid, userHandle);
875                     return;
876                 }
877             }
878
879             // No child found, create one
880             ObserverNode node = new ObserverNode(segment);
881             mChildren.add(node);
882             node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
883                     observersLock, uid, pid, userHandle);
884         }
885
886         public boolean removeObserverLocked(IContentObserver observer) {
887             int size = mChildren.size();
888             for (int i = 0; i < size; i++) {
889                 boolean empty = mChildren.get(i).removeObserverLocked(observer);
890                 if (empty) {
891                     mChildren.remove(i);
892                     i--;
893                     size--;
894                 }
895             }
896
897             IBinder observerBinder = observer.asBinder();
898             size = mObservers.size();
899             for (int i = 0; i < size; i++) {
900                 ObserverEntry entry = mObservers.get(i);
901                 if (entry.observer.asBinder() == observerBinder) {
902                     mObservers.remove(i);
903                     // We no longer need to listen for death notifications. Remove it.
904                     observerBinder.unlinkToDeath(entry, 0);
905                     break;
906                 }
907             }
908
909             if (mChildren.size() == 0 && mObservers.size() == 0) {
910                 return true;
911             }
912             return false;
913         }
914
915         private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
916                 boolean observerWantsSelfNotifications, int targetUserHandle,
917                 ArrayList<ObserverCall> calls) {
918             int N = mObservers.size();
919             IBinder observerBinder = observer == null ? null : observer.asBinder();
920             for (int i = 0; i < N; i++) {
921                 ObserverEntry entry = mObservers.get(i);
922
923                 // Don't notify the observer if it sent the notification and isn't interested
924                 // in self notifications
925                 boolean selfChange = (entry.observer.asBinder() == observerBinder);
926                 if (selfChange && !observerWantsSelfNotifications) {
927                     continue;
928                 }
929
930                 // Does this observer match the target user?
931                 if (targetUserHandle == UserHandle.USER_ALL
932                         || entry.userHandle == UserHandle.USER_ALL
933                         || targetUserHandle == entry.userHandle) {
934                     // Make sure the observer is interested in the notification
935                     if (leaf || (!leaf && entry.notifyForDescendants)) {
936                         calls.add(new ObserverCall(this, entry.observer, selfChange));
937                     }
938                 }
939             }
940         }
941
942         /**
943          * targetUserHandle is either a hard user handle or is USER_ALL
944          */
945         public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
946                 boolean observerWantsSelfNotifications, int targetUserHandle,
947                 ArrayList<ObserverCall> calls) {
948             String segment = null;
949             int segmentCount = countUriSegments(uri);
950             if (index >= segmentCount) {
951                 // This is the leaf node, notify all observers
952                 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
953                         targetUserHandle, calls);
954             } else if (index < segmentCount){
955                 segment = getUriSegment(uri, index);
956                 // Notify any observers at this level who are interested in descendants
957                 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
958                         targetUserHandle, calls);
959             }
960
961             int N = mChildren.size();
962             for (int i = 0; i < N; i++) {
963                 ObserverNode node = mChildren.get(i);
964                 if (segment == null || node.mName.equals(segment)) {
965                     // We found the child,
966                     node.collectObserversLocked(uri, index + 1,
967                             observer, observerWantsSelfNotifications, targetUserHandle, calls);
968                     if (segment != null) {
969                         break;
970                     }
971                 }
972             }
973         }
974     }
975 }