blob: 4a5c0d5d3bbe23604bdd5962710cb1189e04f49b [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;
Dan Morrille4d9a012013-03-28 18:10:43 -070039import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070040import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.util.Log;
Jeff Sharkey60094592013-03-21 18:14:24 -070042import android.util.Slog;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070043import android.util.SparseIntArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070047import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070049import java.util.Collections;
50import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080051import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53/**
54 * {@hide}
55 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070056public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 private static final String TAG = "ContentService";
58 private Context mContext;
59 private boolean mFactoryTest;
60 private final ObserverNode mRootNode = new ObserverNode("");
61 private SyncManager mSyncManager = null;
62 private final Object mSyncManagerLock = new Object();
63
64 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -070065 if (SystemProperties.getBoolean("config.disable_network", false)) {
66 return null;
67 }
68
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 synchronized(mSyncManagerLock) {
70 try {
71 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
72 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
73 } catch (SQLiteException e) {
74 Log.e(TAG, "Can't create SyncManager", e);
75 }
76 return mSyncManager;
77 }
78 }
79
80 @Override
81 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
82 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
83 "caller doesn't have the DUMP permission");
84
85 // This makes it so that future permission checks will be in the context of this
86 // process rather than the caller's process. We will restore this before returning.
87 long identityToken = clearCallingIdentity();
88 try {
89 if (mSyncManager == null) {
90 pw.println("No SyncManager created! (Disk full?)");
91 } else {
92 mSyncManager.dump(fd, pw);
93 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070094 pw.println();
95 pw.println("Observer tree:");
96 synchronized (mRootNode) {
97 int[] counts = new int[2];
98 final SparseIntArray pidCounts = new SparseIntArray();
99 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
100 pw.println();
101 ArrayList<Integer> sorted = new ArrayList<Integer>();
102 for (int i=0; i<pidCounts.size(); i++) {
103 sorted.add(pidCounts.keyAt(i));
104 }
105 Collections.sort(sorted, new Comparator<Integer>() {
106 @Override
107 public int compare(Integer lhs, Integer rhs) {
108 int lc = pidCounts.get(lhs);
109 int rc = pidCounts.get(rhs);
110 if (lc < rc) {
111 return 1;
112 } else if (lc > rc) {
113 return -1;
114 }
115 return 0;
116 }
117
118 });
119 for (int i=0; i<sorted.size(); i++) {
120 int pid = sorted.get(i);
121 pw.print(" pid "); pw.print(pid); pw.print(": ");
122 pw.print(pidCounts.get(pid)); pw.println(" observers");
123 }
124 pw.println();
125 pw.print(" Total number of nodes: "); pw.println(counts[0]);
126 pw.print(" Total number of observers: "); pw.println(counts[1]);
127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 } finally {
129 restoreCallingIdentity(identityToken);
130 }
131 }
132
Dianne Hackborn231cc602009-04-27 17:10:36 -0700133 @Override
134 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
135 throws RemoteException {
136 try {
137 return super.onTransact(code, data, reply, flags);
138 } catch (RuntimeException e) {
139 // The content service only throws security exceptions, so let's
140 // log all others.
141 if (!(e instanceof SecurityException)) {
142 Log.e(TAG, "Content Service Crash", e);
143 }
144 throw e;
145 }
146 }
147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 /*package*/ ContentService(Context context, boolean factoryTest) {
149 mContext = context;
150 mFactoryTest = factoryTest;
Kenny Root26ff6622012-07-30 12:58:03 -0700151 }
152
153 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 getSyncManager();
155 }
156
Christopher Tate16aa9732012-09-17 16:23:44 -0700157 /**
158 * Register a content observer tied to a specific user's view of the provider.
159 * @param userHandle the user whose view of the provider is to be observed. May be
160 * the calling user without requiring any permission, otherwise the caller needs to
161 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
162 * USER_CURRENT are properly handled; all other pseudousers are forbidden.
163 */
164 @Override
165 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
166 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 if (observer == null || uri == null) {
168 throw new IllegalArgumentException("You must pass a valid uri and observer");
169 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700170
171 final int callingUser = UserHandle.getCallingUserId();
172 if (callingUser != userHandle) {
173 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
174 "no permission to observe other users' provider view");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700176
177 if (userHandle < 0) {
178 if (userHandle == UserHandle.USER_CURRENT) {
179 userHandle = ActivityManager.getCurrentUser();
180 } else if (userHandle != UserHandle.USER_ALL) {
181 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
182 + userHandle);
183 }
184 }
185
186 synchronized (mRootNode) {
187 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
188 Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
189 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
190 " with notifyForDescendants " + notifyForDescendants);
191 }
192 }
193
194 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
195 IContentObserver observer) {
196 registerContentObserver(uri, notifyForDescendants, observer,
197 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 }
199
200 public void unregisterContentObserver(IContentObserver observer) {
201 if (observer == null) {
202 throw new IllegalArgumentException("You must pass a valid observer");
203 }
204 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800205 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700206 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
208 }
209
Christopher Tate16aa9732012-09-17 16:23:44 -0700210 /**
211 * Notify observers of a particular user's view of the provider.
212 * @param userHandle the user whose view of the provider is to be notified. May be
213 * the calling user without requiring any permission, otherwise the caller needs to
214 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
215 * USER_CURRENT are properly interpreted; no other pseudousers are allowed.
216 */
217 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 public void notifyChange(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700219 boolean observerWantsSelfNotifications, boolean syncToNetwork,
220 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700222 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
223 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800225
Christopher Tate16aa9732012-09-17 16:23:44 -0700226 // Notify for any user other than the caller's own requires permission.
227 final int callingUserHandle = UserHandle.getCallingUserId();
228 if (userHandle != callingUserHandle) {
229 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
230 "no permission to notify other users");
231 }
232
233 // We passed the permission check; resolve pseudouser targets as appropriate
234 if (userHandle < 0) {
235 if (userHandle == UserHandle.USER_CURRENT) {
236 userHandle = ActivityManager.getCurrentUser();
237 } else if (userHandle != UserHandle.USER_ALL) {
238 throw new InvalidParameterException("Bad user handle for notifyChange: "
239 + userHandle);
240 }
241 }
242
Alon Albert57286f92012-10-09 14:21:38 -0700243 final int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 // This makes it so that future permission checks will be in the context of this
245 // process rather than the caller's process. We will restore this before returning.
246 long identityToken = clearCallingIdentity();
247 try {
248 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
249 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800250 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700251 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 }
253 final int numCalls = calls.size();
254 for (int i=0; i<numCalls; i++) {
255 ObserverCall oc = calls.get(i);
256 try {
Jeff Brown655e66b2012-01-23 15:51:41 -0800257 oc.mObserver.onChange(oc.mSelfChange, uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 if (Log.isLoggable(TAG, Log.VERBOSE)) {
259 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
260 }
261 } catch (RemoteException ex) {
262 synchronized (mRootNode) {
263 Log.w(TAG, "Found dead observer, removing");
264 IBinder binder = oc.mObserver.asBinder();
265 final ArrayList<ObserverNode.ObserverEntry> list
266 = oc.mNode.mObservers;
267 int numList = list.size();
268 for (int j=0; j<numList; j++) {
269 ObserverNode.ObserverEntry oe = list.get(j);
270 if (oe.observer.asBinder() == binder) {
271 list.remove(j);
272 j--;
273 numList--;
274 }
275 }
276 }
277 }
278 }
279 if (syncToNetwork) {
280 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700281 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700282 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800283 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 }
286 } finally {
287 restoreCallingIdentity(identityToken);
288 }
289 }
290
Christopher Tate16aa9732012-09-17 16:23:44 -0700291 public void notifyChange(Uri uri, IContentObserver observer,
292 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
293 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
294 UserHandle.getCallingUserId());
295 }
296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 /**
298 * Hide this class since it is not part of api,
299 * but current unittest framework requires it to be public
300 * @hide
301 *
302 */
303 public static final class ObserverCall {
304 final ObserverNode mNode;
305 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800306 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307
Jeff Brown86de0592012-01-23 13:01:18 -0800308 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 mNode = node;
310 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800311 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313 }
314
Fred Quintanaac9385e2009-06-22 18:00:59 -0700315 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700317 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700318 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800319
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 // This makes it so that future permission checks will be in the context of this
321 // process rather than the caller's process. We will restore this before returning.
322 long identityToken = clearCallingIdentity();
323 try {
324 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700325 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700326 syncManager.scheduleSync(account, userId, uId, authority, extras, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700327 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700328 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 } finally {
330 restoreCallingIdentity(identityToken);
331 }
332 }
333
334 /**
335 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700336 * if they match the authority and account, if they are present.
337 * @param account filter the pending and active syncs to cancel using this account
338 * @param authority filter the pending and active syncs to cancel using this authority
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700340 public void cancelSync(Account account, String authority) {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700341 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800342
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 // This makes it so that future permission checks will be in the context of this
344 // process rather than the caller's process. We will restore this before returning.
345 long identityToken = clearCallingIdentity();
346 try {
347 SyncManager syncManager = getSyncManager();
348 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800349 syncManager.clearScheduledSyncOperations(account, userId, authority);
350 syncManager.cancelActiveSync(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352 } finally {
353 restoreCallingIdentity(identityToken);
354 }
355 }
356
Fred Quintanaac9385e2009-06-22 18:00:59 -0700357 /**
358 * Get information about the SyncAdapters that are known to the system.
359 * @return an array of SyncAdapters that have registered with the system
360 */
361 public SyncAdapterType[] getSyncAdapterTypes() {
362 // This makes it so that future permission checks will be in the context of this
363 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700364 final int userId = UserHandle.getCallingUserId();
365 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700366 try {
367 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700368 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700369 } finally {
370 restoreCallingIdentity(identityToken);
371 }
372 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700373
Fred Quintanaac9385e2009-06-22 18:00:59 -0700374 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700375 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
376 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700377 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800378
Dianne Hackborn231cc602009-04-27 17:10:36 -0700379 long identityToken = clearCallingIdentity();
380 try {
381 SyncManager syncManager = getSyncManager();
382 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700383 return syncManager.getSyncStorageEngine().getSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800384 account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700385 }
386 } finally {
387 restoreCallingIdentity(identityToken);
388 }
389 return false;
390 }
391
Fred Quintanaac9385e2009-06-22 18:00:59 -0700392 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700393 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
394 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700395 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800396
Dianne Hackborn231cc602009-04-27 17:10:36 -0700397 long identityToken = clearCallingIdentity();
398 try {
399 SyncManager syncManager = getSyncManager();
400 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700401 syncManager.getSyncStorageEngine().setSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800402 account, userId, providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700403 }
404 } finally {
405 restoreCallingIdentity(identityToken);
406 }
407 }
408
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800409 public void addPeriodicSync(Account account, String authority, Bundle extras,
410 long pollFrequency) {
411 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
412 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700413 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800414
Jeff Sharkey51366be2013-03-27 14:46:55 -0700415 if (pollFrequency < 60) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700416 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Jeff Sharkey51366be2013-03-27 14:46:55 -0700417 + " seconds being rounded up to 60 seconds.");
418 pollFrequency = 60;
Jeff Sharkey60094592013-03-21 18:14:24 -0700419 }
420
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800421 long identityToken = clearCallingIdentity();
422 try {
423 getSyncManager().getSyncStorageEngine().addPeriodicSync(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800424 account, userId, authority, extras, pollFrequency);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800425 } finally {
426 restoreCallingIdentity(identityToken);
427 }
428 }
429
430 public void removePeriodicSync(Account account, String authority, Bundle extras) {
431 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
432 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700433 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800434
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800435 long identityToken = clearCallingIdentity();
436 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800437 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority,
438 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800439 } finally {
440 restoreCallingIdentity(identityToken);
441 }
442 }
443
444 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
445 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
446 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700447 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800448
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800449 long identityToken = clearCallingIdentity();
450 try {
451 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800452 account, userId, providerName);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800453 } finally {
454 restoreCallingIdentity(identityToken);
455 }
456 }
457
Fred Quintana5e787c42009-08-16 23:13:53 -0700458 public int getIsSyncable(Account account, String providerName) {
459 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
460 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700461 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800462
Fred Quintana5e787c42009-08-16 23:13:53 -0700463 long identityToken = clearCallingIdentity();
464 try {
465 SyncManager syncManager = getSyncManager();
466 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700467 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800468 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700469 }
470 } finally {
471 restoreCallingIdentity(identityToken);
472 }
473 return -1;
474 }
475
476 public void setIsSyncable(Account account, String providerName, int syncable) {
477 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
478 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700479 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800480
Fred Quintana5e787c42009-08-16 23:13:53 -0700481 long identityToken = clearCallingIdentity();
482 try {
483 SyncManager syncManager = getSyncManager();
484 if (syncManager != null) {
485 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800486 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700487 }
488 } finally {
489 restoreCallingIdentity(identityToken);
490 }
491 }
492
Fred Quintanaac9385e2009-06-22 18:00:59 -0700493 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700494 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
495 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700496 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800497
Dianne Hackborn231cc602009-04-27 17:10:36 -0700498 long identityToken = clearCallingIdentity();
499 try {
500 SyncManager syncManager = getSyncManager();
501 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800502 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700503 }
504 } finally {
505 restoreCallingIdentity(identityToken);
506 }
507 return false;
508 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700509
Fred Quintanaac9385e2009-06-22 18:00:59 -0700510 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700511 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
512 "no permission to write the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700513 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800514
Dianne Hackborn231cc602009-04-27 17:10:36 -0700515 long identityToken = clearCallingIdentity();
516 try {
517 SyncManager syncManager = getSyncManager();
518 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800519 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700520 }
521 } finally {
522 restoreCallingIdentity(identityToken);
523 }
524 }
525
Dianne Hackborn7a135592009-05-06 00:28:37 -0700526 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700527 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
528 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700529 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800530
Dianne Hackborn231cc602009-04-27 17:10:36 -0700531 long identityToken = clearCallingIdentity();
532 try {
533 SyncManager syncManager = getSyncManager();
534 if (syncManager != null) {
535 return syncManager.getSyncStorageEngine().isSyncActive(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800536 account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700537 }
538 } finally {
539 restoreCallingIdentity(identityToken);
540 }
541 return false;
542 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700543
Fred Quintanac6a69552010-09-27 17:05:04 -0700544 public List<SyncInfo> getCurrentSyncs() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700545 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
546 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700547 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800548
Dianne Hackborn231cc602009-04-27 17:10:36 -0700549 long identityToken = clearCallingIdentity();
550 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800551 return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700552 } finally {
553 restoreCallingIdentity(identityToken);
554 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700555 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700556
Fred Quintanaac9385e2009-06-22 18:00:59 -0700557 public SyncStatusInfo getSyncStatus(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700558 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
559 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700560 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800561
Dianne Hackborn231cc602009-04-27 17:10:36 -0700562 long identityToken = clearCallingIdentity();
563 try {
564 SyncManager syncManager = getSyncManager();
565 if (syncManager != null) {
Costin Manolacheb7520982009-09-02 18:03:05 -0700566 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800567 account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700568 }
569 } finally {
570 restoreCallingIdentity(identityToken);
571 }
572 return null;
573 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700574
Fred Quintanaac9385e2009-06-22 18:00:59 -0700575 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700576 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
577 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700578 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800579
Dianne Hackborn231cc602009-04-27 17:10:36 -0700580 long identityToken = clearCallingIdentity();
581 try {
582 SyncManager syncManager = getSyncManager();
583 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800584 return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700585 }
586 } finally {
587 restoreCallingIdentity(identityToken);
588 }
589 return false;
590 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700591
Dianne Hackborn231cc602009-04-27 17:10:36 -0700592 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
593 long identityToken = clearCallingIdentity();
594 try {
595 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800596 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700597 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700598 }
599 } finally {
600 restoreCallingIdentity(identityToken);
601 }
602 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700603
Dianne Hackborn231cc602009-04-27 17:10:36 -0700604 public void removeStatusChangeListener(ISyncStatusObserver callback) {
605 long identityToken = clearCallingIdentity();
606 try {
607 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800608 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700609 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700610 }
611 } finally {
612 restoreCallingIdentity(identityToken);
613 }
614 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700615
Kenny Root26ff6622012-07-30 12:58:03 -0700616 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700618 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 return service;
620 }
621
622 /**
623 * Hide this class since it is not part of api,
624 * but current unittest framework requires it to be public
625 * @hide
626 */
627 public static final class ObserverNode {
628 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800629 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700630 public final int uid;
631 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700632 public final boolean notifyForDescendants;
633 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -0800634 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700636 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Christopher Tate16aa9732012-09-17 16:23:44 -0700637 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800638 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700640 uid = _uid;
641 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700642 userHandle = _userHandle;
643 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 try {
645 observer.asBinder().linkToDeath(this, 0);
646 } catch (RemoteException e) {
647 binderDied();
648 }
649 }
650
651 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800652 synchronized (observersLock) {
653 removeObserverLocked(observer);
654 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700656
657 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
658 String name, String prefix, SparseIntArray pidCounts) {
659 pidCounts.put(pid, pidCounts.get(pid)+1);
660 pw.print(prefix); pw.print(name); pw.print(": pid=");
661 pw.print(pid); pw.print(" uid=");
Christopher Tate16aa9732012-09-17 16:23:44 -0700662 pw.print(uid); pw.print(" user=");
663 pw.print(userHandle); pw.print(" target=");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700664 pw.println(Integer.toHexString(System.identityHashCode(
665 observer != null ? observer.asBinder() : null)));
666 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 }
668
669 public static final int INSERT_TYPE = 0;
670 public static final int UPDATE_TYPE = 1;
671 public static final int DELETE_TYPE = 2;
672
673 private String mName;
674 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
675 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
676
677 public ObserverNode(String name) {
678 mName = name;
679 }
680
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700681 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
682 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
683 String innerName = null;
684 if (mObservers.size() > 0) {
685 if ("".equals(name)) {
686 innerName = mName;
687 } else {
688 innerName = name + "/" + mName;
689 }
690 for (int i=0; i<mObservers.size(); i++) {
691 counts[1]++;
692 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
693 pidCounts);
694 }
695 }
696 if (mChildren.size() > 0) {
697 if (innerName == null) {
698 if ("".equals(name)) {
699 innerName = mName;
700 } else {
701 innerName = name + "/" + mName;
702 }
703 }
704 for (int i=0; i<mChildren.size(); i++) {
705 counts[0]++;
706 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
707 counts, pidCounts);
708 }
709 }
710 }
711
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 private String getUriSegment(Uri uri, int index) {
713 if (uri != null) {
714 if (index == 0) {
715 return uri.getAuthority();
716 } else {
717 return uri.getPathSegments().get(index - 1);
718 }
719 } else {
720 return null;
721 }
722 }
723
724 private int countUriSegments(Uri uri) {
725 if (uri == null) {
726 return 0;
727 }
728 return uri.getPathSegments().size() + 1;
729 }
730
Christopher Tate16aa9732012-09-17 16:23:44 -0700731 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -0800732 public void addObserverLocked(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700733 boolean notifyForDescendants, Object observersLock,
734 int uid, int pid, int userHandle) {
735 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
736 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 }
738
Fred Quintana002ffad52010-03-02 11:18:16 -0800739 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700740 boolean notifyForDescendants, Object observersLock,
741 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 // If this is the leaf node add the observer
743 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700744 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
745 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 return;
747 }
748
749 // Look to see if the proper child already exists
750 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -0500751 if (segment == null) {
752 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
753 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 int N = mChildren.size();
755 for (int i = 0; i < N; i++) {
756 ObserverNode node = mChildren.get(i);
757 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700758 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
759 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 return;
761 }
762 }
763
764 // No child found, create one
765 ObserverNode node = new ObserverNode(segment);
766 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -0700767 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
768 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 }
770
Fred Quintana002ffad52010-03-02 11:18:16 -0800771 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 int size = mChildren.size();
773 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800774 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 if (empty) {
776 mChildren.remove(i);
777 i--;
778 size--;
779 }
780 }
781
782 IBinder observerBinder = observer.asBinder();
783 size = mObservers.size();
784 for (int i = 0; i < size; i++) {
785 ObserverEntry entry = mObservers.get(i);
786 if (entry.observer.asBinder() == observerBinder) {
787 mObservers.remove(i);
788 // We no longer need to listen for death notifications. Remove it.
789 observerBinder.unlinkToDeath(entry, 0);
790 break;
791 }
792 }
793
794 if (mChildren.size() == 0 && mObservers.size() == 0) {
795 return true;
796 }
797 return false;
798 }
799
Fred Quintana002ffad52010-03-02 11:18:16 -0800800 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700801 boolean observerWantsSelfNotifications, int targetUserHandle,
802 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 int N = mObservers.size();
804 IBinder observerBinder = observer == null ? null : observer.asBinder();
805 for (int i = 0; i < N; i++) {
806 ObserverEntry entry = mObservers.get(i);
807
Christopher Tate16aa9732012-09-17 16:23:44 -0700808 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -0800810 boolean selfChange = (entry.observer.asBinder() == observerBinder);
811 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 continue;
813 }
814
Christopher Tate16aa9732012-09-17 16:23:44 -0700815 // Does this observer match the target user?
816 if (targetUserHandle == UserHandle.USER_ALL
817 || entry.userHandle == UserHandle.USER_ALL
818 || targetUserHandle == entry.userHandle) {
819 // Make sure the observer is interested in the notification
820 if (leaf || (!leaf && entry.notifyForDescendants)) {
821 calls.add(new ObserverCall(this, entry.observer, selfChange));
822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 }
824 }
825 }
826
Christopher Tate16aa9732012-09-17 16:23:44 -0700827 /**
828 * targetUserHandle is either a hard user handle or is USER_ALL
829 */
Fred Quintana002ffad52010-03-02 11:18:16 -0800830 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700831 boolean observerWantsSelfNotifications, int targetUserHandle,
832 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 String segment = null;
834 int segmentCount = countUriSegments(uri);
835 if (index >= segmentCount) {
836 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -0700837 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
838 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 } else if (index < segmentCount){
840 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -0700841 // Notify any observers at this level who are interested in descendants
842 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
843 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 }
845
846 int N = mChildren.size();
847 for (int i = 0; i < N; i++) {
848 ObserverNode node = mChildren.get(i);
849 if (segment == null || node.mName.equals(segment)) {
850 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -0800851 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -0700852 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 if (segment != null) {
854 break;
855 }
856 }
857 }
858 }
859 }
860}