blob: 7f7ea9d8060c8cced21f877176e863f91af479bb [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;
Jeff Sharkey87314082016-03-11 17:25:11 -070021import android.annotation.Nullable;
Christopher Tate16aa9732012-09-17 16:23:44 -070022import android.app.ActivityManager;
Jeff Sharkey87314082016-03-11 17:25:11 -070023import android.app.AppOpsManager;
24import android.content.BroadcastReceiver;
Matthew Williamsfa774182013-06-18 15:44:11 -070025import android.content.ComponentName;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.content.ContentResolver;
27import android.content.Context;
28import android.content.IContentService;
Jeff Sharkey7732e1e2016-03-30 17:14:23 -060029import android.content.ISyncStatusObserver;
Benjamin Franzadea1912015-06-19 16:03:38 +010030import android.content.Intent;
Jeff Sharkey87314082016-03-11 17:25:11 -070031import android.content.IntentFilter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080032import android.content.PeriodicSync;
33import android.content.SyncAdapterType;
34import android.content.SyncInfo;
Matthew Williamsfa774182013-06-18 15:44:11 -070035import android.content.SyncRequest;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080036import android.content.SyncStatusInfo;
Benjamin Franzadea1912015-06-19 16:03:38 +010037import android.content.pm.PackageManager;
Svetoslav0010b702015-06-30 18:05:26 -070038import android.content.pm.PackageManagerInternal;
Jeff Sharkey87314082016-03-11 17:25:11 -070039import android.content.pm.ProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.database.IContentObserver;
41import android.database.sqlite.SQLiteException;
42import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070043import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.Bundle;
45import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070046import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.RemoteException;
48import android.os.ServiceManager;
Dan Morrille4d9a012013-03-28 18:10:43 -070049import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070050import android.os.UserHandle;
Matthew Williams632515b2013-10-10 15:51:00 -070051import android.text.TextUtils;
Jeff Sharkey87314082016-03-11 17:25:11 -070052import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.util.Log;
Jeff Sharkey87314082016-03-11 17:25:11 -070054import android.util.Pair;
Jeff Sharkey60094592013-03-21 18:14:24 -070055import android.util.Slog;
Jeff Sharkey87314082016-03-11 17:25:11 -070056import android.util.SparseArray;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070057import android.util.SparseIntArray;
Jeff Sharkey87314082016-03-11 17:25:11 -070058
59import com.android.internal.annotations.GuardedBy;
60import com.android.internal.util.IndentingPrintWriter;
Svetoslav0010b702015-06-30 18:05:26 -070061import com.android.server.LocalServices;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
63import java.io.FileDescriptor;
64import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070065import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070067import java.util.Collections;
68import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080069import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
71/**
72 * {@hide}
73 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070074public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 private static final String TAG = "ContentService";
Jeff Sharkey87314082016-03-11 17:25:11 -070076
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 private Context mContext;
78 private boolean mFactoryTest;
Jeff Sharkey87314082016-03-11 17:25:11 -070079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 private final ObserverNode mRootNode = new ObserverNode("");
Jeff Sharkey87314082016-03-11 17:25:11 -070081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private SyncManager mSyncManager = null;
83 private final Object mSyncManagerLock = new Object();
84
Jeff Sharkey87314082016-03-11 17:25:11 -070085 /**
86 * Map from userId to providerPackageName to [clientPackageName, uri] to
87 * value. This structure is carefully optimized to keep invalidation logic
88 * as cheap as possible.
89 */
90 @GuardedBy("mCache")
91 private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
92 mCache = new SparseArray<>();
93
94 private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
95 @Override
96 public void onReceive(Context context, Intent intent) {
97 final Uri data = intent.getData();
98 if (data != null) {
99 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
100 UserHandle.USER_NULL);
101 final String packageName = data.getSchemeSpecificPart();
102 invalidateCacheLocked(userId, packageName, null);
103 }
104 }
105 };
106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -0700108 if (SystemProperties.getBoolean("config.disable_network", false)) {
109 return null;
110 }
111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 synchronized(mSyncManagerLock) {
113 try {
114 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
115 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
116 } catch (SQLiteException e) {
117 Log.e(TAG, "Can't create SyncManager", e);
118 }
119 return mSyncManager;
120 }
121 }
122
123 @Override
Jeff Sharkey87314082016-03-11 17:25:11 -0700124 protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
126 "caller doesn't have the DUMP permission");
127
Jeff Sharkey87314082016-03-11 17:25:11 -0700128 final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 // This makes it so that future permission checks will be in the context of this
131 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey87314082016-03-11 17:25:11 -0700132 final long identityToken = clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 try {
134 if (mSyncManager == null) {
135 pw.println("No SyncManager created! (Disk full?)");
136 } else {
137 mSyncManager.dump(fd, pw);
138 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700139 pw.println();
140 pw.println("Observer tree:");
141 synchronized (mRootNode) {
142 int[] counts = new int[2];
143 final SparseIntArray pidCounts = new SparseIntArray();
144 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
145 pw.println();
146 ArrayList<Integer> sorted = new ArrayList<Integer>();
147 for (int i=0; i<pidCounts.size(); i++) {
148 sorted.add(pidCounts.keyAt(i));
149 }
150 Collections.sort(sorted, new Comparator<Integer>() {
151 @Override
152 public int compare(Integer lhs, Integer rhs) {
153 int lc = pidCounts.get(lhs);
154 int rc = pidCounts.get(rhs);
155 if (lc < rc) {
156 return 1;
157 } else if (lc > rc) {
158 return -1;
159 }
160 return 0;
161 }
162
163 });
164 for (int i=0; i<sorted.size(); i++) {
165 int pid = sorted.get(i);
166 pw.print(" pid "); pw.print(pid); pw.print(": ");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000167 pw.print(pidCounts.get(pid)); pw.println(" observers");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700168 }
169 pw.println();
170 pw.print(" Total number of nodes: "); pw.println(counts[0]);
171 pw.print(" Total number of observers: "); pw.println(counts[1]);
172 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700173
174 synchronized (mCache) {
175 pw.println();
176 pw.println("Cached content:");
177 pw.increaseIndent();
178 for (int i = 0; i < mCache.size(); i++) {
179 pw.println("User " + mCache.keyAt(i) + ":");
180 pw.increaseIndent();
181 pw.println(mCache.valueAt(i));
182 pw.decreaseIndent();
183 }
184 pw.decreaseIndent();
185 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 } finally {
187 restoreCallingIdentity(identityToken);
188 }
189 }
190
Dianne Hackborn231cc602009-04-27 17:10:36 -0700191 @Override
192 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
193 throws RemoteException {
194 try {
195 return super.onTransact(code, data, reply, flags);
196 } catch (RuntimeException e) {
197 // The content service only throws security exceptions, so let's
198 // log all others.
199 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700200 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700201 }
202 throw e;
203 }
204 }
205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 /*package*/ ContentService(Context context, boolean factoryTest) {
207 mContext = context;
208 mFactoryTest = factoryTest;
Svetoslav0010b702015-06-30 18:05:26 -0700209
210 // Let the package manager query for the sync adapters for a given authority
Svet Ganov52153f42015-08-11 08:59:12 -0700211 // as we grant default permissions to sync adapters for specific authorities.
Svetoslav0010b702015-06-30 18:05:26 -0700212 PackageManagerInternal packageManagerInternal = LocalServices.getService(
213 PackageManagerInternal.class);
214 packageManagerInternal.setSyncAdapterPackagesprovider(
215 new PackageManagerInternal.SyncAdapterPackagesProvider() {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000216 @Override
217 public String[] getPackages(String authority, int userId) {
218 return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
219 }
220 });
Jeff Sharkey87314082016-03-11 17:25:11 -0700221
222 final IntentFilter packageFilter = new IntentFilter();
223 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
224 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
225 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
226 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
227 packageFilter.addDataScheme("package");
228 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
229 packageFilter, null, null);
Kenny Root26ff6622012-07-30 12:58:03 -0700230 }
231
232 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 getSyncManager();
234 }
235
Christopher Tate16aa9732012-09-17 16:23:44 -0700236 /**
237 * Register a content observer tied to a specific user's view of the provider.
238 * @param userHandle the user whose view of the provider is to be observed. 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 read uri grant to the uri.
241 * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
242 * are forbidden.
Christopher Tate16aa9732012-09-17 16:23:44 -0700243 */
244 @Override
245 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000246 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 if (observer == null || uri == null) {
248 throw new IllegalArgumentException("You must pass a valid uri and observer");
249 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700250
Benjamin Franzadea1912015-06-19 16:03:38 +0100251 final int uid = Binder.getCallingUid();
252 final int pid = Binder.getCallingPid();
253 final int callingUserHandle = UserHandle.getCallingUserId();
254 // Registering an observer for any user other than the calling user requires uri grant or
255 // cross user permission
256 if (callingUserHandle != userHandle &&
257 mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
258 != PackageManager.PERMISSION_GRANTED) {
259 enforceCrossUserPermission(userHandle,
260 "no permission to observe other users' provider view");
261 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700262
263 if (userHandle < 0) {
264 if (userHandle == UserHandle.USER_CURRENT) {
265 userHandle = ActivityManager.getCurrentUser();
266 } else if (userHandle != UserHandle.USER_ALL) {
267 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
268 + userHandle);
269 }
270 }
271
272 synchronized (mRootNode) {
273 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
Benjamin Franzadea1912015-06-19 16:03:38 +0100274 uid, pid, userHandle);
Christopher Tate16aa9732012-09-17 16:23:44 -0700275 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
276 " with notifyForDescendants " + notifyForDescendants);
277 }
278 }
279
280 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000281 IContentObserver observer) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700282 registerContentObserver(uri, notifyForDescendants, observer,
283 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
285
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600286 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 public void unregisterContentObserver(IContentObserver observer) {
288 if (observer == null) {
289 throw new IllegalArgumentException("You must pass a valid observer");
290 }
291 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800292 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700293 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
295 }
296
Christopher Tate16aa9732012-09-17 16:23:44 -0700297 /**
298 * Notify observers of a particular user's view of the provider.
299 * @param userHandle the user whose view of the provider is to be notified. May be
300 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100301 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
302 * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
303 * allowed.
Christopher Tate16aa9732012-09-17 16:23:44 -0700304 */
305 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 public void notifyChange(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000307 boolean observerWantsSelfNotifications, boolean syncToNetwork,
308 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700310 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
311 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800313
Benjamin Franzadea1912015-06-19 16:03:38 +0100314 final int uid = Binder.getCallingUid();
315 final int pid = Binder.getCallingPid();
Christopher Tate16aa9732012-09-17 16:23:44 -0700316 final int callingUserHandle = UserHandle.getCallingUserId();
Benjamin Franzadea1912015-06-19 16:03:38 +0100317 // Notify for any user other than the caller requires uri grant or cross user permission
318 if (callingUserHandle != userHandle &&
319 mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
320 != PackageManager.PERMISSION_GRANTED) {
321 enforceCrossUserPermission(userHandle, "no permission to notify other users");
Christopher Tate16aa9732012-09-17 16:23:44 -0700322 }
323
324 // We passed the permission check; resolve pseudouser targets as appropriate
325 if (userHandle < 0) {
326 if (userHandle == UserHandle.USER_CURRENT) {
327 userHandle = ActivityManager.getCurrentUser();
328 } else if (userHandle != UserHandle.USER_ALL) {
329 throw new InvalidParameterException("Bad user handle for notifyChange: "
330 + userHandle);
331 }
332 }
333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 // This makes it so that future permission checks will be in the context of this
335 // process rather than the caller's process. We will restore this before returning.
336 long identityToken = clearCallingIdentity();
337 try {
338 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
339 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800340 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700341 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 }
343 final int numCalls = calls.size();
344 for (int i=0; i<numCalls; i++) {
345 ObserverCall oc = calls.get(i);
346 try {
Svetoslav86b1df22014-08-21 14:22:53 -0700347 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 if (Log.isLoggable(TAG, Log.VERBOSE)) {
349 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
350 }
351 } catch (RemoteException ex) {
352 synchronized (mRootNode) {
353 Log.w(TAG, "Found dead observer, removing");
354 IBinder binder = oc.mObserver.asBinder();
355 final ArrayList<ObserverNode.ObserverEntry> list
356 = oc.mNode.mObservers;
357 int numList = list.size();
358 for (int j=0; j<numList; j++) {
359 ObserverNode.ObserverEntry oe = list.get(j);
360 if (oe.observer.asBinder() == binder) {
361 list.remove(j);
362 j--;
363 numList--;
364 }
365 }
366 }
367 }
368 }
369 if (syncToNetwork) {
370 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700371 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700372 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800373 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700376
377 synchronized (mCache) {
378 final String providerPackageName = getProviderPackageName(uri);
379 invalidateCacheLocked(userHandle, providerPackageName, uri);
380 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 } finally {
382 restoreCallingIdentity(identityToken);
383 }
384 }
385
Christopher Tate16aa9732012-09-17 16:23:44 -0700386 public void notifyChange(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000387 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700388 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
389 UserHandle.getCallingUserId());
390 }
391
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 /**
393 * Hide this class since it is not part of api,
394 * but current unittest framework requires it to be public
395 * @hide
396 *
397 */
398 public static final class ObserverCall {
399 final ObserverNode mNode;
400 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800401 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402
Jeff Brown86de0592012-01-23 13:01:18 -0800403 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mNode = node;
405 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800406 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 }
408 }
409
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600410 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700411 public void requestSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600412 Bundle.setDefusable(extras, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700414 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700415 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 // This makes it so that future permission checks will be in the context of this
418 // process rather than the caller's process. We will restore this before returning.
419 long identityToken = clearCallingIdentity();
420 try {
421 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700422 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700423 syncManager.scheduleSync(account, userId, uId, authority, extras,
424 0 /* no delay */, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700425 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700426 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 } finally {
428 restoreCallingIdentity(identityToken);
429 }
430 }
431
432 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700433 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
434 * either:
435 * periodic OR one-off sync.
436 * and
437 * anonymous OR provider sync.
438 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700439 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700440 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600441 @Override
Matthew Williamsfa774182013-06-18 15:44:11 -0700442 public void sync(SyncRequest request) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100443 syncAsUser(request, UserHandle.getCallingUserId());
444 }
445
446 /**
447 * If the user id supplied is different to the calling user, the caller must hold the
448 * INTERACT_ACROSS_USERS_FULL permission.
449 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600450 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100451 public void syncAsUser(SyncRequest request, int userId) {
452 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700453 int callerUid = Binder.getCallingUid();
Matthew Williamsfa774182013-06-18 15:44:11 -0700454 // This makes it so that future permission checks will be in the context of this
455 // process rather than the caller's process. We will restore this before returning.
456 long identityToken = clearCallingIdentity();
457 try {
458 SyncManager syncManager = getSyncManager();
Matthew Williamsd08d6682013-10-14 10:39:41 -0700459 if (syncManager == null) {
460 return;
461 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700462
Matthew Williamsd08d6682013-10-14 10:39:41 -0700463 Bundle extras = request.getBundle();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700464 long flextime = request.getSyncFlexTime();
465 long runAtTime = request.getSyncRunTime();
466 if (request.isPeriodic()) {
467 mContext.enforceCallingOrSelfPermission(
468 Manifest.permission.WRITE_SYNC_SETTINGS,
469 "no permission to write the sync settings");
470 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700471 info = new SyncStorageEngine.EndPoint(
472 request.getAccount(), request.getProvider(), userId);
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000473 if (runAtTime < 3600) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700474 Slog.w(TAG, "Requested poll frequency of " + runAtTime
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000475 + " seconds being rounded up to 1 hour.");
476 runAtTime = 3600;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700477 }
478 // Schedule periodic sync.
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000479 getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
480 flextime, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700481 } else {
482 long beforeRuntimeMillis = (flextime) * 1000;
483 long runtimeMillis = runAtTime * 1000;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700484 syncManager.scheduleSync(
485 request.getAccount(), userId, callerUid, request.getProvider(), extras,
486 beforeRuntimeMillis, runtimeMillis,
487 false /* onlyThoseWithUnknownSyncableState */);
Matthew Williamsfa774182013-06-18 15:44:11 -0700488 }
489 } finally {
490 restoreCallingIdentity(identityToken);
491 }
492 }
493
494 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700496 * if they match the authority and account, if they are present.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700497 *
498 * @param account filter the pending and active syncs to cancel using this account, or null.
499 * @param authority filter the pending and active syncs to cancel using this authority, or
500 * null.
501 * @param cname cancel syncs running on this service, or null for provider/account.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700503 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700504 public void cancelSync(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100505 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
506 }
507
508 /**
509 * Clear all scheduled sync operations that match the uri and cancel the active sync
510 * if they match the authority and account, if they are present.
511 *
512 * <p> If the user id supplied is different to the calling user, the caller must hold the
513 * INTERACT_ACROSS_USERS_FULL permission.
514 *
515 * @param account filter the pending and active syncs to cancel using this account, or null.
516 * @param authority filter the pending and active syncs to cancel using this authority, or
517 * null.
518 * @param userId the user id for which to cancel sync operations.
519 * @param cname cancel syncs running on this service, or null for provider/account.
520 */
521 @Override
522 public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000523 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700524 if (authority != null && authority.length() == 0) {
525 throw new IllegalArgumentException("Authority must be non-empty");
526 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100527 enforceCrossUserPermission(userId,
528 "no permission to modify the sync settings for user " + userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 // This makes it so that future permission checks will be in the context of this
530 // process rather than the caller's process. We will restore this before returning.
531 long identityToken = clearCallingIdentity();
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000532 if (cname != null) {
533 Slog.e(TAG, "cname not null.");
534 return;
535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 try {
537 SyncManager syncManager = getSyncManager();
538 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700539 SyncStorageEngine.EndPoint info;
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000540 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700541 syncManager.clearScheduledSyncOperations(info);
542 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 }
544 } finally {
545 restoreCallingIdentity(identityToken);
546 }
547 }
548
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600549 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700550 public void cancelRequest(SyncRequest request) {
551 SyncManager syncManager = getSyncManager();
552 if (syncManager == null) return;
553 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700554
555 long identityToken = clearCallingIdentity();
556 try {
557 SyncStorageEngine.EndPoint info;
558 Bundle extras = new Bundle(request.getBundle());
Matthew Williams5a9decd2014-06-04 09:25:11 -0700559 Account account = request.getAccount();
560 String provider = request.getProvider();
561 info = new SyncStorageEngine.EndPoint(account, provider, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700562 if (request.isPeriodic()) {
563 // Remove periodic sync.
564 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
565 "no permission to write the sync settings");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000566 getSyncManager().removePeriodicSync(info, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700567 }
568 // Cancel active syncs and clear pending syncs from the queue.
569 syncManager.cancelScheduledSyncOperation(info, extras);
570 syncManager.cancelActiveSync(info, extras);
571 } finally {
572 restoreCallingIdentity(identityToken);
573 }
574 }
575
Fred Quintanaac9385e2009-06-22 18:00:59 -0700576 /**
577 * Get information about the SyncAdapters that are known to the system.
578 * @return an array of SyncAdapters that have registered with the system
579 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700580 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700581 public SyncAdapterType[] getSyncAdapterTypes() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100582 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
583 }
584
585 /**
586 * Get information about the SyncAdapters that are known to the system for a particular user.
587 *
588 * <p> If the user id supplied is different to the calling user, the caller must hold the
589 * INTERACT_ACROSS_USERS_FULL permission.
590 *
591 * @return an array of SyncAdapters that have registered with the system
592 */
593 @Override
594 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
595 enforceCrossUserPermission(userId,
596 "no permission to read sync settings for user " + userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700597 // This makes it so that future permission checks will be in the context of this
598 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700599 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700600 try {
601 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700602 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700603 } finally {
604 restoreCallingIdentity(identityToken);
605 }
606 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700607
Matthew Williamsfa774182013-06-18 15:44:11 -0700608 @Override
Amith Yamasani37a40c22015-06-17 13:25:42 -0700609 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
610 enforceCrossUserPermission(userId,
611 "no permission to read sync settings for user " + userId);
612 // This makes it so that future permission checks will be in the context of this
613 // process rather than the caller's process. We will restore this before returning.
614 final long identityToken = clearCallingIdentity();
615 try {
616 SyncManager syncManager = getSyncManager();
617 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
618 } finally {
619 restoreCallingIdentity(identityToken);
620 }
621 }
622
623 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700624 public boolean getSyncAutomatically(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100625 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
626 }
627
628 /**
629 * If the user id supplied is different to the calling user, the caller must hold the
630 * INTERACT_ACROSS_USERS_FULL permission.
631 */
632 @Override
633 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
634 enforceCrossUserPermission(userId,
635 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700636 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
637 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800638
Dianne Hackborn231cc602009-04-27 17:10:36 -0700639 long identityToken = clearCallingIdentity();
640 try {
641 SyncManager syncManager = getSyncManager();
642 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700643 return syncManager.getSyncStorageEngine()
644 .getSyncAutomatically(account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700645 }
646 } finally {
647 restoreCallingIdentity(identityToken);
648 }
649 return false;
650 }
651
Matthew Williamsfa774182013-06-18 15:44:11 -0700652 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700653 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100654 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
655 }
656
657 @Override
658 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000659 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700660 if (TextUtils.isEmpty(providerName)) {
661 throw new IllegalArgumentException("Authority must be non-empty");
662 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700663 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
664 "no permission to write the sync settings");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100665 enforceCrossUserPermission(userId,
666 "no permission to modify the sync settings for user " + userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800667
Dianne Hackborn231cc602009-04-27 17:10:36 -0700668 long identityToken = clearCallingIdentity();
669 try {
670 SyncManager syncManager = getSyncManager();
671 if (syncManager != null) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100672 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
673 providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700674 }
675 } finally {
676 restoreCallingIdentity(identityToken);
677 }
678 }
679
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000680 /** Old API. Schedule periodic sync with default flexMillis time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700681 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800682 public void addPeriodicSync(Account account, String authority, Bundle extras,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000683 long pollFrequency) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600684 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700685 if (account == null) {
686 throw new IllegalArgumentException("Account must not be null");
687 }
688 if (TextUtils.isEmpty(authority)) {
689 throw new IllegalArgumentException("Authority must not be empty.");
690 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800691 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
692 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800693
Matthew Williams632515b2013-10-10 15:51:00 -0700694 int userId = UserHandle.getCallingUserId();
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000695 if (pollFrequency < 3600) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700696 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000697 + " seconds being rounded up to 1 hour.");
698 pollFrequency = 3600;
Jeff Sharkey60094592013-03-21 18:14:24 -0700699 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700700 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
Jeff Sharkey60094592013-03-21 18:14:24 -0700701
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800702 long identityToken = clearCallingIdentity();
703 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700704 SyncStorageEngine.EndPoint info =
705 new SyncStorageEngine.EndPoint(account, authority, userId);
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000706 getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
707 defaultFlex, extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800708 } finally {
709 restoreCallingIdentity(identityToken);
710 }
711 }
712
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600713 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800714 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600715 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700716 if (account == null) {
717 throw new IllegalArgumentException("Account must not be null");
718 }
719 if (TextUtils.isEmpty(authority)) {
720 throw new IllegalArgumentException("Authority must not be empty");
721 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800722 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
723 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800724
Matthew Williams632515b2013-10-10 15:51:00 -0700725 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800726 long identityToken = clearCallingIdentity();
727 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000728 getSyncManager()
729 .removePeriodicSync(
730 new SyncStorageEngine.EndPoint(account, authority, userId),
731 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800732 } finally {
733 restoreCallingIdentity(identityToken);
734 }
735 }
736
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600737 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700738 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000739 ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700740 if (account == null) {
741 throw new IllegalArgumentException("Account must not be null");
742 }
743 if (TextUtils.isEmpty(providerName)) {
744 throw new IllegalArgumentException("Authority must not be empty");
745 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800746 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
747 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800748
Matthew Williams632515b2013-10-10 15:51:00 -0700749 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800750 long identityToken = clearCallingIdentity();
751 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000752 return getSyncManager().getPeriodicSyncs(
Matthew Williams5a9decd2014-06-04 09:25:11 -0700753 new SyncStorageEngine.EndPoint(account, providerName, userId));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800754 } finally {
755 restoreCallingIdentity(identityToken);
756 }
757 }
758
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600759 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700760 public int getIsSyncable(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100761 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
762 }
763
764 /**
765 * If the user id supplied is different to the calling user, the caller must hold the
766 * INTERACT_ACROSS_USERS_FULL permission.
767 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600768 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100769 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
770 enforceCrossUserPermission(userId,
771 "no permission to read the sync settings for user " + userId);
Fred Quintana5e787c42009-08-16 23:13:53 -0700772 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
773 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800774
Fred Quintana5e787c42009-08-16 23:13:53 -0700775 long identityToken = clearCallingIdentity();
776 try {
777 SyncManager syncManager = getSyncManager();
778 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700779 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800780 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700781 }
782 } finally {
783 restoreCallingIdentity(identityToken);
784 }
785 return -1;
786 }
787
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600788 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700789 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700790 if (TextUtils.isEmpty(providerName)) {
791 throw new IllegalArgumentException("Authority must not be empty");
792 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700793 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
794 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800795
Matthew Williams632515b2013-10-10 15:51:00 -0700796 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700797 long identityToken = clearCallingIdentity();
798 try {
799 SyncManager syncManager = getSyncManager();
800 if (syncManager != null) {
801 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800802 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700803 }
804 } finally {
805 restoreCallingIdentity(identityToken);
806 }
807 }
808
Matthew Williamsfa774182013-06-18 15:44:11 -0700809 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700810 public boolean getMasterSyncAutomatically() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100811 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
812 }
813
814 /**
815 * If the user id supplied is different to the calling user, the caller must hold the
816 * INTERACT_ACROSS_USERS_FULL permission.
817 */
818 @Override
819 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
820 enforceCrossUserPermission(userId,
821 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700822 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
823 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800824
Dianne Hackborn231cc602009-04-27 17:10:36 -0700825 long identityToken = clearCallingIdentity();
826 try {
827 SyncManager syncManager = getSyncManager();
828 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800829 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700830 }
831 } finally {
832 restoreCallingIdentity(identityToken);
833 }
834 return false;
835 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700836
Matthew Williamsfa774182013-06-18 15:44:11 -0700837 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700838 public void setMasterSyncAutomatically(boolean flag) {
Alexandra Gherghina0e9ac202014-07-15 23:11:48 +0100839 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
840 }
841
842 @Override
843 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
844 enforceCrossUserPermission(userId,
845 "no permission to set the sync status for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700846 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
847 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800848
Dianne Hackborn231cc602009-04-27 17:10:36 -0700849 long identityToken = clearCallingIdentity();
850 try {
851 SyncManager syncManager = getSyncManager();
852 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800853 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700854 }
855 } finally {
856 restoreCallingIdentity(identityToken);
857 }
858 }
859
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600860 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700861 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700862 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
863 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700864 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700865 long identityToken = clearCallingIdentity();
866 try {
867 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700868 if (syncManager == null) {
869 return false;
870 }
Matthew Williams5a9decd2014-06-04 09:25:11 -0700871 return syncManager.getSyncStorageEngine().isSyncActive(
872 new SyncStorageEngine.EndPoint(account, authority, userId));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700873 } finally {
874 restoreCallingIdentity(identityToken);
875 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700876 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700877
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600878 @Override
Fred Quintanac6a69552010-09-27 17:05:04 -0700879 public List<SyncInfo> getCurrentSyncs() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100880 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
881 }
882
883 /**
884 * If the user id supplied is different to the calling user, the caller must hold the
885 * INTERACT_ACROSS_USERS_FULL permission.
886 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600887 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100888 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
889 enforceCrossUserPermission(userId,
890 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700891 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
892 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800893
Matthew Williamsf39549e2016-01-19 23:04:04 +0000894 final boolean canAccessAccounts =
895 mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
896 == PackageManager.PERMISSION_GRANTED;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700897 long identityToken = clearCallingIdentity();
898 try {
Matthew Williamsf39549e2016-01-19 23:04:04 +0000899 return getSyncManager().getSyncStorageEngine()
900 .getCurrentSyncsCopy(userId, canAccessAccounts);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700901 } finally {
902 restoreCallingIdentity(identityToken);
903 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700904 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700905
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600906 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700907 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100908 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
909 }
910
911 /**
912 * If the user id supplied is different to the calling user, the caller must hold the
913 * INTERACT_ACROSS_USERS_FULL permission.
914 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600915 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100916 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000917 ComponentName cname, int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700918 if (TextUtils.isEmpty(authority)) {
919 throw new IllegalArgumentException("Authority must not be empty");
920 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100921
922 enforceCrossUserPermission(userId,
923 "no permission to read the sync stats for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700924 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
925 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800926
Dianne Hackborn231cc602009-04-27 17:10:36 -0700927 long identityToken = clearCallingIdentity();
928 try {
929 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700930 if (syncManager == null) {
931 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700932 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700933 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700934 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700935 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700936 } else {
937 throw new IllegalArgumentException("Must call sync status with valid authority");
938 }
939 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700940 } finally {
941 restoreCallingIdentity(identityToken);
942 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700943 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700944
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600945 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700946 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100947 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
948 }
949
950 @Override
951 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000952 int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700953 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
954 "no permission to read the sync stats");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100955 enforceCrossUserPermission(userId,
956 "no permission to retrieve the sync settings for user " + userId);
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000957 long identityToken = clearCallingIdentity();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700958 SyncManager syncManager = getSyncManager();
959 if (syncManager == null) return false;
960
Dianne Hackborn231cc602009-04-27 17:10:36 -0700961 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700962 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700963 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700964 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700965 } else {
966 throw new IllegalArgumentException("Invalid authority specified");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700967 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700968 return syncManager.getSyncStorageEngine().isSyncPending(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700969 } finally {
970 restoreCallingIdentity(identityToken);
971 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700972 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700973
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600974 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -0700975 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
976 long identityToken = clearCallingIdentity();
977 try {
978 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800979 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700980 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700981 }
982 } finally {
983 restoreCallingIdentity(identityToken);
984 }
985 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700986
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600987 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -0700988 public void removeStatusChangeListener(ISyncStatusObserver callback) {
989 long identityToken = clearCallingIdentity();
990 try {
991 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800992 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700993 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700994 }
995 } finally {
996 restoreCallingIdentity(identityToken);
997 }
998 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700999
Jeff Sharkey87314082016-03-11 17:25:11 -07001000 private @Nullable String getProviderPackageName(Uri uri) {
1001 final ProviderInfo pi = mContext.getPackageManager()
1002 .resolveContentProvider(uri.getAuthority(), 0);
1003 return (pi != null) ? pi.packageName : null;
1004 }
1005
1006 private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1007 String providerPackageName) {
1008 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1009 if (userCache == null) {
1010 userCache = new ArrayMap<>();
1011 mCache.put(userId, userCache);
1012 }
1013 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1014 if (packageCache == null) {
1015 packageCache = new ArrayMap<>();
1016 userCache.put(providerPackageName, packageCache);
1017 }
1018 return packageCache;
1019 }
1020
1021 private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1022 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1023 if (userCache == null) return;
1024
1025 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1026 if (packageCache == null) return;
1027
1028 if (uri != null) {
1029 for (int i = 0; i < packageCache.size();) {
Jeff Sharkey7732e1e2016-03-30 17:14:23 -06001030 final Pair<String, Uri> key = packageCache.keyAt(i);
1031 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
1032 Slog.d(TAG, "Invalidating cache for key " + key);
Jeff Sharkey87314082016-03-11 17:25:11 -07001033 packageCache.removeAt(i);
1034 } else {
1035 i++;
1036 }
1037 }
1038 } else {
Jeff Sharkey7732e1e2016-03-30 17:14:23 -06001039 Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
Jeff Sharkey87314082016-03-11 17:25:11 -07001040 packageCache.clear();
1041 }
1042 }
1043
1044 @Override
1045 public void putCache(String packageName, Uri key, Bundle value, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001046 Bundle.setDefusable(value, true);
Jeff Sharkey87314082016-03-11 17:25:11 -07001047 enforceCrossUserPermission(userId, TAG);
1048 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1049 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1050 packageName);
1051
1052 final String providerPackageName = getProviderPackageName(key);
1053 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1054
1055 synchronized (mCache) {
1056 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1057 providerPackageName);
1058 if (value != null) {
1059 cache.put(fullKey, value);
1060 } else {
1061 cache.remove(fullKey);
1062 }
1063 }
1064 }
1065
1066 @Override
1067 public Bundle getCache(String packageName, Uri key, int userId) {
1068 enforceCrossUserPermission(userId, TAG);
1069 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1070 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1071 packageName);
1072
1073 final String providerPackageName = getProviderPackageName(key);
1074 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1075
1076 synchronized (mCache) {
1077 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1078 providerPackageName);
1079 return cache.get(fullKey);
1080 }
1081 }
1082
Kenny Root26ff6622012-07-30 12:58:03 -07001083 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001085 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 return service;
1087 }
1088
1089 /**
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +01001090 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1091 * permission, if the userHandle is not for the caller.
1092 *
1093 * @param userHandle the user handle of the user we want to act on behalf of.
1094 * @param message the message to log on security exception.
1095 */
1096 private void enforceCrossUserPermission(int userHandle, String message) {
1097 final int callingUser = UserHandle.getCallingUserId();
1098 if (callingUser != userHandle) {
1099 mContext.enforceCallingOrSelfPermission(
1100 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1101 }
1102 }
1103
1104 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 * Hide this class since it is not part of api,
1106 * but current unittest framework requires it to be public
1107 * @hide
1108 */
1109 public static final class ObserverNode {
1110 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -08001111 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001112 public final int uid;
1113 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001114 public final boolean notifyForDescendants;
1115 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -08001116 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001118 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001119 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001120 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001122 uid = _uid;
1123 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001124 userHandle = _userHandle;
1125 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 try {
1127 observer.asBinder().linkToDeath(this, 0);
1128 } catch (RemoteException e) {
1129 binderDied();
1130 }
1131 }
1132
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001133 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -08001135 synchronized (observersLock) {
1136 removeObserverLocked(observer);
1137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001139
1140 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001141 String name, String prefix, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001142 pidCounts.put(pid, pidCounts.get(pid)+1);
1143 pw.print(prefix); pw.print(name); pw.print(": pid=");
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001144 pw.print(pid); pw.print(" uid=");
1145 pw.print(uid); pw.print(" user=");
1146 pw.print(userHandle); pw.print(" target=");
1147 pw.println(Integer.toHexString(System.identityHashCode(
1148 observer != null ? observer.asBinder() : null)));
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001149 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 }
1151
1152 public static final int INSERT_TYPE = 0;
1153 public static final int UPDATE_TYPE = 1;
1154 public static final int DELETE_TYPE = 2;
1155
1156 private String mName;
1157 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1158 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1159
1160 public ObserverNode(String name) {
1161 mName = name;
1162 }
1163
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001164 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001165 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001166 String innerName = null;
1167 if (mObservers.size() > 0) {
1168 if ("".equals(name)) {
1169 innerName = mName;
1170 } else {
1171 innerName = name + "/" + mName;
1172 }
1173 for (int i=0; i<mObservers.size(); i++) {
1174 counts[1]++;
1175 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1176 pidCounts);
1177 }
1178 }
1179 if (mChildren.size() > 0) {
1180 if (innerName == null) {
1181 if ("".equals(name)) {
1182 innerName = mName;
1183 } else {
1184 innerName = name + "/" + mName;
1185 }
1186 }
1187 for (int i=0; i<mChildren.size(); i++) {
1188 counts[0]++;
1189 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1190 counts, pidCounts);
1191 }
1192 }
1193 }
1194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 private String getUriSegment(Uri uri, int index) {
1196 if (uri != null) {
1197 if (index == 0) {
1198 return uri.getAuthority();
1199 } else {
1200 return uri.getPathSegments().get(index - 1);
1201 }
1202 } else {
1203 return null;
1204 }
1205 }
1206
1207 private int countUriSegments(Uri uri) {
1208 if (uri == null) {
1209 return 0;
1210 }
1211 return uri.getPathSegments().size() + 1;
1212 }
1213
Christopher Tate16aa9732012-09-17 16:23:44 -07001214 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -08001215 public void addObserverLocked(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001216 boolean notifyForDescendants, Object observersLock,
1217 int uid, int pid, int userHandle) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001218 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1219 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 }
1221
Fred Quintana002ffad52010-03-02 11:18:16 -08001222 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001223 boolean notifyForDescendants, Object observersLock,
1224 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 // If this is the leaf node add the observer
1226 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001227 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1228 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 return;
1230 }
1231
1232 // Look to see if the proper child already exists
1233 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -05001234 if (segment == null) {
1235 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 int N = mChildren.size();
1238 for (int i = 0; i < N; i++) {
1239 ObserverNode node = mChildren.get(i);
1240 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001241 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1242 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 return;
1244 }
1245 }
1246
1247 // No child found, create one
1248 ObserverNode node = new ObserverNode(segment);
1249 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -07001250 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1251 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 }
1253
Fred Quintana002ffad52010-03-02 11:18:16 -08001254 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 int size = mChildren.size();
1256 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001257 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 if (empty) {
1259 mChildren.remove(i);
1260 i--;
1261 size--;
1262 }
1263 }
1264
1265 IBinder observerBinder = observer.asBinder();
1266 size = mObservers.size();
1267 for (int i = 0; i < size; i++) {
1268 ObserverEntry entry = mObservers.get(i);
1269 if (entry.observer.asBinder() == observerBinder) {
1270 mObservers.remove(i);
1271 // We no longer need to listen for death notifications. Remove it.
1272 observerBinder.unlinkToDeath(entry, 0);
1273 break;
1274 }
1275 }
1276
1277 if (mChildren.size() == 0 && mObservers.size() == 0) {
1278 return true;
1279 }
1280 return false;
1281 }
1282
Fred Quintana002ffad52010-03-02 11:18:16 -08001283 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001284 boolean observerWantsSelfNotifications, int targetUserHandle,
1285 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 int N = mObservers.size();
1287 IBinder observerBinder = observer == null ? null : observer.asBinder();
1288 for (int i = 0; i < N; i++) {
1289 ObserverEntry entry = mObservers.get(i);
1290
Christopher Tate16aa9732012-09-17 16:23:44 -07001291 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -08001293 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1294 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 continue;
1296 }
1297
Christopher Tate16aa9732012-09-17 16:23:44 -07001298 // Does this observer match the target user?
1299 if (targetUserHandle == UserHandle.USER_ALL
1300 || entry.userHandle == UserHandle.USER_ALL
1301 || targetUserHandle == entry.userHandle) {
1302 // Make sure the observer is interested in the notification
1303 if (leaf || (!leaf && entry.notifyForDescendants)) {
1304 calls.add(new ObserverCall(this, entry.observer, selfChange));
1305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 }
1307 }
1308 }
1309
Christopher Tate16aa9732012-09-17 16:23:44 -07001310 /**
1311 * targetUserHandle is either a hard user handle or is USER_ALL
1312 */
Fred Quintana002ffad52010-03-02 11:18:16 -08001313 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001314 boolean observerWantsSelfNotifications, int targetUserHandle,
1315 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 String segment = null;
1317 int segmentCount = countUriSegments(uri);
1318 if (index >= segmentCount) {
1319 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -07001320 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1321 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 } else if (index < segmentCount){
1323 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -07001324 // Notify any observers at this level who are interested in descendants
1325 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1326 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 }
1328
1329 int N = mChildren.size();
1330 for (int i = 0; i < N; i++) {
1331 ObserverNode node = mChildren.get(i);
1332 if (segment == null || node.mName.equals(segment)) {
1333 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -08001334 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -07001335 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 if (segment != null) {
1337 break;
1338 }
1339 }
1340 }
1341 }
1342 }
1343}