blob: c7d28714e4dbcfe7483c4eae34045849132105d0 [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;
Matthew Williamsfa774182013-06-18 15:44:11 -070022import android.content.ComponentName;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080023import android.content.ContentResolver;
24import android.content.Context;
25import android.content.IContentService;
26import android.content.ISyncStatusObserver;
27import android.content.PeriodicSync;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070028import android.content.pm.PackageManager;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080029import android.content.SyncAdapterType;
30import android.content.SyncInfo;
Matthew Williamsfa774182013-06-18 15:44:11 -070031import android.content.SyncRequest;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080032import android.content.SyncStatusInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.database.IContentObserver;
34import android.database.sqlite.SQLiteException;
35import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070036import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.os.Bundle;
38import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070039import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.RemoteException;
41import android.os.ServiceManager;
Dan Morrille4d9a012013-03-28 18:10:43 -070042import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070043import android.os.UserHandle;
Matthew Williams632515b2013-10-10 15:51:00 -070044import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.Log;
Jeff Sharkey60094592013-03-21 18:14:24 -070046import android.util.Slog;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070047import android.util.SparseIntArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49import java.io.FileDescriptor;
50import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070051import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070053import java.util.Collections;
54import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080055import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
57/**
58 * {@hide}
59 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070060public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 private static final String TAG = "ContentService";
62 private Context mContext;
63 private boolean mFactoryTest;
64 private final ObserverNode mRootNode = new ObserverNode("");
65 private SyncManager mSyncManager = null;
66 private final Object mSyncManagerLock = new Object();
67
68 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -070069 if (SystemProperties.getBoolean("config.disable_network", false)) {
70 return null;
71 }
72
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 synchronized(mSyncManagerLock) {
74 try {
75 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
76 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
77 } catch (SQLiteException e) {
78 Log.e(TAG, "Can't create SyncManager", e);
79 }
80 return mSyncManager;
81 }
82 }
83
84 @Override
85 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
86 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
87 "caller doesn't have the DUMP permission");
88
89 // This makes it so that future permission checks will be in the context of this
90 // process rather than the caller's process. We will restore this before returning.
91 long identityToken = clearCallingIdentity();
92 try {
93 if (mSyncManager == null) {
94 pw.println("No SyncManager created! (Disk full?)");
95 } else {
96 mSyncManager.dump(fd, pw);
97 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070098 pw.println();
99 pw.println("Observer tree:");
100 synchronized (mRootNode) {
101 int[] counts = new int[2];
102 final SparseIntArray pidCounts = new SparseIntArray();
103 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
104 pw.println();
105 ArrayList<Integer> sorted = new ArrayList<Integer>();
106 for (int i=0; i<pidCounts.size(); i++) {
107 sorted.add(pidCounts.keyAt(i));
108 }
109 Collections.sort(sorted, new Comparator<Integer>() {
110 @Override
111 public int compare(Integer lhs, Integer rhs) {
112 int lc = pidCounts.get(lhs);
113 int rc = pidCounts.get(rhs);
114 if (lc < rc) {
115 return 1;
116 } else if (lc > rc) {
117 return -1;
118 }
119 return 0;
120 }
121
122 });
123 for (int i=0; i<sorted.size(); i++) {
124 int pid = sorted.get(i);
125 pw.print(" pid "); pw.print(pid); pw.print(": ");
126 pw.print(pidCounts.get(pid)); pw.println(" observers");
127 }
128 pw.println();
129 pw.print(" Total number of nodes: "); pw.println(counts[0]);
130 pw.print(" Total number of observers: "); pw.println(counts[1]);
131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 } finally {
133 restoreCallingIdentity(identityToken);
134 }
135 }
136
Dianne Hackborn231cc602009-04-27 17:10:36 -0700137 @Override
138 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
139 throws RemoteException {
140 try {
141 return super.onTransact(code, data, reply, flags);
142 } catch (RuntimeException e) {
143 // The content service only throws security exceptions, so let's
144 // log all others.
145 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700146 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700147 }
148 throw e;
149 }
150 }
151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 /*package*/ ContentService(Context context, boolean factoryTest) {
153 mContext = context;
154 mFactoryTest = factoryTest;
Kenny Root26ff6622012-07-30 12:58:03 -0700155 }
156
157 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 getSyncManager();
159 }
160
Christopher Tate16aa9732012-09-17 16:23:44 -0700161 /**
162 * Register a content observer tied to a specific user's view of the provider.
163 * @param userHandle the user whose view of the provider is to be observed. May be
164 * the calling user without requiring any permission, otherwise the caller needs to
165 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
166 * USER_CURRENT are properly handled; all other pseudousers are forbidden.
167 */
168 @Override
169 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
170 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 if (observer == null || uri == null) {
172 throw new IllegalArgumentException("You must pass a valid uri and observer");
173 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700174
175 final int callingUser = UserHandle.getCallingUserId();
176 if (callingUser != userHandle) {
177 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
178 "no permission to observe other users' provider view");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700180
181 if (userHandle < 0) {
182 if (userHandle == UserHandle.USER_CURRENT) {
183 userHandle = ActivityManager.getCurrentUser();
184 } else if (userHandle != UserHandle.USER_ALL) {
185 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
186 + userHandle);
187 }
188 }
189
190 synchronized (mRootNode) {
191 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
192 Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
193 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
194 " with notifyForDescendants " + notifyForDescendants);
195 }
196 }
197
198 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
199 IContentObserver observer) {
200 registerContentObserver(uri, notifyForDescendants, observer,
201 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 public void unregisterContentObserver(IContentObserver observer) {
205 if (observer == null) {
206 throw new IllegalArgumentException("You must pass a valid observer");
207 }
208 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800209 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700210 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 }
212 }
213
Christopher Tate16aa9732012-09-17 16:23:44 -0700214 /**
215 * Notify observers of a particular user's view of the provider.
216 * @param userHandle the user whose view of the provider is to be notified. May be
217 * the calling user without requiring any permission, otherwise the caller needs to
218 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
219 * USER_CURRENT are properly interpreted; no other pseudousers are allowed.
220 */
221 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 public void notifyChange(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700223 boolean observerWantsSelfNotifications, boolean syncToNetwork,
224 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700226 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
227 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800229
Christopher Tate16aa9732012-09-17 16:23:44 -0700230 // Notify for any user other than the caller's own requires permission.
231 final int callingUserHandle = UserHandle.getCallingUserId();
232 if (userHandle != callingUserHandle) {
233 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
234 "no permission to notify other users");
235 }
236
237 // We passed the permission check; resolve pseudouser targets as appropriate
238 if (userHandle < 0) {
239 if (userHandle == UserHandle.USER_CURRENT) {
240 userHandle = ActivityManager.getCurrentUser();
241 } else if (userHandle != UserHandle.USER_ALL) {
242 throw new InvalidParameterException("Bad user handle for notifyChange: "
243 + userHandle);
244 }
245 }
246
Alon Albert57286f92012-10-09 14:21:38 -0700247 final int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 // This makes it so that future permission checks will be in the context of this
249 // process rather than the caller's process. We will restore this before returning.
250 long identityToken = clearCallingIdentity();
251 try {
252 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
253 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800254 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700255 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 }
257 final int numCalls = calls.size();
258 for (int i=0; i<numCalls; i++) {
259 ObserverCall oc = calls.get(i);
260 try {
Jeff Brown655e66b2012-01-23 15:51:41 -0800261 oc.mObserver.onChange(oc.mSelfChange, uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 if (Log.isLoggable(TAG, Log.VERBOSE)) {
263 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
264 }
265 } catch (RemoteException ex) {
266 synchronized (mRootNode) {
267 Log.w(TAG, "Found dead observer, removing");
268 IBinder binder = oc.mObserver.asBinder();
269 final ArrayList<ObserverNode.ObserverEntry> list
270 = oc.mNode.mObservers;
271 int numList = list.size();
272 for (int j=0; j<numList; j++) {
273 ObserverNode.ObserverEntry oe = list.get(j);
274 if (oe.observer.asBinder() == binder) {
275 list.remove(j);
276 j--;
277 numList--;
278 }
279 }
280 }
281 }
282 }
283 if (syncToNetwork) {
284 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700285 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700286 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800287 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700288 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
290 } finally {
291 restoreCallingIdentity(identityToken);
292 }
293 }
294
Christopher Tate16aa9732012-09-17 16:23:44 -0700295 public void notifyChange(Uri uri, IContentObserver observer,
296 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
297 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
298 UserHandle.getCallingUserId());
299 }
300
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 /**
302 * Hide this class since it is not part of api,
303 * but current unittest framework requires it to be public
304 * @hide
305 *
306 */
307 public static final class ObserverCall {
308 final ObserverNode mNode;
309 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800310 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311
Jeff Brown86de0592012-01-23 13:01:18 -0800312 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 mNode = node;
314 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800315 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317 }
318
Fred Quintanaac9385e2009-06-22 18:00:59 -0700319 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700321 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700322 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 // This makes it so that future permission checks will be in the context of this
325 // process rather than the caller's process. We will restore this before returning.
326 long identityToken = clearCallingIdentity();
327 try {
328 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700329 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700330 syncManager.scheduleSync(account, userId, uId, authority, extras,
331 0 /* no delay */, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700332 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700333 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 } finally {
335 restoreCallingIdentity(identityToken);
336 }
337 }
338
339 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700340 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
341 * either:
342 * periodic OR one-off sync.
343 * and
344 * anonymous OR provider sync.
345 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700346 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700347 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700348 public void sync(SyncRequest request) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700349 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700350 int callerUid = Binder.getCallingUid();
Matthew Williamsfa774182013-06-18 15:44:11 -0700351 // This makes it so that future permission checks will be in the context of this
352 // process rather than the caller's process. We will restore this before returning.
353 long identityToken = clearCallingIdentity();
354 try {
355 SyncManager syncManager = getSyncManager();
Matthew Williamsd08d6682013-10-14 10:39:41 -0700356 if (syncManager == null) {
357 return;
358 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700359
Matthew Williamsd08d6682013-10-14 10:39:41 -0700360 Bundle extras = request.getBundle();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700361 long flextime = request.getSyncFlexTime();
362 long runAtTime = request.getSyncRunTime();
363 if (request.isPeriodic()) {
364 mContext.enforceCallingOrSelfPermission(
365 Manifest.permission.WRITE_SYNC_SETTINGS,
366 "no permission to write the sync settings");
367 SyncStorageEngine.EndPoint info;
368 if (!request.hasAuthority()) {
369 // Extra permissions checking for sync service.
370 verifySignatureForPackage(callerUid,
371 request.getService().getPackageName(), "sync");
372 info = new SyncStorageEngine.EndPoint(request.getService(), userId);
Matthew Williams96ca46c2013-07-26 12:56:39 -0700373 } else {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700374 info = new SyncStorageEngine.EndPoint(
375 request.getAccount(), request.getProvider(), userId);
376 }
377 if (runAtTime < 60) {
378 Slog.w(TAG, "Requested poll frequency of " + runAtTime
379 + " seconds being rounded up to 60 seconds.");
380 runAtTime = 60;
381 }
382 // Schedule periodic sync.
383 getSyncManager().getSyncStorageEngine()
384 .updateOrAddPeriodicSync(info, runAtTime, flextime, extras);
385 } else {
386 long beforeRuntimeMillis = (flextime) * 1000;
387 long runtimeMillis = runAtTime * 1000;
388 if (request.hasAuthority()) {
389 syncManager.scheduleSync(
390 request.getAccount(), userId, callerUid, request.getProvider(), extras,
391 beforeRuntimeMillis, runtimeMillis,
392 false /* onlyThoseWithUnknownSyncableState */);
393 } else {
394 syncManager.scheduleSync(
395 request.getService(), userId, callerUid, extras,
396 beforeRuntimeMillis,
397 runtimeMillis); // Empty function.
Matthew Williamsfa774182013-06-18 15:44:11 -0700398 }
399 }
400 } finally {
401 restoreCallingIdentity(identityToken);
402 }
403 }
404
405 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700407 * if they match the authority and account, if they are present.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700408 *
409 * @param account filter the pending and active syncs to cancel using this account, or null.
410 * @param authority filter the pending and active syncs to cancel using this authority, or
411 * null.
412 * @param cname cancel syncs running on this service, or null for provider/account.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700414 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700415 public void cancelSync(Account account, String authority, ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700416 if (authority != null && authority.length() == 0) {
417 throw new IllegalArgumentException("Authority must be non-empty");
418 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800419
Matthew Williams632515b2013-10-10 15:51:00 -0700420 int userId = UserHandle.getCallingUserId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 // This makes it so that future permission checks will be in the context of this
422 // process rather than the caller's process. We will restore this before returning.
423 long identityToken = clearCallingIdentity();
424 try {
425 SyncManager syncManager = getSyncManager();
426 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700427 SyncStorageEngine.EndPoint info;
428 if (cname == null) {
429 info = new SyncStorageEngine.EndPoint(account, authority, userId);
430 } else {
431 info = new SyncStorageEngine.EndPoint(cname, userId);
432 }
433 syncManager.clearScheduledSyncOperations(info);
434 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 }
436 } finally {
437 restoreCallingIdentity(identityToken);
438 }
439 }
440
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700441 public void cancelRequest(SyncRequest request) {
442 SyncManager syncManager = getSyncManager();
443 if (syncManager == null) return;
444 int userId = UserHandle.getCallingUserId();
445 int callerUid = Binder.getCallingUid();
446
447 long identityToken = clearCallingIdentity();
448 try {
449 SyncStorageEngine.EndPoint info;
450 Bundle extras = new Bundle(request.getBundle());
451 if (request.hasAuthority()) {
452 Account account = request.getAccount();
453 String provider = request.getProvider();
454 info = new SyncStorageEngine.EndPoint(account, provider, userId);
455 } else {
456 // Only allowed to manipulate syncs for a service which you own.
457 ComponentName service = request.getService();
458 verifySignatureForPackage(callerUid, service.getPackageName(), "cancel");
459 info = new SyncStorageEngine.EndPoint(service, userId);
460 }
461 if (request.isPeriodic()) {
462 // Remove periodic sync.
463 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
464 "no permission to write the sync settings");
465 getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras);
466 }
467 // Cancel active syncs and clear pending syncs from the queue.
468 syncManager.cancelScheduledSyncOperation(info, extras);
469 syncManager.cancelActiveSync(info, extras);
470 } finally {
471 restoreCallingIdentity(identityToken);
472 }
473 }
474
Fred Quintanaac9385e2009-06-22 18:00:59 -0700475 /**
476 * Get information about the SyncAdapters that are known to the system.
477 * @return an array of SyncAdapters that have registered with the system
478 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700479 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700480 public SyncAdapterType[] getSyncAdapterTypes() {
481 // This makes it so that future permission checks will be in the context of this
482 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700483 final int userId = UserHandle.getCallingUserId();
484 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700485 try {
486 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700487 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700488 } finally {
489 restoreCallingIdentity(identityToken);
490 }
491 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700492
Matthew Williamsfa774182013-06-18 15:44:11 -0700493 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700494 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700495 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
496 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800497
Matthew Williams632515b2013-10-10 15:51:00 -0700498 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700499 long identityToken = clearCallingIdentity();
500 try {
501 SyncManager syncManager = getSyncManager();
502 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700503 return syncManager.getSyncStorageEngine()
504 .getSyncAutomatically(account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700505 }
506 } finally {
507 restoreCallingIdentity(identityToken);
508 }
509 return false;
510 }
511
Matthew Williamsfa774182013-06-18 15:44:11 -0700512 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700513 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Matthew Williams632515b2013-10-10 15:51:00 -0700514 if (TextUtils.isEmpty(providerName)) {
515 throw new IllegalArgumentException("Authority must be non-empty");
516 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700517 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
518 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800519
Matthew Williams632515b2013-10-10 15:51:00 -0700520 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700521 long identityToken = clearCallingIdentity();
522 try {
523 SyncManager syncManager = getSyncManager();
524 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700525 syncManager.getSyncStorageEngine()
526 .setSyncAutomatically(account, userId, providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700527 }
528 } finally {
529 restoreCallingIdentity(identityToken);
530 }
531 }
532
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700533 /** Old API. Schedule periodic sync with default flex time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700534 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800535 public void addPeriodicSync(Account account, String authority, Bundle extras,
536 long pollFrequency) {
Matthew Williams632515b2013-10-10 15:51:00 -0700537 if (account == null) {
538 throw new IllegalArgumentException("Account must not be null");
539 }
540 if (TextUtils.isEmpty(authority)) {
541 throw new IllegalArgumentException("Authority must not be empty.");
542 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800543 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
544 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800545
Matthew Williams632515b2013-10-10 15:51:00 -0700546 int userId = UserHandle.getCallingUserId();
Jeff Sharkey51366be2013-03-27 14:46:55 -0700547 if (pollFrequency < 60) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700548 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Jeff Sharkey51366be2013-03-27 14:46:55 -0700549 + " seconds being rounded up to 60 seconds.");
550 pollFrequency = 60;
Jeff Sharkey60094592013-03-21 18:14:24 -0700551 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700552 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
Jeff Sharkey60094592013-03-21 18:14:24 -0700553
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800554 long identityToken = clearCallingIdentity();
555 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700556 SyncStorageEngine.EndPoint info =
557 new SyncStorageEngine.EndPoint(account, authority, userId);
558 getSyncManager().getSyncStorageEngine()
559 .updateOrAddPeriodicSync(info,
560 pollFrequency,
561 defaultFlex,
562 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800563 } finally {
564 restoreCallingIdentity(identityToken);
565 }
566 }
567
568 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Matthew Williams632515b2013-10-10 15:51:00 -0700569 if (account == null) {
570 throw new IllegalArgumentException("Account must not be null");
571 }
572 if (TextUtils.isEmpty(authority)) {
573 throw new IllegalArgumentException("Authority must not be empty");
574 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800575 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
576 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800577
Matthew Williams632515b2013-10-10 15:51:00 -0700578 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800579 long identityToken = clearCallingIdentity();
580 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700581 getSyncManager().getSyncStorageEngine()
582 .removePeriodicSync(
583 new SyncStorageEngine.EndPoint(account, authority, userId),
584 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800585 } finally {
586 restoreCallingIdentity(identityToken);
587 }
588 }
589
Matthew Williamsfa774182013-06-18 15:44:11 -0700590
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700591 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
592 ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700593 if (account == null) {
594 throw new IllegalArgumentException("Account must not be null");
595 }
596 if (TextUtils.isEmpty(providerName)) {
597 throw new IllegalArgumentException("Authority must not be empty");
598 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800599 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
600 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800601
Matthew Williamsd08d6682013-10-14 10:39:41 -0700602 int callerUid = Binder.getCallingUid();
Matthew Williams632515b2013-10-10 15:51:00 -0700603 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800604 long identityToken = clearCallingIdentity();
605 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700606 if (cname == null) {
607 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
608 new SyncStorageEngine.EndPoint(account, providerName, userId));
609 } else if (account == null && providerName == null) {
610 verifySignatureForPackage(callerUid, cname.getPackageName(), "getPeriodicSyncs");
611 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
612 new SyncStorageEngine.EndPoint(cname, userId));
613 } else {
614 throw new IllegalArgumentException("Invalid authority specified");
615 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800616 } finally {
617 restoreCallingIdentity(identityToken);
618 }
619 }
620
Fred Quintana5e787c42009-08-16 23:13:53 -0700621 public int getIsSyncable(Account account, String providerName) {
622 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
623 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700624 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800625
Fred Quintana5e787c42009-08-16 23:13:53 -0700626 long identityToken = clearCallingIdentity();
627 try {
628 SyncManager syncManager = getSyncManager();
629 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700630 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800631 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700632 }
633 } finally {
634 restoreCallingIdentity(identityToken);
635 }
636 return -1;
637 }
638
639 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700640 if (TextUtils.isEmpty(providerName)) {
641 throw new IllegalArgumentException("Authority must not be empty");
642 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700643 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
644 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800645
Matthew Williams632515b2013-10-10 15:51:00 -0700646 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700647 long identityToken = clearCallingIdentity();
648 try {
649 SyncManager syncManager = getSyncManager();
650 if (syncManager != null) {
651 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800652 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700653 }
654 } finally {
655 restoreCallingIdentity(identityToken);
656 }
657 }
658
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700659 public void setServiceActive(ComponentName cname, boolean active) {
660 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
661 "no permission to write the sync settings");
Matthew Williams7a2ab3a2013-09-11 14:25:51 -0700662 verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(),
663 "setServiceActive");
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700664
665 int userId = UserHandle.getCallingUserId();
666 long identityToken = clearCallingIdentity();
667 try {
668 SyncManager syncManager = getSyncManager();
669 if (syncManager != null) {
Matthew Williams7a2ab3a2013-09-11 14:25:51 -0700670 syncManager.getSyncStorageEngine().setIsTargetServiceActive(
671 cname, userId, active);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700672 }
673 } finally {
674 restoreCallingIdentity(identityToken);
675 }
676 }
677
678 public boolean isServiceActive(ComponentName cname) {
679 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
680 "no permission to read the sync settings");
Matthew Williams8ef22042013-07-26 12:56:39 -0700681 verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(),
682 "isServiceActive");
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700683
684 int userId = UserHandle.getCallingUserId();
685 long identityToken = clearCallingIdentity();
686 try {
687 SyncManager syncManager = getSyncManager();
688 if (syncManager != null) {
Matthew Williams7a2ab3a2013-09-11 14:25:51 -0700689 return syncManager.getSyncStorageEngine()
690 .getIsTargetServiceActive(cname, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700691 }
692 } finally {
693 restoreCallingIdentity(identityToken);
694 }
695 return false;
696 }
697
Matthew Williamsfa774182013-06-18 15:44:11 -0700698 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700699 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700700 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
701 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800702
Matthew Williams632515b2013-10-10 15:51:00 -0700703 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700704 long identityToken = clearCallingIdentity();
705 try {
706 SyncManager syncManager = getSyncManager();
707 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800708 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700709 }
710 } finally {
711 restoreCallingIdentity(identityToken);
712 }
713 return false;
714 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700715
Matthew Williamsfa774182013-06-18 15:44:11 -0700716 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700717 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700718 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
719 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800720
Matthew Williams632515b2013-10-10 15:51:00 -0700721 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700722 long identityToken = clearCallingIdentity();
723 try {
724 SyncManager syncManager = getSyncManager();
725 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800726 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700727 }
728 } finally {
729 restoreCallingIdentity(identityToken);
730 }
731 }
732
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700733 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700734 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
735 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700736 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700737 int callingUid = Binder.getCallingUid();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700738 long identityToken = clearCallingIdentity();
739 try {
740 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700741 if (syncManager == null) {
742 return false;
743 }
744 if (cname == null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700745 return syncManager.getSyncStorageEngine().isSyncActive(
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700746 new SyncStorageEngine.EndPoint(account, authority, userId));
747 } else if (account == null && authority == null) {
748 verifySignatureForPackage(callingUid, cname.getPackageName(), "isSyncActive");
749 return syncManager.getSyncStorageEngine().isSyncActive(
750 new SyncStorageEngine.EndPoint(cname, userId));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700751 }
752 } finally {
753 restoreCallingIdentity(identityToken);
754 }
755 return false;
756 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700757
Fred Quintanac6a69552010-09-27 17:05:04 -0700758 public List<SyncInfo> getCurrentSyncs() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700759 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
760 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800761
Matthew Williams632515b2013-10-10 15:51:00 -0700762 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700763 long identityToken = clearCallingIdentity();
764 try {
Matthew Williamsa7456e42013-11-12 14:41:02 -0800765 return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700766 } finally {
767 restoreCallingIdentity(identityToken);
768 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700769 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700770
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700771 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700772 if (TextUtils.isEmpty(authority)) {
773 throw new IllegalArgumentException("Authority must not be empty");
774 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700775 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
776 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800777
Dianne Hackborn231cc602009-04-27 17:10:36 -0700778 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700779 int callerUid = Binder.getCallingUid();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700780 long identityToken = clearCallingIdentity();
781 try {
782 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700783 if (syncManager == null) {
784 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700785 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700786 SyncStorageEngine.EndPoint info;
787 if (cname == null) {
788 info = new SyncStorageEngine.EndPoint(account, authority, userId);
789 } else if (account == null && authority == null) {
790 verifySignatureForPackage(callerUid, cname.getPackageName(), "getSyncStatus");
791 info = new SyncStorageEngine.EndPoint(cname, userId);
792 } else {
793 throw new IllegalArgumentException("Must call sync status with valid authority");
794 }
795 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700796 } finally {
797 restoreCallingIdentity(identityToken);
798 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700799 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700800
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700801 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700802 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
803 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800804
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700805 int callerUid = Binder.getCallingUid();
Matthew Williams632515b2013-10-10 15:51:00 -0700806 int userId = UserHandle.getCallingUserId();
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000807 long identityToken = clearCallingIdentity();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700808 SyncManager syncManager = getSyncManager();
809 if (syncManager == null) return false;
810
Dianne Hackborn231cc602009-04-27 17:10:36 -0700811 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700812 SyncStorageEngine.EndPoint info;
813 if (cname == null) {
814 info = new SyncStorageEngine.EndPoint(account, authority, userId);
815 } else if (account == null && authority == null) {
816 verifySignatureForPackage(callerUid, cname.getPackageName(), "isSyncPending");
817 info = new SyncStorageEngine.EndPoint(cname, userId);
818 } else {
819 throw new IllegalArgumentException("Invalid authority specified");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700820 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700821 return syncManager.getSyncStorageEngine().isSyncPending(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700822 } finally {
823 restoreCallingIdentity(identityToken);
824 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700825 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700826
Dianne Hackborn231cc602009-04-27 17:10:36 -0700827 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
828 long identityToken = clearCallingIdentity();
829 try {
830 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800831 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700832 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700833 }
834 } finally {
835 restoreCallingIdentity(identityToken);
836 }
837 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700838
Dianne Hackborn231cc602009-04-27 17:10:36 -0700839 public void removeStatusChangeListener(ISyncStatusObserver callback) {
840 long identityToken = clearCallingIdentity();
841 try {
842 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800843 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700844 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700845 }
846 } finally {
847 restoreCallingIdentity(identityToken);
848 }
849 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700850
Kenny Root26ff6622012-07-30 12:58:03 -0700851 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700853 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 return service;
855 }
856
857 /**
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700858 * Helper to verify that the provided package name shares the same cert as the caller.
859 * @param callerUid uid of the calling process.
860 * @param packageName package to verify against package of calling application.
861 * @param tag a tag to use when throwing an exception if the signatures don't
862 * match. Cannot be null.
863 * @return true if the calling application and the provided package are signed with the same
864 * certificate.
865 */
866 private boolean verifySignatureForPackage(int callerUid, String packageName, String tag) {
867 PackageManager pm = mContext.getPackageManager();
868 try {
869 int serviceUid = pm.getApplicationInfo(packageName, 0).uid;
870 if (pm.checkSignatures(callerUid, serviceUid) == PackageManager.SIGNATURE_MATCH) {
871 return true;
872 } else {
873 throw new SecurityException(tag + ": Caller certificate does not match that for - "
874 + packageName);
875 }
876 } catch (PackageManager.NameNotFoundException e) {
877 throw new IllegalArgumentException(tag + ": " + packageName + " package not found.");
878 }
879 }
880
881 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 * Hide this class since it is not part of api,
883 * but current unittest framework requires it to be public
884 * @hide
885 */
886 public static final class ObserverNode {
887 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800888 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700889 public final int uid;
890 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700891 public final boolean notifyForDescendants;
892 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -0800893 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700895 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Christopher Tate16aa9732012-09-17 16:23:44 -0700896 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800897 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700899 uid = _uid;
900 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700901 userHandle = _userHandle;
902 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 try {
904 observer.asBinder().linkToDeath(this, 0);
905 } catch (RemoteException e) {
906 binderDied();
907 }
908 }
909
910 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800911 synchronized (observersLock) {
912 removeObserverLocked(observer);
913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700915
916 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
917 String name, String prefix, SparseIntArray pidCounts) {
918 pidCounts.put(pid, pidCounts.get(pid)+1);
919 pw.print(prefix); pw.print(name); pw.print(": pid=");
920 pw.print(pid); pw.print(" uid=");
Christopher Tate16aa9732012-09-17 16:23:44 -0700921 pw.print(uid); pw.print(" user=");
922 pw.print(userHandle); pw.print(" target=");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700923 pw.println(Integer.toHexString(System.identityHashCode(
924 observer != null ? observer.asBinder() : null)));
925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 }
927
928 public static final int INSERT_TYPE = 0;
929 public static final int UPDATE_TYPE = 1;
930 public static final int DELETE_TYPE = 2;
931
932 private String mName;
933 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
934 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
935
936 public ObserverNode(String name) {
937 mName = name;
938 }
939
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700940 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
941 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
942 String innerName = null;
943 if (mObservers.size() > 0) {
944 if ("".equals(name)) {
945 innerName = mName;
946 } else {
947 innerName = name + "/" + mName;
948 }
949 for (int i=0; i<mObservers.size(); i++) {
950 counts[1]++;
951 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
952 pidCounts);
953 }
954 }
955 if (mChildren.size() > 0) {
956 if (innerName == null) {
957 if ("".equals(name)) {
958 innerName = mName;
959 } else {
960 innerName = name + "/" + mName;
961 }
962 }
963 for (int i=0; i<mChildren.size(); i++) {
964 counts[0]++;
965 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
966 counts, pidCounts);
967 }
968 }
969 }
970
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 private String getUriSegment(Uri uri, int index) {
972 if (uri != null) {
973 if (index == 0) {
974 return uri.getAuthority();
975 } else {
976 return uri.getPathSegments().get(index - 1);
977 }
978 } else {
979 return null;
980 }
981 }
982
983 private int countUriSegments(Uri uri) {
984 if (uri == null) {
985 return 0;
986 }
987 return uri.getPathSegments().size() + 1;
988 }
989
Christopher Tate16aa9732012-09-17 16:23:44 -0700990 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -0800991 public void addObserverLocked(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700992 boolean notifyForDescendants, Object observersLock,
993 int uid, int pid, int userHandle) {
994 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
995 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 }
997
Fred Quintana002ffad52010-03-02 11:18:16 -0800998 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700999 boolean notifyForDescendants, Object observersLock,
1000 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 // If this is the leaf node add the observer
1002 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001003 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1004 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 return;
1006 }
1007
1008 // Look to see if the proper child already exists
1009 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -05001010 if (segment == null) {
1011 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1012 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 int N = mChildren.size();
1014 for (int i = 0; i < N; i++) {
1015 ObserverNode node = mChildren.get(i);
1016 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001017 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1018 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 return;
1020 }
1021 }
1022
1023 // No child found, create one
1024 ObserverNode node = new ObserverNode(segment);
1025 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -07001026 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1027 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
1029
Fred Quintana002ffad52010-03-02 11:18:16 -08001030 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 int size = mChildren.size();
1032 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001033 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 if (empty) {
1035 mChildren.remove(i);
1036 i--;
1037 size--;
1038 }
1039 }
1040
1041 IBinder observerBinder = observer.asBinder();
1042 size = mObservers.size();
1043 for (int i = 0; i < size; i++) {
1044 ObserverEntry entry = mObservers.get(i);
1045 if (entry.observer.asBinder() == observerBinder) {
1046 mObservers.remove(i);
1047 // We no longer need to listen for death notifications. Remove it.
1048 observerBinder.unlinkToDeath(entry, 0);
1049 break;
1050 }
1051 }
1052
1053 if (mChildren.size() == 0 && mObservers.size() == 0) {
1054 return true;
1055 }
1056 return false;
1057 }
1058
Fred Quintana002ffad52010-03-02 11:18:16 -08001059 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -07001060 boolean observerWantsSelfNotifications, int targetUserHandle,
1061 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 int N = mObservers.size();
1063 IBinder observerBinder = observer == null ? null : observer.asBinder();
1064 for (int i = 0; i < N; i++) {
1065 ObserverEntry entry = mObservers.get(i);
1066
Christopher Tate16aa9732012-09-17 16:23:44 -07001067 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -08001069 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1070 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 continue;
1072 }
1073
Christopher Tate16aa9732012-09-17 16:23:44 -07001074 // Does this observer match the target user?
1075 if (targetUserHandle == UserHandle.USER_ALL
1076 || entry.userHandle == UserHandle.USER_ALL
1077 || targetUserHandle == entry.userHandle) {
1078 // Make sure the observer is interested in the notification
1079 if (leaf || (!leaf && entry.notifyForDescendants)) {
1080 calls.add(new ObserverCall(this, entry.observer, selfChange));
1081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 }
1083 }
1084 }
1085
Christopher Tate16aa9732012-09-17 16:23:44 -07001086 /**
1087 * targetUserHandle is either a hard user handle or is USER_ALL
1088 */
Fred Quintana002ffad52010-03-02 11:18:16 -08001089 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -07001090 boolean observerWantsSelfNotifications, int targetUserHandle,
1091 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 String segment = null;
1093 int segmentCount = countUriSegments(uri);
1094 if (index >= segmentCount) {
1095 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -07001096 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1097 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 } else if (index < segmentCount){
1099 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -07001100 // Notify any observers at this level who are interested in descendants
1101 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1102 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 }
1104
1105 int N = mChildren.size();
1106 for (int i = 0; i < N; i++) {
1107 ObserverNode node = mChildren.get(i);
1108 if (segment == null || node.mName.equals(segment)) {
1109 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -08001110 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -07001111 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 if (segment != null) {
1113 break;
1114 }
1115 }
1116 }
1117 }
1118 }
1119}