blob: b766894d72b3568440c67d8fb037205dc140362c [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;
Benjamin Franzadea1912015-06-19 16:03:38 +010026import android.content.Intent;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080027import android.content.ISyncStatusObserver;
28import android.content.PeriodicSync;
29import 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;
Benjamin Franzadea1912015-06-19 16:03:38 +010033import android.content.pm.PackageManager;
Svetoslav0010b702015-06-30 18:05:26 -070034import android.content.pm.PackageManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.database.IContentObserver;
36import android.database.sqlite.SQLiteException;
37import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070038import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.Bundle;
40import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070041import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.os.RemoteException;
43import android.os.ServiceManager;
Dan Morrille4d9a012013-03-28 18:10:43 -070044import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070045import android.os.UserHandle;
Matthew Williams632515b2013-10-10 15:51:00 -070046import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.util.Log;
Jeff Sharkey60094592013-03-21 18:14:24 -070048import android.util.Slog;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070049import android.util.SparseIntArray;
Svetoslav0010b702015-06-30 18:05:26 -070050import com.android.server.LocalServices;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
52import java.io.FileDescriptor;
53import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070054import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070056import java.util.Collections;
57import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080058import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
60/**
61 * {@hide}
62 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070063public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 private static final String TAG = "ContentService";
65 private Context mContext;
66 private boolean mFactoryTest;
67 private final ObserverNode mRootNode = new ObserverNode("");
68 private SyncManager mSyncManager = null;
69 private final Object mSyncManagerLock = new Object();
70
71 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -070072 if (SystemProperties.getBoolean("config.disable_network", false)) {
73 return null;
74 }
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 synchronized(mSyncManagerLock) {
77 try {
78 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
79 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
80 } catch (SQLiteException e) {
81 Log.e(TAG, "Can't create SyncManager", e);
82 }
83 return mSyncManager;
84 }
85 }
86
87 @Override
88 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
89 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
90 "caller doesn't have the DUMP permission");
91
92 // This makes it so that future permission checks will be in the context of this
93 // process rather than the caller's process. We will restore this before returning.
94 long identityToken = clearCallingIdentity();
95 try {
96 if (mSyncManager == null) {
97 pw.println("No SyncManager created! (Disk full?)");
98 } else {
99 mSyncManager.dump(fd, pw);
100 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700101 pw.println();
102 pw.println("Observer tree:");
103 synchronized (mRootNode) {
104 int[] counts = new int[2];
105 final SparseIntArray pidCounts = new SparseIntArray();
106 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
107 pw.println();
108 ArrayList<Integer> sorted = new ArrayList<Integer>();
109 for (int i=0; i<pidCounts.size(); i++) {
110 sorted.add(pidCounts.keyAt(i));
111 }
112 Collections.sort(sorted, new Comparator<Integer>() {
113 @Override
114 public int compare(Integer lhs, Integer rhs) {
115 int lc = pidCounts.get(lhs);
116 int rc = pidCounts.get(rhs);
117 if (lc < rc) {
118 return 1;
119 } else if (lc > rc) {
120 return -1;
121 }
122 return 0;
123 }
124
125 });
126 for (int i=0; i<sorted.size(); i++) {
127 int pid = sorted.get(i);
128 pw.print(" pid "); pw.print(pid); pw.print(": ");
129 pw.print(pidCounts.get(pid)); pw.println(" observers");
130 }
131 pw.println();
132 pw.print(" Total number of nodes: "); pw.println(counts[0]);
133 pw.print(" Total number of observers: "); pw.println(counts[1]);
134 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 } finally {
136 restoreCallingIdentity(identityToken);
137 }
138 }
139
Dianne Hackborn231cc602009-04-27 17:10:36 -0700140 @Override
141 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
142 throws RemoteException {
143 try {
144 return super.onTransact(code, data, reply, flags);
145 } catch (RuntimeException e) {
146 // The content service only throws security exceptions, so let's
147 // log all others.
148 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700149 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700150 }
151 throw e;
152 }
153 }
154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 /*package*/ ContentService(Context context, boolean factoryTest) {
156 mContext = context;
157 mFactoryTest = factoryTest;
Svetoslav0010b702015-06-30 18:05:26 -0700158
159 // Let the package manager query for the sync adapters for a given authority
Svet Ganov52153f42015-08-11 08:59:12 -0700160 // as we grant default permissions to sync adapters for specific authorities.
Svetoslav0010b702015-06-30 18:05:26 -0700161 PackageManagerInternal packageManagerInternal = LocalServices.getService(
162 PackageManagerInternal.class);
163 packageManagerInternal.setSyncAdapterPackagesprovider(
164 new PackageManagerInternal.SyncAdapterPackagesProvider() {
165 @Override
166 public String[] getPackages(String authority, int userId) {
167 return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
168 }
169 });
Kenny Root26ff6622012-07-30 12:58:03 -0700170 }
171
172 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 getSyncManager();
174 }
175
Christopher Tate16aa9732012-09-17 16:23:44 -0700176 /**
177 * Register a content observer tied to a specific user's view of the provider.
178 * @param userHandle the user whose view of the provider is to be observed. May be
179 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100180 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
181 * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
182 * are forbidden.
Christopher Tate16aa9732012-09-17 16:23:44 -0700183 */
184 @Override
185 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
186 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 if (observer == null || uri == null) {
188 throw new IllegalArgumentException("You must pass a valid uri and observer");
189 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700190
Benjamin Franzadea1912015-06-19 16:03:38 +0100191 final int uid = Binder.getCallingUid();
192 final int pid = Binder.getCallingPid();
193 final int callingUserHandle = UserHandle.getCallingUserId();
194 // Registering an observer for any user other than the calling user requires uri grant or
195 // cross user permission
196 if (callingUserHandle != userHandle &&
197 mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
198 != PackageManager.PERMISSION_GRANTED) {
199 enforceCrossUserPermission(userHandle,
200 "no permission to observe other users' provider view");
201 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700202
203 if (userHandle < 0) {
204 if (userHandle == UserHandle.USER_CURRENT) {
205 userHandle = ActivityManager.getCurrentUser();
206 } else if (userHandle != UserHandle.USER_ALL) {
207 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
208 + userHandle);
209 }
210 }
211
212 synchronized (mRootNode) {
213 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
Benjamin Franzadea1912015-06-19 16:03:38 +0100214 uid, pid, userHandle);
Christopher Tate16aa9732012-09-17 16:23:44 -0700215 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
216 " with notifyForDescendants " + notifyForDescendants);
217 }
218 }
219
220 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
221 IContentObserver observer) {
222 registerContentObserver(uri, notifyForDescendants, observer,
223 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225
226 public void unregisterContentObserver(IContentObserver observer) {
227 if (observer == null) {
228 throw new IllegalArgumentException("You must pass a valid observer");
229 }
230 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800231 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700232 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 }
234 }
235
Christopher Tate16aa9732012-09-17 16:23:44 -0700236 /**
237 * Notify observers of a particular user's view of the provider.
238 * @param userHandle the user whose view of the provider is to be notified. May be
239 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100240 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
241 * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
242 * allowed.
Christopher Tate16aa9732012-09-17 16:23:44 -0700243 */
244 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 public void notifyChange(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700246 boolean observerWantsSelfNotifications, boolean syncToNetwork,
247 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700249 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
250 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800252
Benjamin Franzadea1912015-06-19 16:03:38 +0100253 final int uid = Binder.getCallingUid();
254 final int pid = Binder.getCallingPid();
Christopher Tate16aa9732012-09-17 16:23:44 -0700255 final int callingUserHandle = UserHandle.getCallingUserId();
Benjamin Franzadea1912015-06-19 16:03:38 +0100256 // Notify for any user other than the caller requires uri grant or cross user permission
257 if (callingUserHandle != userHandle &&
258 mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
259 != PackageManager.PERMISSION_GRANTED) {
260 enforceCrossUserPermission(userHandle, "no permission to notify other users");
Christopher Tate16aa9732012-09-17 16:23:44 -0700261 }
262
263 // We passed the permission check; resolve pseudouser targets as appropriate
264 if (userHandle < 0) {
265 if (userHandle == UserHandle.USER_CURRENT) {
266 userHandle = ActivityManager.getCurrentUser();
267 } else if (userHandle != UserHandle.USER_ALL) {
268 throw new InvalidParameterException("Bad user handle for notifyChange: "
269 + userHandle);
270 }
271 }
272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 // This makes it so that future permission checks will be in the context of this
274 // process rather than the caller's process. We will restore this before returning.
275 long identityToken = clearCallingIdentity();
276 try {
277 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
278 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800279 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700280 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282 final int numCalls = calls.size();
283 for (int i=0; i<numCalls; i++) {
284 ObserverCall oc = calls.get(i);
285 try {
Svetoslav86b1df22014-08-21 14:22:53 -0700286 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 if (Log.isLoggable(TAG, Log.VERBOSE)) {
288 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
289 }
290 } catch (RemoteException ex) {
291 synchronized (mRootNode) {
292 Log.w(TAG, "Found dead observer, removing");
293 IBinder binder = oc.mObserver.asBinder();
294 final ArrayList<ObserverNode.ObserverEntry> list
295 = oc.mNode.mObservers;
296 int numList = list.size();
297 for (int j=0; j<numList; j++) {
298 ObserverNode.ObserverEntry oe = list.get(j);
299 if (oe.observer.asBinder() == binder) {
300 list.remove(j);
301 j--;
302 numList--;
303 }
304 }
305 }
306 }
307 }
308 if (syncToNetwork) {
309 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700310 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700311 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800312 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700313 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315 } finally {
316 restoreCallingIdentity(identityToken);
317 }
318 }
319
Christopher Tate16aa9732012-09-17 16:23:44 -0700320 public void notifyChange(Uri uri, IContentObserver observer,
321 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
322 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
323 UserHandle.getCallingUserId());
324 }
325
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 /**
327 * Hide this class since it is not part of api,
328 * but current unittest framework requires it to be public
329 * @hide
330 *
331 */
332 public static final class ObserverCall {
333 final ObserverNode mNode;
334 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800335 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336
Jeff Brown86de0592012-01-23 13:01:18 -0800337 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 mNode = node;
339 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800340 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 }
343
Fred Quintanaac9385e2009-06-22 18:00:59 -0700344 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700346 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700347 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 // This makes it so that future permission checks will be in the context of this
350 // process rather than the caller's process. We will restore this before returning.
351 long identityToken = clearCallingIdentity();
352 try {
353 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700354 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700355 syncManager.scheduleSync(account, userId, uId, authority, extras,
356 0 /* no delay */, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700357 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700358 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 } finally {
360 restoreCallingIdentity(identityToken);
361 }
362 }
363
364 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700365 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
366 * either:
367 * periodic OR one-off sync.
368 * and
369 * anonymous OR provider sync.
370 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700371 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700372 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700373 public void sync(SyncRequest request) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100374 syncAsUser(request, UserHandle.getCallingUserId());
375 }
376
377 /**
378 * If the user id supplied is different to the calling user, the caller must hold the
379 * INTERACT_ACROSS_USERS_FULL permission.
380 */
381 public void syncAsUser(SyncRequest request, int userId) {
382 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700383 int callerUid = Binder.getCallingUid();
Matthew Williamsfa774182013-06-18 15:44:11 -0700384 // This makes it so that future permission checks will be in the context of this
385 // process rather than the caller's process. We will restore this before returning.
386 long identityToken = clearCallingIdentity();
387 try {
388 SyncManager syncManager = getSyncManager();
Matthew Williamsd08d6682013-10-14 10:39:41 -0700389 if (syncManager == null) {
390 return;
391 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700392
Matthew Williamsd08d6682013-10-14 10:39:41 -0700393 Bundle extras = request.getBundle();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700394 long flextime = request.getSyncFlexTime();
395 long runAtTime = request.getSyncRunTime();
396 if (request.isPeriodic()) {
397 mContext.enforceCallingOrSelfPermission(
398 Manifest.permission.WRITE_SYNC_SETTINGS,
399 "no permission to write the sync settings");
400 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700401 info = new SyncStorageEngine.EndPoint(
402 request.getAccount(), request.getProvider(), userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700403 if (runAtTime < 60) {
404 Slog.w(TAG, "Requested poll frequency of " + runAtTime
405 + " seconds being rounded up to 60 seconds.");
406 runAtTime = 60;
407 }
408 // Schedule periodic sync.
409 getSyncManager().getSyncStorageEngine()
410 .updateOrAddPeriodicSync(info, runAtTime, flextime, extras);
411 } else {
412 long beforeRuntimeMillis = (flextime) * 1000;
413 long runtimeMillis = runAtTime * 1000;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700414 syncManager.scheduleSync(
415 request.getAccount(), userId, callerUid, request.getProvider(), extras,
416 beforeRuntimeMillis, runtimeMillis,
417 false /* onlyThoseWithUnknownSyncableState */);
Matthew Williamsfa774182013-06-18 15:44:11 -0700418 }
419 } finally {
420 restoreCallingIdentity(identityToken);
421 }
422 }
423
424 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700426 * if they match the authority and account, if they are present.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700427 *
428 * @param account filter the pending and active syncs to cancel using this account, or null.
429 * @param authority filter the pending and active syncs to cancel using this authority, or
430 * null.
431 * @param cname cancel syncs running on this service, or null for provider/account.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700433 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700434 public void cancelSync(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100435 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
436 }
437
438 /**
439 * Clear all scheduled sync operations that match the uri and cancel the active sync
440 * if they match the authority and account, if they are present.
441 *
442 * <p> If the user id supplied is different to the calling user, the caller must hold the
443 * INTERACT_ACROSS_USERS_FULL permission.
444 *
445 * @param account filter the pending and active syncs to cancel using this account, or null.
446 * @param authority filter the pending and active syncs to cancel using this authority, or
447 * null.
448 * @param userId the user id for which to cancel sync operations.
449 * @param cname cancel syncs running on this service, or null for provider/account.
450 */
451 @Override
452 public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
453 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700454 if (authority != null && authority.length() == 0) {
455 throw new IllegalArgumentException("Authority must be non-empty");
456 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100457 enforceCrossUserPermission(userId,
458 "no permission to modify the sync settings for user " + userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 // This makes it so that future permission checks will be in the context of this
460 // process rather than the caller's process. We will restore this before returning.
461 long identityToken = clearCallingIdentity();
462 try {
463 SyncManager syncManager = getSyncManager();
464 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700465 SyncStorageEngine.EndPoint info;
466 if (cname == null) {
467 info = new SyncStorageEngine.EndPoint(account, authority, userId);
468 } else {
469 info = new SyncStorageEngine.EndPoint(cname, userId);
470 }
471 syncManager.clearScheduledSyncOperations(info);
472 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 }
474 } finally {
475 restoreCallingIdentity(identityToken);
476 }
477 }
478
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700479 public void cancelRequest(SyncRequest request) {
480 SyncManager syncManager = getSyncManager();
481 if (syncManager == null) return;
482 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700483
484 long identityToken = clearCallingIdentity();
485 try {
486 SyncStorageEngine.EndPoint info;
487 Bundle extras = new Bundle(request.getBundle());
Matthew Williams5a9decd2014-06-04 09:25:11 -0700488 Account account = request.getAccount();
489 String provider = request.getProvider();
490 info = new SyncStorageEngine.EndPoint(account, provider, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700491 if (request.isPeriodic()) {
492 // Remove periodic sync.
493 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
494 "no permission to write the sync settings");
495 getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras);
496 }
497 // Cancel active syncs and clear pending syncs from the queue.
498 syncManager.cancelScheduledSyncOperation(info, extras);
499 syncManager.cancelActiveSync(info, extras);
500 } finally {
501 restoreCallingIdentity(identityToken);
502 }
503 }
504
Fred Quintanaac9385e2009-06-22 18:00:59 -0700505 /**
506 * Get information about the SyncAdapters that are known to the system.
507 * @return an array of SyncAdapters that have registered with the system
508 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700509 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700510 public SyncAdapterType[] getSyncAdapterTypes() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100511 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
512 }
513
514 /**
515 * Get information about the SyncAdapters that are known to the system for a particular user.
516 *
517 * <p> If the user id supplied is different to the calling user, the caller must hold the
518 * INTERACT_ACROSS_USERS_FULL permission.
519 *
520 * @return an array of SyncAdapters that have registered with the system
521 */
522 @Override
523 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
524 enforceCrossUserPermission(userId,
525 "no permission to read sync settings for user " + userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700526 // This makes it so that future permission checks will be in the context of this
527 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700528 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700529 try {
530 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700531 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700532 } finally {
533 restoreCallingIdentity(identityToken);
534 }
535 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700536
Matthew Williamsfa774182013-06-18 15:44:11 -0700537 @Override
Amith Yamasani37a40c22015-06-17 13:25:42 -0700538 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
539 enforceCrossUserPermission(userId,
540 "no permission to read sync settings for user " + userId);
541 // This makes it so that future permission checks will be in the context of this
542 // process rather than the caller's process. We will restore this before returning.
543 final long identityToken = clearCallingIdentity();
544 try {
545 SyncManager syncManager = getSyncManager();
546 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
547 } finally {
548 restoreCallingIdentity(identityToken);
549 }
550 }
551
552 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700553 public boolean getSyncAutomatically(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100554 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
555 }
556
557 /**
558 * If the user id supplied is different to the calling user, the caller must hold the
559 * INTERACT_ACROSS_USERS_FULL permission.
560 */
561 @Override
562 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
563 enforceCrossUserPermission(userId,
564 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700565 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
566 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800567
Dianne Hackborn231cc602009-04-27 17:10:36 -0700568 long identityToken = clearCallingIdentity();
569 try {
570 SyncManager syncManager = getSyncManager();
571 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700572 return syncManager.getSyncStorageEngine()
573 .getSyncAutomatically(account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700574 }
575 } finally {
576 restoreCallingIdentity(identityToken);
577 }
578 return false;
579 }
580
Matthew Williamsfa774182013-06-18 15:44:11 -0700581 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700582 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100583 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
584 }
585
586 @Override
587 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
588 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700589 if (TextUtils.isEmpty(providerName)) {
590 throw new IllegalArgumentException("Authority must be non-empty");
591 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700592 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
593 "no permission to write the sync settings");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100594 enforceCrossUserPermission(userId,
595 "no permission to modify the sync settings for user " + userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800596
Dianne Hackborn231cc602009-04-27 17:10:36 -0700597 long identityToken = clearCallingIdentity();
598 try {
599 SyncManager syncManager = getSyncManager();
600 if (syncManager != null) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100601 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
602 providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700603 }
604 } finally {
605 restoreCallingIdentity(identityToken);
606 }
607 }
608
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700609 /** Old API. Schedule periodic sync with default flex time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700610 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800611 public void addPeriodicSync(Account account, String authority, Bundle extras,
612 long pollFrequency) {
Matthew Williams632515b2013-10-10 15:51:00 -0700613 if (account == null) {
614 throw new IllegalArgumentException("Account must not be null");
615 }
616 if (TextUtils.isEmpty(authority)) {
617 throw new IllegalArgumentException("Authority must not be empty.");
618 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800619 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
620 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800621
Matthew Williams632515b2013-10-10 15:51:00 -0700622 int userId = UserHandle.getCallingUserId();
Jeff Sharkey51366be2013-03-27 14:46:55 -0700623 if (pollFrequency < 60) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700624 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Jeff Sharkey51366be2013-03-27 14:46:55 -0700625 + " seconds being rounded up to 60 seconds.");
626 pollFrequency = 60;
Jeff Sharkey60094592013-03-21 18:14:24 -0700627 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700628 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
Jeff Sharkey60094592013-03-21 18:14:24 -0700629
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800630 long identityToken = clearCallingIdentity();
631 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700632 SyncStorageEngine.EndPoint info =
633 new SyncStorageEngine.EndPoint(account, authority, userId);
634 getSyncManager().getSyncStorageEngine()
635 .updateOrAddPeriodicSync(info,
636 pollFrequency,
637 defaultFlex,
638 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800639 } finally {
640 restoreCallingIdentity(identityToken);
641 }
642 }
643
644 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Matthew Williams632515b2013-10-10 15:51:00 -0700645 if (account == null) {
646 throw new IllegalArgumentException("Account must not be null");
647 }
648 if (TextUtils.isEmpty(authority)) {
649 throw new IllegalArgumentException("Authority must not be empty");
650 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800651 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
652 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800653
Matthew Williams632515b2013-10-10 15:51:00 -0700654 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800655 long identityToken = clearCallingIdentity();
656 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700657 getSyncManager().getSyncStorageEngine()
658 .removePeriodicSync(
659 new SyncStorageEngine.EndPoint(account, authority, userId),
660 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800661 } finally {
662 restoreCallingIdentity(identityToken);
663 }
664 }
665
Matthew Williamsfa774182013-06-18 15:44:11 -0700666
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700667 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
668 ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700669 if (account == null) {
670 throw new IllegalArgumentException("Account must not be null");
671 }
672 if (TextUtils.isEmpty(providerName)) {
673 throw new IllegalArgumentException("Authority must not be empty");
674 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800675 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
676 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800677
Matthew Williams632515b2013-10-10 15:51:00 -0700678 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800679 long identityToken = clearCallingIdentity();
680 try {
Matthew Williams5a9decd2014-06-04 09:25:11 -0700681 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
682 new SyncStorageEngine.EndPoint(account, providerName, userId));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800683 } finally {
684 restoreCallingIdentity(identityToken);
685 }
686 }
687
Fred Quintana5e787c42009-08-16 23:13:53 -0700688 public int getIsSyncable(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100689 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
690 }
691
692 /**
693 * If the user id supplied is different to the calling user, the caller must hold the
694 * INTERACT_ACROSS_USERS_FULL permission.
695 */
696 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
697 enforceCrossUserPermission(userId,
698 "no permission to read the sync settings for user " + userId);
Fred Quintana5e787c42009-08-16 23:13:53 -0700699 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
700 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800701
Fred Quintana5e787c42009-08-16 23:13:53 -0700702 long identityToken = clearCallingIdentity();
703 try {
704 SyncManager syncManager = getSyncManager();
705 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700706 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800707 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700708 }
709 } finally {
710 restoreCallingIdentity(identityToken);
711 }
712 return -1;
713 }
714
715 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700716 if (TextUtils.isEmpty(providerName)) {
717 throw new IllegalArgumentException("Authority must not be empty");
718 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700719 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
720 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800721
Matthew Williams632515b2013-10-10 15:51:00 -0700722 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700723 long identityToken = clearCallingIdentity();
724 try {
725 SyncManager syncManager = getSyncManager();
726 if (syncManager != null) {
727 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800728 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700729 }
730 } finally {
731 restoreCallingIdentity(identityToken);
732 }
733 }
734
Matthew Williamsfa774182013-06-18 15:44:11 -0700735 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700736 public boolean getMasterSyncAutomatically() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100737 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
738 }
739
740 /**
741 * If the user id supplied is different to the calling user, the caller must hold the
742 * INTERACT_ACROSS_USERS_FULL permission.
743 */
744 @Override
745 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
746 enforceCrossUserPermission(userId,
747 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700748 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
749 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800750
Dianne Hackborn231cc602009-04-27 17:10:36 -0700751 long identityToken = clearCallingIdentity();
752 try {
753 SyncManager syncManager = getSyncManager();
754 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800755 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700756 }
757 } finally {
758 restoreCallingIdentity(identityToken);
759 }
760 return false;
761 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700762
Matthew Williamsfa774182013-06-18 15:44:11 -0700763 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700764 public void setMasterSyncAutomatically(boolean flag) {
Alexandra Gherghina0e9ac202014-07-15 23:11:48 +0100765 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
766 }
767
768 @Override
769 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
770 enforceCrossUserPermission(userId,
771 "no permission to set the sync status for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700772 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
773 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800774
Dianne Hackborn231cc602009-04-27 17:10:36 -0700775 long identityToken = clearCallingIdentity();
776 try {
777 SyncManager syncManager = getSyncManager();
778 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800779 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700780 }
781 } finally {
782 restoreCallingIdentity(identityToken);
783 }
784 }
785
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700786 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700787 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
788 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700789 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700790 int callingUid = Binder.getCallingUid();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700791 long identityToken = clearCallingIdentity();
792 try {
793 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700794 if (syncManager == null) {
795 return false;
796 }
Matthew Williams5a9decd2014-06-04 09:25:11 -0700797 return syncManager.getSyncStorageEngine().isSyncActive(
798 new SyncStorageEngine.EndPoint(account, authority, userId));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700799 } finally {
800 restoreCallingIdentity(identityToken);
801 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700802 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700803
Fred Quintanac6a69552010-09-27 17:05:04 -0700804 public List<SyncInfo> getCurrentSyncs() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100805 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
806 }
807
808 /**
809 * If the user id supplied is different to the calling user, the caller must hold the
810 * INTERACT_ACROSS_USERS_FULL permission.
811 */
812 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
813 enforceCrossUserPermission(userId,
814 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700815 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
816 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800817
Dianne Hackborn231cc602009-04-27 17:10:36 -0700818 long identityToken = clearCallingIdentity();
819 try {
Matthew Williamsa7456e42013-11-12 14:41:02 -0800820 return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700821 } finally {
822 restoreCallingIdentity(identityToken);
823 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700824 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700825
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700826 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100827 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
828 }
829
830 /**
831 * If the user id supplied is different to the calling user, the caller must hold the
832 * INTERACT_ACROSS_USERS_FULL permission.
833 */
834 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
835 ComponentName cname, int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700836 if (TextUtils.isEmpty(authority)) {
837 throw new IllegalArgumentException("Authority must not be empty");
838 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100839
840 enforceCrossUserPermission(userId,
841 "no permission to read the sync stats for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700842 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
843 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800844
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700845 int callerUid = Binder.getCallingUid();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700846 long identityToken = clearCallingIdentity();
847 try {
848 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700849 if (syncManager == null) {
850 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700851 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700852 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700853 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700854 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700855 } else {
856 throw new IllegalArgumentException("Must call sync status with valid authority");
857 }
858 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700859 } finally {
860 restoreCallingIdentity(identityToken);
861 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700862 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700863
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700864 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100865 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
866 }
867
868 @Override
869 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
870 int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700871 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
872 "no permission to read the sync stats");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100873 enforceCrossUserPermission(userId,
874 "no permission to retrieve the sync settings for user " + userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700875 int callerUid = Binder.getCallingUid();
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000876 long identityToken = clearCallingIdentity();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700877 SyncManager syncManager = getSyncManager();
878 if (syncManager == null) return false;
879
Dianne Hackborn231cc602009-04-27 17:10:36 -0700880 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700881 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700882 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700883 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700884 } else {
885 throw new IllegalArgumentException("Invalid authority specified");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700886 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700887 return syncManager.getSyncStorageEngine().isSyncPending(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700888 } finally {
889 restoreCallingIdentity(identityToken);
890 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700891 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700892
Dianne Hackborn231cc602009-04-27 17:10:36 -0700893 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
894 long identityToken = clearCallingIdentity();
895 try {
896 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800897 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700898 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700899 }
900 } finally {
901 restoreCallingIdentity(identityToken);
902 }
903 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700904
Dianne Hackborn231cc602009-04-27 17:10:36 -0700905 public void removeStatusChangeListener(ISyncStatusObserver callback) {
906 long identityToken = clearCallingIdentity();
907 try {
908 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800909 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700910 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700911 }
912 } finally {
913 restoreCallingIdentity(identityToken);
914 }
915 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700916
Kenny Root26ff6622012-07-30 12:58:03 -0700917 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700919 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 return service;
921 }
922
923 /**
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100924 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
925 * permission, if the userHandle is not for the caller.
926 *
927 * @param userHandle the user handle of the user we want to act on behalf of.
928 * @param message the message to log on security exception.
929 */
930 private void enforceCrossUserPermission(int userHandle, String message) {
931 final int callingUser = UserHandle.getCallingUserId();
932 if (callingUser != userHandle) {
933 mContext.enforceCallingOrSelfPermission(
934 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
935 }
936 }
937
938 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 * Hide this class since it is not part of api,
940 * but current unittest framework requires it to be public
941 * @hide
942 */
943 public static final class ObserverNode {
944 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800945 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700946 public final int uid;
947 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700948 public final boolean notifyForDescendants;
949 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -0800950 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700952 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Christopher Tate16aa9732012-09-17 16:23:44 -0700953 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800954 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700956 uid = _uid;
957 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700958 userHandle = _userHandle;
959 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 try {
961 observer.asBinder().linkToDeath(this, 0);
962 } catch (RemoteException e) {
963 binderDied();
964 }
965 }
966
967 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800968 synchronized (observersLock) {
969 removeObserverLocked(observer);
970 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700972
973 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
974 String name, String prefix, SparseIntArray pidCounts) {
975 pidCounts.put(pid, pidCounts.get(pid)+1);
976 pw.print(prefix); pw.print(name); pw.print(": pid=");
977 pw.print(pid); pw.print(" uid=");
Christopher Tate16aa9732012-09-17 16:23:44 -0700978 pw.print(uid); pw.print(" user=");
979 pw.print(userHandle); pw.print(" target=");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700980 pw.println(Integer.toHexString(System.identityHashCode(
981 observer != null ? observer.asBinder() : null)));
982 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 }
984
985 public static final int INSERT_TYPE = 0;
986 public static final int UPDATE_TYPE = 1;
987 public static final int DELETE_TYPE = 2;
988
989 private String mName;
990 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
991 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
992
993 public ObserverNode(String name) {
994 mName = name;
995 }
996
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700997 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
998 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
999 String innerName = null;
1000 if (mObservers.size() > 0) {
1001 if ("".equals(name)) {
1002 innerName = mName;
1003 } else {
1004 innerName = name + "/" + mName;
1005 }
1006 for (int i=0; i<mObservers.size(); i++) {
1007 counts[1]++;
1008 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1009 pidCounts);
1010 }
1011 }
1012 if (mChildren.size() > 0) {
1013 if (innerName == null) {
1014 if ("".equals(name)) {
1015 innerName = mName;
1016 } else {
1017 innerName = name + "/" + mName;
1018 }
1019 }
1020 for (int i=0; i<mChildren.size(); i++) {
1021 counts[0]++;
1022 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1023 counts, pidCounts);
1024 }
1025 }
1026 }
1027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 private String getUriSegment(Uri uri, int index) {
1029 if (uri != null) {
1030 if (index == 0) {
1031 return uri.getAuthority();
1032 } else {
1033 return uri.getPathSegments().get(index - 1);
1034 }
1035 } else {
1036 return null;
1037 }
1038 }
1039
1040 private int countUriSegments(Uri uri) {
1041 if (uri == null) {
1042 return 0;
1043 }
1044 return uri.getPathSegments().size() + 1;
1045 }
1046
Christopher Tate16aa9732012-09-17 16:23:44 -07001047 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -08001048 public void addObserverLocked(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -07001049 boolean notifyForDescendants, Object observersLock,
1050 int uid, int pid, int userHandle) {
1051 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1052 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 }
1054
Fred Quintana002ffad52010-03-02 11:18:16 -08001055 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -07001056 boolean notifyForDescendants, Object observersLock,
1057 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 // If this is the leaf node add the observer
1059 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001060 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1061 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 return;
1063 }
1064
1065 // Look to see if the proper child already exists
1066 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -05001067 if (segment == null) {
1068 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1069 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 int N = mChildren.size();
1071 for (int i = 0; i < N; i++) {
1072 ObserverNode node = mChildren.get(i);
1073 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001074 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1075 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 return;
1077 }
1078 }
1079
1080 // No child found, create one
1081 ObserverNode node = new ObserverNode(segment);
1082 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -07001083 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1084 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 }
1086
Fred Quintana002ffad52010-03-02 11:18:16 -08001087 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 int size = mChildren.size();
1089 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001090 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 if (empty) {
1092 mChildren.remove(i);
1093 i--;
1094 size--;
1095 }
1096 }
1097
1098 IBinder observerBinder = observer.asBinder();
1099 size = mObservers.size();
1100 for (int i = 0; i < size; i++) {
1101 ObserverEntry entry = mObservers.get(i);
1102 if (entry.observer.asBinder() == observerBinder) {
1103 mObservers.remove(i);
1104 // We no longer need to listen for death notifications. Remove it.
1105 observerBinder.unlinkToDeath(entry, 0);
1106 break;
1107 }
1108 }
1109
1110 if (mChildren.size() == 0 && mObservers.size() == 0) {
1111 return true;
1112 }
1113 return false;
1114 }
1115
Fred Quintana002ffad52010-03-02 11:18:16 -08001116 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -07001117 boolean observerWantsSelfNotifications, int targetUserHandle,
1118 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 int N = mObservers.size();
1120 IBinder observerBinder = observer == null ? null : observer.asBinder();
1121 for (int i = 0; i < N; i++) {
1122 ObserverEntry entry = mObservers.get(i);
1123
Christopher Tate16aa9732012-09-17 16:23:44 -07001124 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -08001126 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1127 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 continue;
1129 }
1130
Christopher Tate16aa9732012-09-17 16:23:44 -07001131 // Does this observer match the target user?
1132 if (targetUserHandle == UserHandle.USER_ALL
1133 || entry.userHandle == UserHandle.USER_ALL
1134 || targetUserHandle == entry.userHandle) {
1135 // Make sure the observer is interested in the notification
1136 if (leaf || (!leaf && entry.notifyForDescendants)) {
1137 calls.add(new ObserverCall(this, entry.observer, selfChange));
1138 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 }
1140 }
1141 }
1142
Christopher Tate16aa9732012-09-17 16:23:44 -07001143 /**
1144 * targetUserHandle is either a hard user handle or is USER_ALL
1145 */
Fred Quintana002ffad52010-03-02 11:18:16 -08001146 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -07001147 boolean observerWantsSelfNotifications, int targetUserHandle,
1148 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 String segment = null;
1150 int segmentCount = countUriSegments(uri);
1151 if (index >= segmentCount) {
1152 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -07001153 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1154 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 } else if (index < segmentCount){
1156 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -07001157 // Notify any observers at this level who are interested in descendants
1158 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1159 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
1161
1162 int N = mChildren.size();
1163 for (int i = 0; i < N; i++) {
1164 ObserverNode node = mChildren.get(i);
1165 if (segment == null || node.mName.equals(segment)) {
1166 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -08001167 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -07001168 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 if (segment != null) {
1170 break;
1171 }
1172 }
1173 }
1174 }
1175 }
1176}