ContentService.java revision 9158825f9c41869689d6b1786d7c7aa8bdd524ce
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
17package com.android.server.content;
18
19import android.Manifest;
20import android.accounts.Account;
21import android.app.ActivityManager;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.IContentService;
25import android.content.ISyncStatusObserver;
26import android.content.PeriodicSync;
27import android.content.SyncAdapterType;
28import android.content.SyncInfo;
29import android.content.SyncRequest;
30import android.content.SyncStatusInfo;
31import android.database.IContentObserver;
32import android.database.sqlite.SQLiteException;
33import android.net.Uri;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.IBinder;
37import android.os.Parcel;
38import android.os.RemoteException;
39import android.os.ServiceManager;
40import android.os.SystemProperties;
41import android.os.UserHandle;
42import android.text.TextUtils;
43import android.util.Log;
44import android.util.Pair;
45import android.util.Slog;
46import android.util.SparseIntArray;
47
48import java.io.FileDescriptor;
49import java.io.PrintWriter;
50import java.security.InvalidParameterException;
51import java.util.ArrayList;
52import java.util.Collections;
53import java.util.Comparator;
54import java.util.List;
55
56/**
57 * {@hide}
58 */
59public 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}
976