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