blob: f82cf0110227490018cd5e94075abe848b0d71cb [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.content;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Jeff Sharkey7a96c392012-11-15 14:01:46 -080019import android.Manifest;
Dianne Hackborn7a135592009-05-06 00:28:37 -070020import android.accounts.Account;
Christopher Tate16aa9732012-09-17 16:23:44 -070021import android.app.ActivityManager;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080022import android.content.ContentResolver;
23import android.content.Context;
24import android.content.IContentService;
25import android.content.ISyncStatusObserver;
26import android.content.PeriodicSync;
27import android.content.SyncAdapterType;
28import android.content.SyncInfo;
29import android.content.SyncStatusInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.database.IContentObserver;
31import android.database.sqlite.SQLiteException;
32import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070033import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.Bundle;
35import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070036import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.os.RemoteException;
38import android.os.ServiceManager;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070039import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.util.Log;
Jeff Sharkey60094592013-03-21 18:14:24 -070041import android.util.Slog;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070042import android.util.SparseIntArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
44import java.io.FileDescriptor;
45import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070046import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070048import java.util.Collections;
49import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080050import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
52/**
53 * {@hide}
54 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070055public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private static final String TAG = "ContentService";
57 private Context mContext;
58 private boolean mFactoryTest;
59 private final ObserverNode mRootNode = new ObserverNode("");
60 private SyncManager mSyncManager = null;
61 private final Object mSyncManagerLock = new Object();
62
63 private SyncManager getSyncManager() {
64 synchronized(mSyncManagerLock) {
65 try {
66 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
67 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
68 } catch (SQLiteException e) {
69 Log.e(TAG, "Can't create SyncManager", e);
70 }
71 return mSyncManager;
72 }
73 }
74
75 @Override
76 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
77 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
78 "caller doesn't have the DUMP permission");
79
80 // This makes it so that future permission checks will be in the context of this
81 // process rather than the caller's process. We will restore this before returning.
82 long identityToken = clearCallingIdentity();
83 try {
84 if (mSyncManager == null) {
85 pw.println("No SyncManager created! (Disk full?)");
86 } else {
87 mSyncManager.dump(fd, pw);
88 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070089 pw.println();
90 pw.println("Observer tree:");
91 synchronized (mRootNode) {
92 int[] counts = new int[2];
93 final SparseIntArray pidCounts = new SparseIntArray();
94 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
95 pw.println();
96 ArrayList<Integer> sorted = new ArrayList<Integer>();
97 for (int i=0; i<pidCounts.size(); i++) {
98 sorted.add(pidCounts.keyAt(i));
99 }
100 Collections.sort(sorted, new Comparator<Integer>() {
101 @Override
102 public int compare(Integer lhs, Integer rhs) {
103 int lc = pidCounts.get(lhs);
104 int rc = pidCounts.get(rhs);
105 if (lc < rc) {
106 return 1;
107 } else if (lc > rc) {
108 return -1;
109 }
110 return 0;
111 }
112
113 });
114 for (int i=0; i<sorted.size(); i++) {
115 int pid = sorted.get(i);
116 pw.print(" pid "); pw.print(pid); pw.print(": ");
117 pw.print(pidCounts.get(pid)); pw.println(" observers");
118 }
119 pw.println();
120 pw.print(" Total number of nodes: "); pw.println(counts[0]);
121 pw.print(" Total number of observers: "); pw.println(counts[1]);
122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 } finally {
124 restoreCallingIdentity(identityToken);
125 }
126 }
127
Dianne Hackborn231cc602009-04-27 17:10:36 -0700128 @Override
129 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
130 throws RemoteException {
131 try {
132 return super.onTransact(code, data, reply, flags);
133 } catch (RuntimeException e) {
134 // The content service only throws security exceptions, so let's
135 // log all others.
136 if (!(e instanceof SecurityException)) {
137 Log.e(TAG, "Content Service Crash", e);
138 }
139 throw e;
140 }
141 }
142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 /*package*/ ContentService(Context context, boolean factoryTest) {
144 mContext = context;
145 mFactoryTest = factoryTest;
Kenny Root26ff6622012-07-30 12:58:03 -0700146 }
147
148 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 getSyncManager();
150 }
151
Christopher Tate16aa9732012-09-17 16:23:44 -0700152 /**
153 * Register a content observer tied to a specific user's view of the provider.
154 * @param userHandle the user whose view of the provider is to be observed. May be
155 * the calling user without requiring any permission, otherwise the caller needs to
156 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
157 * USER_CURRENT are properly handled; all other pseudousers are forbidden.
158 */
159 @Override
160 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
161 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 if (observer == null || uri == null) {
163 throw new IllegalArgumentException("You must pass a valid uri and observer");
164 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700165
166 final int callingUser = UserHandle.getCallingUserId();
167 if (callingUser != userHandle) {
168 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
169 "no permission to observe other users' provider view");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700171
172 if (userHandle < 0) {
173 if (userHandle == UserHandle.USER_CURRENT) {
174 userHandle = ActivityManager.getCurrentUser();
175 } else if (userHandle != UserHandle.USER_ALL) {
176 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
177 + userHandle);
178 }
179 }
180
181 synchronized (mRootNode) {
182 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
183 Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
184 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
185 " with notifyForDescendants " + notifyForDescendants);
186 }
187 }
188
189 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
190 IContentObserver observer) {
191 registerContentObserver(uri, notifyForDescendants, observer,
192 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
194
195 public void unregisterContentObserver(IContentObserver observer) {
196 if (observer == null) {
197 throw new IllegalArgumentException("You must pass a valid observer");
198 }
199 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800200 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700201 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203 }
204
Christopher Tate16aa9732012-09-17 16:23:44 -0700205 /**
206 * Notify observers of a particular user's view of the provider.
207 * @param userHandle the user whose view of the provider is to be notified. May be
208 * the calling user without requiring any permission, otherwise the caller needs to
209 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
210 * USER_CURRENT are properly interpreted; no other pseudousers are allowed.
211 */
212 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 public void notifyChange(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700214 boolean observerWantsSelfNotifications, boolean syncToNetwork,
215 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700217 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
218 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800220
Christopher Tate16aa9732012-09-17 16:23:44 -0700221 // Notify for any user other than the caller's own requires permission.
222 final int callingUserHandle = UserHandle.getCallingUserId();
223 if (userHandle != callingUserHandle) {
224 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
225 "no permission to notify other users");
226 }
227
228 // We passed the permission check; resolve pseudouser targets as appropriate
229 if (userHandle < 0) {
230 if (userHandle == UserHandle.USER_CURRENT) {
231 userHandle = ActivityManager.getCurrentUser();
232 } else if (userHandle != UserHandle.USER_ALL) {
233 throw new InvalidParameterException("Bad user handle for notifyChange: "
234 + userHandle);
235 }
236 }
237
Alon Albert57286f92012-10-09 14:21:38 -0700238 final int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 // This makes it so that future permission checks will be in the context of this
240 // process rather than the caller's process. We will restore this before returning.
241 long identityToken = clearCallingIdentity();
242 try {
243 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
244 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800245 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700246 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 }
248 final int numCalls = calls.size();
249 for (int i=0; i<numCalls; i++) {
250 ObserverCall oc = calls.get(i);
251 try {
Jeff Brown655e66b2012-01-23 15:51:41 -0800252 oc.mObserver.onChange(oc.mSelfChange, uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 if (Log.isLoggable(TAG, Log.VERBOSE)) {
254 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
255 }
256 } catch (RemoteException ex) {
257 synchronized (mRootNode) {
258 Log.w(TAG, "Found dead observer, removing");
259 IBinder binder = oc.mObserver.asBinder();
260 final ArrayList<ObserverNode.ObserverEntry> list
261 = oc.mNode.mObservers;
262 int numList = list.size();
263 for (int j=0; j<numList; j++) {
264 ObserverNode.ObserverEntry oe = list.get(j);
265 if (oe.observer.asBinder() == binder) {
266 list.remove(j);
267 j--;
268 numList--;
269 }
270 }
271 }
272 }
273 }
274 if (syncToNetwork) {
275 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700276 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700277 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800278 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 }
281 } finally {
282 restoreCallingIdentity(identityToken);
283 }
284 }
285
Christopher Tate16aa9732012-09-17 16:23:44 -0700286 public void notifyChange(Uri uri, IContentObserver observer,
287 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
288 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
289 UserHandle.getCallingUserId());
290 }
291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 /**
293 * Hide this class since it is not part of api,
294 * but current unittest framework requires it to be public
295 * @hide
296 *
297 */
298 public static final class ObserverCall {
299 final ObserverNode mNode;
300 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800301 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302
Jeff Brown86de0592012-01-23 13:01:18 -0800303 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 mNode = node;
305 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800306 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 }
308 }
309
Fred Quintanaac9385e2009-06-22 18:00:59 -0700310 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700312 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700313 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800314
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 // This makes it so that future permission checks will be in the context of this
316 // process rather than the caller's process. We will restore this before returning.
317 long identityToken = clearCallingIdentity();
318 try {
319 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700320 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700321 syncManager.scheduleSync(account, userId, uId, authority, extras, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700322 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 } finally {
325 restoreCallingIdentity(identityToken);
326 }
327 }
328
329 /**
330 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700331 * if they match the authority and account, if they are present.
332 * @param account filter the pending and active syncs to cancel using this account
333 * @param authority filter the pending and active syncs to cancel using this authority
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700335 public void cancelSync(Account account, String authority) {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700336 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800337
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 // This makes it so that future permission checks will be in the context of this
339 // process rather than the caller's process. We will restore this before returning.
340 long identityToken = clearCallingIdentity();
341 try {
342 SyncManager syncManager = getSyncManager();
343 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800344 syncManager.clearScheduledSyncOperations(account, userId, authority);
345 syncManager.cancelActiveSync(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
347 } finally {
348 restoreCallingIdentity(identityToken);
349 }
350 }
351
Fred Quintanaac9385e2009-06-22 18:00:59 -0700352 /**
353 * Get information about the SyncAdapters that are known to the system.
354 * @return an array of SyncAdapters that have registered with the system
355 */
356 public SyncAdapterType[] getSyncAdapterTypes() {
357 // This makes it so that future permission checks will be in the context of this
358 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700359 final int userId = UserHandle.getCallingUserId();
360 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700361 try {
362 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700363 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700364 } finally {
365 restoreCallingIdentity(identityToken);
366 }
367 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700368
Fred Quintanaac9385e2009-06-22 18:00:59 -0700369 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700370 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
371 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700372 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800373
Dianne Hackborn231cc602009-04-27 17:10:36 -0700374 long identityToken = clearCallingIdentity();
375 try {
376 SyncManager syncManager = getSyncManager();
377 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700378 return syncManager.getSyncStorageEngine().getSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800379 account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700380 }
381 } finally {
382 restoreCallingIdentity(identityToken);
383 }
384 return false;
385 }
386
Fred Quintanaac9385e2009-06-22 18:00:59 -0700387 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700388 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
389 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700390 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800391
Dianne Hackborn231cc602009-04-27 17:10:36 -0700392 long identityToken = clearCallingIdentity();
393 try {
394 SyncManager syncManager = getSyncManager();
395 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700396 syncManager.getSyncStorageEngine().setSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800397 account, userId, providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700398 }
399 } finally {
400 restoreCallingIdentity(identityToken);
401 }
402 }
403
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800404 public void addPeriodicSync(Account account, String authority, Bundle extras,
405 long pollFrequency) {
406 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
407 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700408 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800409
Jeff Sharkey51366be2013-03-27 14:46:55 -0700410 if (pollFrequency < 60) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700411 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Jeff Sharkey51366be2013-03-27 14:46:55 -0700412 + " seconds being rounded up to 60 seconds.");
413 pollFrequency = 60;
Jeff Sharkey60094592013-03-21 18:14:24 -0700414 }
415
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800416 long identityToken = clearCallingIdentity();
417 try {
418 getSyncManager().getSyncStorageEngine().addPeriodicSync(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800419 account, userId, authority, extras, pollFrequency);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800420 } finally {
421 restoreCallingIdentity(identityToken);
422 }
423 }
424
425 public void removePeriodicSync(Account account, String authority, Bundle extras) {
426 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
427 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700428 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800429
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800430 long identityToken = clearCallingIdentity();
431 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800432 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority,
433 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800434 } finally {
435 restoreCallingIdentity(identityToken);
436 }
437 }
438
439 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
440 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
441 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700442 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800443
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800444 long identityToken = clearCallingIdentity();
445 try {
446 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800447 account, userId, providerName);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800448 } finally {
449 restoreCallingIdentity(identityToken);
450 }
451 }
452
Fred Quintana5e787c42009-08-16 23:13:53 -0700453 public int getIsSyncable(Account account, String providerName) {
454 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
455 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700456 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800457
Fred Quintana5e787c42009-08-16 23:13:53 -0700458 long identityToken = clearCallingIdentity();
459 try {
460 SyncManager syncManager = getSyncManager();
461 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700462 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800463 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700464 }
465 } finally {
466 restoreCallingIdentity(identityToken);
467 }
468 return -1;
469 }
470
471 public void setIsSyncable(Account account, String providerName, int syncable) {
472 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
473 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700474 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800475
Fred Quintana5e787c42009-08-16 23:13:53 -0700476 long identityToken = clearCallingIdentity();
477 try {
478 SyncManager syncManager = getSyncManager();
479 if (syncManager != null) {
480 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800481 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700482 }
483 } finally {
484 restoreCallingIdentity(identityToken);
485 }
486 }
487
Fred Quintanaac9385e2009-06-22 18:00:59 -0700488 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700489 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
490 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700491 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800492
Dianne Hackborn231cc602009-04-27 17:10:36 -0700493 long identityToken = clearCallingIdentity();
494 try {
495 SyncManager syncManager = getSyncManager();
496 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800497 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700498 }
499 } finally {
500 restoreCallingIdentity(identityToken);
501 }
502 return false;
503 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700504
Fred Quintanaac9385e2009-06-22 18:00:59 -0700505 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700506 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
507 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700508 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800509
Dianne Hackborn231cc602009-04-27 17:10:36 -0700510 long identityToken = clearCallingIdentity();
511 try {
512 SyncManager syncManager = getSyncManager();
513 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800514 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700515 }
516 } finally {
517 restoreCallingIdentity(identityToken);
518 }
519 }
520
Dianne Hackborn7a135592009-05-06 00:28:37 -0700521 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700522 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
523 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700524 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800525
Dianne Hackborn231cc602009-04-27 17:10:36 -0700526 long identityToken = clearCallingIdentity();
527 try {
528 SyncManager syncManager = getSyncManager();
529 if (syncManager != null) {
530 return syncManager.getSyncStorageEngine().isSyncActive(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800531 account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700532 }
533 } finally {
534 restoreCallingIdentity(identityToken);
535 }
536 return false;
537 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700538
Fred Quintanac6a69552010-09-27 17:05:04 -0700539 public List<SyncInfo> getCurrentSyncs() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700540 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
541 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700542 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800543
Dianne Hackborn231cc602009-04-27 17:10:36 -0700544 long identityToken = clearCallingIdentity();
545 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800546 return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700547 } finally {
548 restoreCallingIdentity(identityToken);
549 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700550 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700551
Fred Quintanaac9385e2009-06-22 18:00:59 -0700552 public SyncStatusInfo getSyncStatus(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700553 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
554 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700555 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800556
Dianne Hackborn231cc602009-04-27 17:10:36 -0700557 long identityToken = clearCallingIdentity();
558 try {
559 SyncManager syncManager = getSyncManager();
560 if (syncManager != null) {
Costin Manolacheb7520982009-09-02 18:03:05 -0700561 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800562 account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700563 }
564 } finally {
565 restoreCallingIdentity(identityToken);
566 }
567 return null;
568 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700569
Fred Quintanaac9385e2009-06-22 18:00:59 -0700570 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700571 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
572 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700573 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800574
Dianne Hackborn231cc602009-04-27 17:10:36 -0700575 long identityToken = clearCallingIdentity();
576 try {
577 SyncManager syncManager = getSyncManager();
578 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800579 return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700580 }
581 } finally {
582 restoreCallingIdentity(identityToken);
583 }
584 return false;
585 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700586
Dianne Hackborn231cc602009-04-27 17:10:36 -0700587 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
588 long identityToken = clearCallingIdentity();
589 try {
590 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800591 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700592 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700593 }
594 } finally {
595 restoreCallingIdentity(identityToken);
596 }
597 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700598
Dianne Hackborn231cc602009-04-27 17:10:36 -0700599 public void removeStatusChangeListener(ISyncStatusObserver callback) {
600 long identityToken = clearCallingIdentity();
601 try {
602 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800603 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700604 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700605 }
606 } finally {
607 restoreCallingIdentity(identityToken);
608 }
609 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700610
Kenny Root26ff6622012-07-30 12:58:03 -0700611 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700613 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 return service;
615 }
616
617 /**
618 * Hide this class since it is not part of api,
619 * but current unittest framework requires it to be public
620 * @hide
621 */
622 public static final class ObserverNode {
623 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800624 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700625 public final int uid;
626 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700627 public final boolean notifyForDescendants;
628 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -0800629 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700631 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Christopher Tate16aa9732012-09-17 16:23:44 -0700632 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800633 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700635 uid = _uid;
636 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700637 userHandle = _userHandle;
638 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 try {
640 observer.asBinder().linkToDeath(this, 0);
641 } catch (RemoteException e) {
642 binderDied();
643 }
644 }
645
646 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800647 synchronized (observersLock) {
648 removeObserverLocked(observer);
649 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700651
652 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
653 String name, String prefix, SparseIntArray pidCounts) {
654 pidCounts.put(pid, pidCounts.get(pid)+1);
655 pw.print(prefix); pw.print(name); pw.print(": pid=");
656 pw.print(pid); pw.print(" uid=");
Christopher Tate16aa9732012-09-17 16:23:44 -0700657 pw.print(uid); pw.print(" user=");
658 pw.print(userHandle); pw.print(" target=");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700659 pw.println(Integer.toHexString(System.identityHashCode(
660 observer != null ? observer.asBinder() : null)));
661 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 }
663
664 public static final int INSERT_TYPE = 0;
665 public static final int UPDATE_TYPE = 1;
666 public static final int DELETE_TYPE = 2;
667
668 private String mName;
669 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
670 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
671
672 public ObserverNode(String name) {
673 mName = name;
674 }
675
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700676 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
677 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
678 String innerName = null;
679 if (mObservers.size() > 0) {
680 if ("".equals(name)) {
681 innerName = mName;
682 } else {
683 innerName = name + "/" + mName;
684 }
685 for (int i=0; i<mObservers.size(); i++) {
686 counts[1]++;
687 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
688 pidCounts);
689 }
690 }
691 if (mChildren.size() > 0) {
692 if (innerName == null) {
693 if ("".equals(name)) {
694 innerName = mName;
695 } else {
696 innerName = name + "/" + mName;
697 }
698 }
699 for (int i=0; i<mChildren.size(); i++) {
700 counts[0]++;
701 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
702 counts, pidCounts);
703 }
704 }
705 }
706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 private String getUriSegment(Uri uri, int index) {
708 if (uri != null) {
709 if (index == 0) {
710 return uri.getAuthority();
711 } else {
712 return uri.getPathSegments().get(index - 1);
713 }
714 } else {
715 return null;
716 }
717 }
718
719 private int countUriSegments(Uri uri) {
720 if (uri == null) {
721 return 0;
722 }
723 return uri.getPathSegments().size() + 1;
724 }
725
Christopher Tate16aa9732012-09-17 16:23:44 -0700726 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -0800727 public void addObserverLocked(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700728 boolean notifyForDescendants, Object observersLock,
729 int uid, int pid, int userHandle) {
730 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
731 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 }
733
Fred Quintana002ffad52010-03-02 11:18:16 -0800734 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700735 boolean notifyForDescendants, Object observersLock,
736 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 // If this is the leaf node add the observer
738 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700739 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
740 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 return;
742 }
743
744 // Look to see if the proper child already exists
745 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -0500746 if (segment == null) {
747 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
748 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 int N = mChildren.size();
750 for (int i = 0; i < N; i++) {
751 ObserverNode node = mChildren.get(i);
752 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700753 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
754 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 return;
756 }
757 }
758
759 // No child found, create one
760 ObserverNode node = new ObserverNode(segment);
761 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -0700762 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
763 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 }
765
Fred Quintana002ffad52010-03-02 11:18:16 -0800766 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 int size = mChildren.size();
768 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800769 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 if (empty) {
771 mChildren.remove(i);
772 i--;
773 size--;
774 }
775 }
776
777 IBinder observerBinder = observer.asBinder();
778 size = mObservers.size();
779 for (int i = 0; i < size; i++) {
780 ObserverEntry entry = mObservers.get(i);
781 if (entry.observer.asBinder() == observerBinder) {
782 mObservers.remove(i);
783 // We no longer need to listen for death notifications. Remove it.
784 observerBinder.unlinkToDeath(entry, 0);
785 break;
786 }
787 }
788
789 if (mChildren.size() == 0 && mObservers.size() == 0) {
790 return true;
791 }
792 return false;
793 }
794
Fred Quintana002ffad52010-03-02 11:18:16 -0800795 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700796 boolean observerWantsSelfNotifications, int targetUserHandle,
797 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 int N = mObservers.size();
799 IBinder observerBinder = observer == null ? null : observer.asBinder();
800 for (int i = 0; i < N; i++) {
801 ObserverEntry entry = mObservers.get(i);
802
Christopher Tate16aa9732012-09-17 16:23:44 -0700803 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -0800805 boolean selfChange = (entry.observer.asBinder() == observerBinder);
806 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 continue;
808 }
809
Christopher Tate16aa9732012-09-17 16:23:44 -0700810 // Does this observer match the target user?
811 if (targetUserHandle == UserHandle.USER_ALL
812 || entry.userHandle == UserHandle.USER_ALL
813 || targetUserHandle == entry.userHandle) {
814 // Make sure the observer is interested in the notification
815 if (leaf || (!leaf && entry.notifyForDescendants)) {
816 calls.add(new ObserverCall(this, entry.observer, selfChange));
817 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
819 }
820 }
821
Christopher Tate16aa9732012-09-17 16:23:44 -0700822 /**
823 * targetUserHandle is either a hard user handle or is USER_ALL
824 */
Fred Quintana002ffad52010-03-02 11:18:16 -0800825 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700826 boolean observerWantsSelfNotifications, int targetUserHandle,
827 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 String segment = null;
829 int segmentCount = countUriSegments(uri);
830 if (index >= segmentCount) {
831 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -0700832 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
833 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 } else if (index < segmentCount){
835 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -0700836 // Notify any observers at this level who are interested in descendants
837 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
838 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 }
840
841 int N = mChildren.size();
842 for (int i = 0; i < N; i++) {
843 ObserverNode node = mChildren.get(i);
844 if (segment == null || node.mName.equals(segment)) {
845 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -0800846 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -0700847 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 if (segment != null) {
849 break;
850 }
851 }
852 }
853 }
854 }
855}