OSDN Git Service

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