blob: 28170f2a6b1c2c48ea04611e841d155aaa674bd2 [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;
Benjamin Franzadea1912015-06-19 16:03:38 +010029import android.content.Intent;
Jeff Sharkey87314082016-03-11 17:25:11 -070030import android.content.IntentFilter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080031import android.content.ISyncStatusObserver;
32import 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;
61import com.android.internal.util.Preconditions;
Svetoslav0010b702015-06-30 18:05:26 -070062import com.android.server.LocalServices;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64import java.io.FileDescriptor;
65import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070066import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import java.util.ArrayList;
Jeff Sharkey87314082016-03-11 17:25:11 -070068import java.util.Arrays;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070069import java.util.Collections;
70import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080071import java.util.List;
Jeff Sharkey87314082016-03-11 17:25:11 -070072import java.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74/**
75 * {@hide}
76 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070077public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private static final String TAG = "ContentService";
Jeff Sharkey87314082016-03-11 17:25:11 -070079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 private Context mContext;
81 private boolean mFactoryTest;
Jeff Sharkey87314082016-03-11 17:25:11 -070082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private final ObserverNode mRootNode = new ObserverNode("");
Jeff Sharkey87314082016-03-11 17:25:11 -070084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private SyncManager mSyncManager = null;
86 private final Object mSyncManagerLock = new Object();
87
Jeff Sharkey87314082016-03-11 17:25:11 -070088 /**
89 * Map from userId to providerPackageName to [clientPackageName, uri] to
90 * value. This structure is carefully optimized to keep invalidation logic
91 * as cheap as possible.
92 */
93 @GuardedBy("mCache")
94 private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
95 mCache = new SparseArray<>();
96
97 private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
98 @Override
99 public void onReceive(Context context, Intent intent) {
100 final Uri data = intent.getData();
101 if (data != null) {
102 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
103 UserHandle.USER_NULL);
104 final String packageName = data.getSchemeSpecificPart();
105 invalidateCacheLocked(userId, packageName, null);
106 }
107 }
108 };
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -0700111 if (SystemProperties.getBoolean("config.disable_network", false)) {
112 return null;
113 }
114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 synchronized(mSyncManagerLock) {
116 try {
117 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
118 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
119 } catch (SQLiteException e) {
120 Log.e(TAG, "Can't create SyncManager", e);
121 }
122 return mSyncManager;
123 }
124 }
125
126 @Override
Jeff Sharkey87314082016-03-11 17:25:11 -0700127 protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
129 "caller doesn't have the DUMP permission");
130
Jeff Sharkey87314082016-03-11 17:25:11 -0700131 final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 // This makes it so that future permission checks will be in the context of this
134 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey87314082016-03-11 17:25:11 -0700135 final long identityToken = clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 try {
137 if (mSyncManager == null) {
138 pw.println("No SyncManager created! (Disk full?)");
139 } else {
140 mSyncManager.dump(fd, pw);
141 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700142 pw.println();
143 pw.println("Observer tree:");
144 synchronized (mRootNode) {
145 int[] counts = new int[2];
146 final SparseIntArray pidCounts = new SparseIntArray();
147 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
148 pw.println();
149 ArrayList<Integer> sorted = new ArrayList<Integer>();
150 for (int i=0; i<pidCounts.size(); i++) {
151 sorted.add(pidCounts.keyAt(i));
152 }
153 Collections.sort(sorted, new Comparator<Integer>() {
154 @Override
155 public int compare(Integer lhs, Integer rhs) {
156 int lc = pidCounts.get(lhs);
157 int rc = pidCounts.get(rhs);
158 if (lc < rc) {
159 return 1;
160 } else if (lc > rc) {
161 return -1;
162 }
163 return 0;
164 }
165
166 });
167 for (int i=0; i<sorted.size(); i++) {
168 int pid = sorted.get(i);
169 pw.print(" pid "); pw.print(pid); pw.print(": ");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000170 pw.print(pidCounts.get(pid)); pw.println(" observers");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700171 }
172 pw.println();
173 pw.print(" Total number of nodes: "); pw.println(counts[0]);
174 pw.print(" Total number of observers: "); pw.println(counts[1]);
175 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700176
177 synchronized (mCache) {
178 pw.println();
179 pw.println("Cached content:");
180 pw.increaseIndent();
181 for (int i = 0; i < mCache.size(); i++) {
182 pw.println("User " + mCache.keyAt(i) + ":");
183 pw.increaseIndent();
184 pw.println(mCache.valueAt(i));
185 pw.decreaseIndent();
186 }
187 pw.decreaseIndent();
188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 } finally {
190 restoreCallingIdentity(identityToken);
191 }
192 }
193
Dianne Hackborn231cc602009-04-27 17:10:36 -0700194 @Override
195 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
196 throws RemoteException {
197 try {
198 return super.onTransact(code, data, reply, flags);
199 } catch (RuntimeException e) {
200 // The content service only throws security exceptions, so let's
201 // log all others.
202 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700203 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700204 }
205 throw e;
206 }
207 }
208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 /*package*/ ContentService(Context context, boolean factoryTest) {
210 mContext = context;
211 mFactoryTest = factoryTest;
Svetoslav0010b702015-06-30 18:05:26 -0700212
213 // Let the package manager query for the sync adapters for a given authority
Svet Ganov52153f42015-08-11 08:59:12 -0700214 // as we grant default permissions to sync adapters for specific authorities.
Svetoslav0010b702015-06-30 18:05:26 -0700215 PackageManagerInternal packageManagerInternal = LocalServices.getService(
216 PackageManagerInternal.class);
217 packageManagerInternal.setSyncAdapterPackagesprovider(
218 new PackageManagerInternal.SyncAdapterPackagesProvider() {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000219 @Override
220 public String[] getPackages(String authority, int userId) {
221 return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
222 }
223 });
Jeff Sharkey87314082016-03-11 17:25:11 -0700224
225 final IntentFilter packageFilter = new IntentFilter();
226 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
227 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
228 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
229 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
230 packageFilter.addDataScheme("package");
231 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
232 packageFilter, null, null);
Kenny Root26ff6622012-07-30 12:58:03 -0700233 }
234
235 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 getSyncManager();
237 }
238
Christopher Tate16aa9732012-09-17 16:23:44 -0700239 /**
240 * Register a content observer tied to a specific user's view of the provider.
241 * @param userHandle the user whose view of the provider is to be observed. May be
242 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100243 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
244 * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
245 * are forbidden.
Christopher Tate16aa9732012-09-17 16:23:44 -0700246 */
247 @Override
248 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000249 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 if (observer == null || uri == null) {
251 throw new IllegalArgumentException("You must pass a valid uri and observer");
252 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700253
Benjamin Franzadea1912015-06-19 16:03:38 +0100254 final int uid = Binder.getCallingUid();
255 final int pid = Binder.getCallingPid();
256 final int callingUserHandle = UserHandle.getCallingUserId();
257 // Registering an observer for any user other than the calling user requires uri grant or
258 // cross user permission
259 if (callingUserHandle != userHandle &&
260 mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
261 != PackageManager.PERMISSION_GRANTED) {
262 enforceCrossUserPermission(userHandle,
263 "no permission to observe other users' provider view");
264 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700265
266 if (userHandle < 0) {
267 if (userHandle == UserHandle.USER_CURRENT) {
268 userHandle = ActivityManager.getCurrentUser();
269 } else if (userHandle != UserHandle.USER_ALL) {
270 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
271 + userHandle);
272 }
273 }
274
275 synchronized (mRootNode) {
276 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
Benjamin Franzadea1912015-06-19 16:03:38 +0100277 uid, pid, userHandle);
Christopher Tate16aa9732012-09-17 16:23:44 -0700278 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
279 " with notifyForDescendants " + notifyForDescendants);
280 }
281 }
282
283 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000284 IContentObserver observer) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700285 registerContentObserver(uri, notifyForDescendants, observer,
286 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600289 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 public void unregisterContentObserver(IContentObserver observer) {
291 if (observer == null) {
292 throw new IllegalArgumentException("You must pass a valid observer");
293 }
294 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800295 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700296 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
298 }
299
Christopher Tate16aa9732012-09-17 16:23:44 -0700300 /**
301 * Notify observers of a particular user's view of the provider.
302 * @param userHandle the user whose view of the provider is to be notified. May be
303 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100304 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
305 * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
306 * allowed.
Christopher Tate16aa9732012-09-17 16:23:44 -0700307 */
308 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 public void notifyChange(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000310 boolean observerWantsSelfNotifications, boolean syncToNetwork,
311 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700313 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
314 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800316
Benjamin Franzadea1912015-06-19 16:03:38 +0100317 final int uid = Binder.getCallingUid();
318 final int pid = Binder.getCallingPid();
Christopher Tate16aa9732012-09-17 16:23:44 -0700319 final int callingUserHandle = UserHandle.getCallingUserId();
Benjamin Franzadea1912015-06-19 16:03:38 +0100320 // Notify for any user other than the caller requires uri grant or cross user permission
321 if (callingUserHandle != userHandle &&
322 mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
323 != PackageManager.PERMISSION_GRANTED) {
324 enforceCrossUserPermission(userHandle, "no permission to notify other users");
Christopher Tate16aa9732012-09-17 16:23:44 -0700325 }
326
327 // We passed the permission check; resolve pseudouser targets as appropriate
328 if (userHandle < 0) {
329 if (userHandle == UserHandle.USER_CURRENT) {
330 userHandle = ActivityManager.getCurrentUser();
331 } else if (userHandle != UserHandle.USER_ALL) {
332 throw new InvalidParameterException("Bad user handle for notifyChange: "
333 + userHandle);
334 }
335 }
336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 // This makes it so that future permission checks will be in the context of this
338 // process rather than the caller's process. We will restore this before returning.
339 long identityToken = clearCallingIdentity();
340 try {
341 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
342 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800343 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700344 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
346 final int numCalls = calls.size();
347 for (int i=0; i<numCalls; i++) {
348 ObserverCall oc = calls.get(i);
349 try {
Svetoslav86b1df22014-08-21 14:22:53 -0700350 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 if (Log.isLoggable(TAG, Log.VERBOSE)) {
352 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
353 }
354 } catch (RemoteException ex) {
355 synchronized (mRootNode) {
356 Log.w(TAG, "Found dead observer, removing");
357 IBinder binder = oc.mObserver.asBinder();
358 final ArrayList<ObserverNode.ObserverEntry> list
359 = oc.mNode.mObservers;
360 int numList = list.size();
361 for (int j=0; j<numList; j++) {
362 ObserverNode.ObserverEntry oe = list.get(j);
363 if (oe.observer.asBinder() == binder) {
364 list.remove(j);
365 j--;
366 numList--;
367 }
368 }
369 }
370 }
371 }
372 if (syncToNetwork) {
373 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700374 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700375 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800376 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700377 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700379
380 synchronized (mCache) {
381 final String providerPackageName = getProviderPackageName(uri);
382 invalidateCacheLocked(userHandle, providerPackageName, uri);
383 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 } finally {
385 restoreCallingIdentity(identityToken);
386 }
387 }
388
Christopher Tate16aa9732012-09-17 16:23:44 -0700389 public void notifyChange(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000390 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700391 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
392 UserHandle.getCallingUserId());
393 }
394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 /**
396 * Hide this class since it is not part of api,
397 * but current unittest framework requires it to be public
398 * @hide
399 *
400 */
401 public static final class ObserverCall {
402 final ObserverNode mNode;
403 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800404 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405
Jeff Brown86de0592012-01-23 13:01:18 -0800406 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 mNode = node;
408 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800409 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 }
411 }
412
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600413 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700414 public void requestSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600415 Bundle.setDefusable(extras, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700417 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700418 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 // This makes it so that future permission checks will be in the context of this
421 // process rather than the caller's process. We will restore this before returning.
422 long identityToken = clearCallingIdentity();
423 try {
424 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700425 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700426 syncManager.scheduleSync(account, userId, uId, authority, extras,
427 0 /* no delay */, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700428 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 } finally {
431 restoreCallingIdentity(identityToken);
432 }
433 }
434
435 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700436 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
437 * either:
438 * periodic OR one-off sync.
439 * and
440 * anonymous OR provider sync.
441 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700442 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700443 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600444 @Override
Matthew Williamsfa774182013-06-18 15:44:11 -0700445 public void sync(SyncRequest request) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100446 syncAsUser(request, UserHandle.getCallingUserId());
447 }
448
449 /**
450 * If the user id supplied is different to the calling user, the caller must hold the
451 * INTERACT_ACROSS_USERS_FULL permission.
452 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600453 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100454 public void syncAsUser(SyncRequest request, int userId) {
455 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700456 int callerUid = Binder.getCallingUid();
Matthew Williamsfa774182013-06-18 15:44:11 -0700457 // This makes it so that future permission checks will be in the context of this
458 // process rather than the caller's process. We will restore this before returning.
459 long identityToken = clearCallingIdentity();
460 try {
461 SyncManager syncManager = getSyncManager();
Matthew Williamsd08d6682013-10-14 10:39:41 -0700462 if (syncManager == null) {
463 return;
464 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700465
Matthew Williamsd08d6682013-10-14 10:39:41 -0700466 Bundle extras = request.getBundle();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700467 long flextime = request.getSyncFlexTime();
468 long runAtTime = request.getSyncRunTime();
469 if (request.isPeriodic()) {
470 mContext.enforceCallingOrSelfPermission(
471 Manifest.permission.WRITE_SYNC_SETTINGS,
472 "no permission to write the sync settings");
473 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700474 info = new SyncStorageEngine.EndPoint(
475 request.getAccount(), request.getProvider(), userId);
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000476 if (runAtTime < 3600) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700477 Slog.w(TAG, "Requested poll frequency of " + runAtTime
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000478 + " seconds being rounded up to 1 hour.");
479 runAtTime = 3600;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700480 }
481 // Schedule periodic sync.
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000482 getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
483 flextime, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700484 } else {
485 long beforeRuntimeMillis = (flextime) * 1000;
486 long runtimeMillis = runAtTime * 1000;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700487 syncManager.scheduleSync(
488 request.getAccount(), userId, callerUid, request.getProvider(), extras,
489 beforeRuntimeMillis, runtimeMillis,
490 false /* onlyThoseWithUnknownSyncableState */);
Matthew Williamsfa774182013-06-18 15:44:11 -0700491 }
492 } finally {
493 restoreCallingIdentity(identityToken);
494 }
495 }
496
497 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700499 * if they match the authority and account, if they are present.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700500 *
501 * @param account filter the pending and active syncs to cancel using this account, or null.
502 * @param authority filter the pending and active syncs to cancel using this authority, or
503 * null.
504 * @param cname cancel syncs running on this service, or null for provider/account.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700506 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700507 public void cancelSync(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100508 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
509 }
510
511 /**
512 * Clear all scheduled sync operations that match the uri and cancel the active sync
513 * if they match the authority and account, if they are present.
514 *
515 * <p> If the user id supplied is different to the calling user, the caller must hold the
516 * INTERACT_ACROSS_USERS_FULL permission.
517 *
518 * @param account filter the pending and active syncs to cancel using this account, or null.
519 * @param authority filter the pending and active syncs to cancel using this authority, or
520 * null.
521 * @param userId the user id for which to cancel sync operations.
522 * @param cname cancel syncs running on this service, or null for provider/account.
523 */
524 @Override
525 public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000526 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700527 if (authority != null && authority.length() == 0) {
528 throw new IllegalArgumentException("Authority must be non-empty");
529 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100530 enforceCrossUserPermission(userId,
531 "no permission to modify the sync settings for user " + userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 // This makes it so that future permission checks will be in the context of this
533 // process rather than the caller's process. We will restore this before returning.
534 long identityToken = clearCallingIdentity();
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000535 if (cname != null) {
536 Slog.e(TAG, "cname not null.");
537 return;
538 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 try {
540 SyncManager syncManager = getSyncManager();
541 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700542 SyncStorageEngine.EndPoint info;
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000543 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700544 syncManager.clearScheduledSyncOperations(info);
545 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 }
547 } finally {
548 restoreCallingIdentity(identityToken);
549 }
550 }
551
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600552 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700553 public void cancelRequest(SyncRequest request) {
554 SyncManager syncManager = getSyncManager();
555 if (syncManager == null) return;
556 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700557
558 long identityToken = clearCallingIdentity();
559 try {
560 SyncStorageEngine.EndPoint info;
561 Bundle extras = new Bundle(request.getBundle());
Matthew Williams5a9decd2014-06-04 09:25:11 -0700562 Account account = request.getAccount();
563 String provider = request.getProvider();
564 info = new SyncStorageEngine.EndPoint(account, provider, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700565 if (request.isPeriodic()) {
566 // Remove periodic sync.
567 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
568 "no permission to write the sync settings");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000569 getSyncManager().removePeriodicSync(info, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700570 }
571 // Cancel active syncs and clear pending syncs from the queue.
572 syncManager.cancelScheduledSyncOperation(info, extras);
573 syncManager.cancelActiveSync(info, extras);
574 } finally {
575 restoreCallingIdentity(identityToken);
576 }
577 }
578
Fred Quintanaac9385e2009-06-22 18:00:59 -0700579 /**
580 * Get information about the SyncAdapters that are known to the system.
581 * @return an array of SyncAdapters that have registered with the system
582 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700583 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700584 public SyncAdapterType[] getSyncAdapterTypes() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100585 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
586 }
587
588 /**
589 * Get information about the SyncAdapters that are known to the system for a particular user.
590 *
591 * <p> If the user id supplied is different to the calling user, the caller must hold the
592 * INTERACT_ACROSS_USERS_FULL permission.
593 *
594 * @return an array of SyncAdapters that have registered with the system
595 */
596 @Override
597 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
598 enforceCrossUserPermission(userId,
599 "no permission to read sync settings for user " + userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700600 // This makes it so that future permission checks will be in the context of this
601 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700602 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700603 try {
604 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700605 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700606 } finally {
607 restoreCallingIdentity(identityToken);
608 }
609 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700610
Matthew Williamsfa774182013-06-18 15:44:11 -0700611 @Override
Amith Yamasani37a40c22015-06-17 13:25:42 -0700612 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
613 enforceCrossUserPermission(userId,
614 "no permission to read sync settings for user " + userId);
615 // This makes it so that future permission checks will be in the context of this
616 // process rather than the caller's process. We will restore this before returning.
617 final long identityToken = clearCallingIdentity();
618 try {
619 SyncManager syncManager = getSyncManager();
620 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
621 } finally {
622 restoreCallingIdentity(identityToken);
623 }
624 }
625
626 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700627 public boolean getSyncAutomatically(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100628 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
629 }
630
631 /**
632 * If the user id supplied is different to the calling user, the caller must hold the
633 * INTERACT_ACROSS_USERS_FULL permission.
634 */
635 @Override
636 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
637 enforceCrossUserPermission(userId,
638 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700639 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
640 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800641
Dianne Hackborn231cc602009-04-27 17:10:36 -0700642 long identityToken = clearCallingIdentity();
643 try {
644 SyncManager syncManager = getSyncManager();
645 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700646 return syncManager.getSyncStorageEngine()
647 .getSyncAutomatically(account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700648 }
649 } finally {
650 restoreCallingIdentity(identityToken);
651 }
652 return false;
653 }
654
Matthew Williamsfa774182013-06-18 15:44:11 -0700655 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700656 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100657 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
658 }
659
660 @Override
661 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000662 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700663 if (TextUtils.isEmpty(providerName)) {
664 throw new IllegalArgumentException("Authority must be non-empty");
665 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700666 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
667 "no permission to write the sync settings");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100668 enforceCrossUserPermission(userId,
669 "no permission to modify the sync settings for user " + userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800670
Dianne Hackborn231cc602009-04-27 17:10:36 -0700671 long identityToken = clearCallingIdentity();
672 try {
673 SyncManager syncManager = getSyncManager();
674 if (syncManager != null) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100675 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
676 providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700677 }
678 } finally {
679 restoreCallingIdentity(identityToken);
680 }
681 }
682
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000683 /** Old API. Schedule periodic sync with default flexMillis time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700684 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800685 public void addPeriodicSync(Account account, String authority, Bundle extras,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000686 long pollFrequency) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600687 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700688 if (account == null) {
689 throw new IllegalArgumentException("Account must not be null");
690 }
691 if (TextUtils.isEmpty(authority)) {
692 throw new IllegalArgumentException("Authority must not be empty.");
693 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800694 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
695 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800696
Matthew Williams632515b2013-10-10 15:51:00 -0700697 int userId = UserHandle.getCallingUserId();
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000698 if (pollFrequency < 3600) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700699 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Shreyas Basargee96c3b72016-01-29 19:25:51 +0000700 + " seconds being rounded up to 1 hour.");
701 pollFrequency = 3600;
Jeff Sharkey60094592013-03-21 18:14:24 -0700702 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700703 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
Jeff Sharkey60094592013-03-21 18:14:24 -0700704
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800705 long identityToken = clearCallingIdentity();
706 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700707 SyncStorageEngine.EndPoint info =
708 new SyncStorageEngine.EndPoint(account, authority, userId);
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000709 getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
710 defaultFlex, extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800711 } finally {
712 restoreCallingIdentity(identityToken);
713 }
714 }
715
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600716 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800717 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600718 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700719 if (account == null) {
720 throw new IllegalArgumentException("Account must not be null");
721 }
722 if (TextUtils.isEmpty(authority)) {
723 throw new IllegalArgumentException("Authority must not be empty");
724 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800725 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
726 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800727
Matthew Williams632515b2013-10-10 15:51:00 -0700728 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800729 long identityToken = clearCallingIdentity();
730 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000731 getSyncManager()
732 .removePeriodicSync(
733 new SyncStorageEngine.EndPoint(account, authority, userId),
734 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800735 } finally {
736 restoreCallingIdentity(identityToken);
737 }
738 }
739
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600740 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700741 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000742 ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700743 if (account == null) {
744 throw new IllegalArgumentException("Account must not be null");
745 }
746 if (TextUtils.isEmpty(providerName)) {
747 throw new IllegalArgumentException("Authority must not be empty");
748 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800749 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
750 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800751
Matthew Williams632515b2013-10-10 15:51:00 -0700752 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800753 long identityToken = clearCallingIdentity();
754 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000755 return getSyncManager().getPeriodicSyncs(
Matthew Williams5a9decd2014-06-04 09:25:11 -0700756 new SyncStorageEngine.EndPoint(account, providerName, userId));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800757 } finally {
758 restoreCallingIdentity(identityToken);
759 }
760 }
761
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600762 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700763 public int getIsSyncable(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100764 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
765 }
766
767 /**
768 * If the user id supplied is different to the calling user, the caller must hold the
769 * INTERACT_ACROSS_USERS_FULL permission.
770 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600771 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100772 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
773 enforceCrossUserPermission(userId,
774 "no permission to read the sync settings for user " + userId);
Fred Quintana5e787c42009-08-16 23:13:53 -0700775 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
776 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800777
Fred Quintana5e787c42009-08-16 23:13:53 -0700778 long identityToken = clearCallingIdentity();
779 try {
780 SyncManager syncManager = getSyncManager();
781 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700782 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800783 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700784 }
785 } finally {
786 restoreCallingIdentity(identityToken);
787 }
788 return -1;
789 }
790
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600791 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700792 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700793 if (TextUtils.isEmpty(providerName)) {
794 throw new IllegalArgumentException("Authority must not be empty");
795 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700796 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
797 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800798
Matthew Williams632515b2013-10-10 15:51:00 -0700799 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700800 long identityToken = clearCallingIdentity();
801 try {
802 SyncManager syncManager = getSyncManager();
803 if (syncManager != null) {
804 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800805 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700806 }
807 } finally {
808 restoreCallingIdentity(identityToken);
809 }
810 }
811
Matthew Williamsfa774182013-06-18 15:44:11 -0700812 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700813 public boolean getMasterSyncAutomatically() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100814 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
815 }
816
817 /**
818 * If the user id supplied is different to the calling user, the caller must hold the
819 * INTERACT_ACROSS_USERS_FULL permission.
820 */
821 @Override
822 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
823 enforceCrossUserPermission(userId,
824 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700825 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
826 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800827
Dianne Hackborn231cc602009-04-27 17:10:36 -0700828 long identityToken = clearCallingIdentity();
829 try {
830 SyncManager syncManager = getSyncManager();
831 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800832 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700833 }
834 } finally {
835 restoreCallingIdentity(identityToken);
836 }
837 return false;
838 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700839
Matthew Williamsfa774182013-06-18 15:44:11 -0700840 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700841 public void setMasterSyncAutomatically(boolean flag) {
Alexandra Gherghina0e9ac202014-07-15 23:11:48 +0100842 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
843 }
844
845 @Override
846 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
847 enforceCrossUserPermission(userId,
848 "no permission to set the sync status for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700849 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
850 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800851
Dianne Hackborn231cc602009-04-27 17:10:36 -0700852 long identityToken = clearCallingIdentity();
853 try {
854 SyncManager syncManager = getSyncManager();
855 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800856 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700857 }
858 } finally {
859 restoreCallingIdentity(identityToken);
860 }
861 }
862
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600863 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700864 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700865 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
866 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700867 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700868 long identityToken = clearCallingIdentity();
869 try {
870 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700871 if (syncManager == null) {
872 return false;
873 }
Matthew Williams5a9decd2014-06-04 09:25:11 -0700874 return syncManager.getSyncStorageEngine().isSyncActive(
875 new SyncStorageEngine.EndPoint(account, authority, userId));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700876 } finally {
877 restoreCallingIdentity(identityToken);
878 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700879 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700880
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600881 @Override
Fred Quintanac6a69552010-09-27 17:05:04 -0700882 public List<SyncInfo> getCurrentSyncs() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100883 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
884 }
885
886 /**
887 * If the user id supplied is different to the calling user, the caller must hold the
888 * INTERACT_ACROSS_USERS_FULL permission.
889 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600890 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100891 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
892 enforceCrossUserPermission(userId,
893 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700894 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
895 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800896
Matthew Williamsf39549e2016-01-19 23:04:04 +0000897 final boolean canAccessAccounts =
898 mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
899 == PackageManager.PERMISSION_GRANTED;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700900 long identityToken = clearCallingIdentity();
901 try {
Matthew Williamsf39549e2016-01-19 23:04:04 +0000902 return getSyncManager().getSyncStorageEngine()
903 .getCurrentSyncsCopy(userId, canAccessAccounts);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700904 } finally {
905 restoreCallingIdentity(identityToken);
906 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700907 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700908
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600909 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700910 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100911 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
912 }
913
914 /**
915 * If the user id supplied is different to the calling user, the caller must hold the
916 * INTERACT_ACROSS_USERS_FULL permission.
917 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600918 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100919 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000920 ComponentName cname, int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700921 if (TextUtils.isEmpty(authority)) {
922 throw new IllegalArgumentException("Authority must not be empty");
923 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100924
925 enforceCrossUserPermission(userId,
926 "no permission to read the sync stats for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700927 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
928 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800929
Dianne Hackborn231cc602009-04-27 17:10:36 -0700930 long identityToken = clearCallingIdentity();
931 try {
932 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700933 if (syncManager == null) {
934 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700935 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700936 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700937 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700938 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700939 } else {
940 throw new IllegalArgumentException("Must call sync status with valid authority");
941 }
942 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700943 } finally {
944 restoreCallingIdentity(identityToken);
945 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700946 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700947
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600948 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700949 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100950 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
951 }
952
953 @Override
954 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000955 int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700956 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
957 "no permission to read the sync stats");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100958 enforceCrossUserPermission(userId,
959 "no permission to retrieve the sync settings for user " + userId);
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000960 long identityToken = clearCallingIdentity();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700961 SyncManager syncManager = getSyncManager();
962 if (syncManager == null) return false;
963
Dianne Hackborn231cc602009-04-27 17:10:36 -0700964 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700965 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700966 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700967 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700968 } else {
969 throw new IllegalArgumentException("Invalid authority specified");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700970 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700971 return syncManager.getSyncStorageEngine().isSyncPending(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700972 } finally {
973 restoreCallingIdentity(identityToken);
974 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700975 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700976
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600977 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -0700978 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
979 long identityToken = clearCallingIdentity();
980 try {
981 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800982 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700983 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700984 }
985 } finally {
986 restoreCallingIdentity(identityToken);
987 }
988 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700989
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600990 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -0700991 public void removeStatusChangeListener(ISyncStatusObserver callback) {
992 long identityToken = clearCallingIdentity();
993 try {
994 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800995 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700996 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700997 }
998 } finally {
999 restoreCallingIdentity(identityToken);
1000 }
1001 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001002
Jeff Sharkey87314082016-03-11 17:25:11 -07001003 private @Nullable String getProviderPackageName(Uri uri) {
1004 final ProviderInfo pi = mContext.getPackageManager()
1005 .resolveContentProvider(uri.getAuthority(), 0);
1006 return (pi != null) ? pi.packageName : null;
1007 }
1008
1009 private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1010 String providerPackageName) {
1011 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1012 if (userCache == null) {
1013 userCache = new ArrayMap<>();
1014 mCache.put(userId, userCache);
1015 }
1016 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1017 if (packageCache == null) {
1018 packageCache = new ArrayMap<>();
1019 userCache.put(providerPackageName, packageCache);
1020 }
1021 return packageCache;
1022 }
1023
1024 private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1025 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1026 if (userCache == null) return;
1027
1028 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1029 if (packageCache == null) return;
1030
1031 if (uri != null) {
1032 for (int i = 0; i < packageCache.size();) {
1033 final Uri key = packageCache.keyAt(i).second;
1034 if (Objects.equals(key, uri)) {
1035 packageCache.removeAt(i);
1036 } else {
1037 i++;
1038 }
1039 }
1040 } else {
1041 packageCache.clear();
1042 }
1043 }
1044
1045 @Override
1046 public void putCache(String packageName, Uri key, Bundle value, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001047 Bundle.setDefusable(value, true);
Jeff Sharkey87314082016-03-11 17:25:11 -07001048 enforceCrossUserPermission(userId, TAG);
1049 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1050 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1051 packageName);
1052
1053 final String providerPackageName = getProviderPackageName(key);
1054 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1055
1056 synchronized (mCache) {
1057 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1058 providerPackageName);
1059 if (value != null) {
1060 cache.put(fullKey, value);
1061 } else {
1062 cache.remove(fullKey);
1063 }
1064 }
1065 }
1066
1067 @Override
1068 public Bundle getCache(String packageName, Uri key, int userId) {
1069 enforceCrossUserPermission(userId, TAG);
1070 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1071 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1072 packageName);
1073
1074 final String providerPackageName = getProviderPackageName(key);
1075 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1076
1077 synchronized (mCache) {
1078 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1079 providerPackageName);
1080 return cache.get(fullKey);
1081 }
1082 }
1083
Kenny Root26ff6622012-07-30 12:58:03 -07001084 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001086 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 return service;
1088 }
1089
1090 /**
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +01001091 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1092 * permission, if the userHandle is not for the caller.
1093 *
1094 * @param userHandle the user handle of the user we want to act on behalf of.
1095 * @param message the message to log on security exception.
1096 */
1097 private void enforceCrossUserPermission(int userHandle, String message) {
1098 final int callingUser = UserHandle.getCallingUserId();
1099 if (callingUser != userHandle) {
1100 mContext.enforceCallingOrSelfPermission(
1101 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1102 }
1103 }
1104
1105 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 * Hide this class since it is not part of api,
1107 * but current unittest framework requires it to be public
1108 * @hide
1109 */
1110 public static final class ObserverNode {
1111 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -08001112 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001113 public final int uid;
1114 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001115 public final boolean notifyForDescendants;
1116 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -08001117 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001119 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001120 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001121 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001123 uid = _uid;
1124 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001125 userHandle = _userHandle;
1126 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 try {
1128 observer.asBinder().linkToDeath(this, 0);
1129 } catch (RemoteException e) {
1130 binderDied();
1131 }
1132 }
1133
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001134 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -08001136 synchronized (observersLock) {
1137 removeObserverLocked(observer);
1138 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001140
1141 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001142 String name, String prefix, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001143 pidCounts.put(pid, pidCounts.get(pid)+1);
1144 pw.print(prefix); pw.print(name); pw.print(": pid=");
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001145 pw.print(pid); pw.print(" uid=");
1146 pw.print(uid); pw.print(" user=");
1147 pw.print(userHandle); pw.print(" target=");
1148 pw.println(Integer.toHexString(System.identityHashCode(
1149 observer != null ? observer.asBinder() : null)));
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001150 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 }
1152
1153 public static final int INSERT_TYPE = 0;
1154 public static final int UPDATE_TYPE = 1;
1155 public static final int DELETE_TYPE = 2;
1156
1157 private String mName;
1158 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1159 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1160
1161 public ObserverNode(String name) {
1162 mName = name;
1163 }
1164
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001165 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001166 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001167 String innerName = null;
1168 if (mObservers.size() > 0) {
1169 if ("".equals(name)) {
1170 innerName = mName;
1171 } else {
1172 innerName = name + "/" + mName;
1173 }
1174 for (int i=0; i<mObservers.size(); i++) {
1175 counts[1]++;
1176 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1177 pidCounts);
1178 }
1179 }
1180 if (mChildren.size() > 0) {
1181 if (innerName == null) {
1182 if ("".equals(name)) {
1183 innerName = mName;
1184 } else {
1185 innerName = name + "/" + mName;
1186 }
1187 }
1188 for (int i=0; i<mChildren.size(); i++) {
1189 counts[0]++;
1190 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1191 counts, pidCounts);
1192 }
1193 }
1194 }
1195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 private String getUriSegment(Uri uri, int index) {
1197 if (uri != null) {
1198 if (index == 0) {
1199 return uri.getAuthority();
1200 } else {
1201 return uri.getPathSegments().get(index - 1);
1202 }
1203 } else {
1204 return null;
1205 }
1206 }
1207
1208 private int countUriSegments(Uri uri) {
1209 if (uri == null) {
1210 return 0;
1211 }
1212 return uri.getPathSegments().size() + 1;
1213 }
1214
Christopher Tate16aa9732012-09-17 16:23:44 -07001215 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -08001216 public void addObserverLocked(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001217 boolean notifyForDescendants, Object observersLock,
1218 int uid, int pid, int userHandle) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001219 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1220 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 }
1222
Fred Quintana002ffad52010-03-02 11:18:16 -08001223 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001224 boolean notifyForDescendants, Object observersLock,
1225 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 // If this is the leaf node add the observer
1227 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001228 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1229 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 return;
1231 }
1232
1233 // Look to see if the proper child already exists
1234 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -05001235 if (segment == null) {
1236 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 int N = mChildren.size();
1239 for (int i = 0; i < N; i++) {
1240 ObserverNode node = mChildren.get(i);
1241 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001242 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1243 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 return;
1245 }
1246 }
1247
1248 // No child found, create one
1249 ObserverNode node = new ObserverNode(segment);
1250 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -07001251 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1252 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 }
1254
Fred Quintana002ffad52010-03-02 11:18:16 -08001255 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 int size = mChildren.size();
1257 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001258 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001259 if (empty) {
1260 mChildren.remove(i);
1261 i--;
1262 size--;
1263 }
1264 }
1265
1266 IBinder observerBinder = observer.asBinder();
1267 size = mObservers.size();
1268 for (int i = 0; i < size; i++) {
1269 ObserverEntry entry = mObservers.get(i);
1270 if (entry.observer.asBinder() == observerBinder) {
1271 mObservers.remove(i);
1272 // We no longer need to listen for death notifications. Remove it.
1273 observerBinder.unlinkToDeath(entry, 0);
1274 break;
1275 }
1276 }
1277
1278 if (mChildren.size() == 0 && mObservers.size() == 0) {
1279 return true;
1280 }
1281 return false;
1282 }
1283
Fred Quintana002ffad52010-03-02 11:18:16 -08001284 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001285 boolean observerWantsSelfNotifications, int targetUserHandle,
1286 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 int N = mObservers.size();
1288 IBinder observerBinder = observer == null ? null : observer.asBinder();
1289 for (int i = 0; i < N; i++) {
1290 ObserverEntry entry = mObservers.get(i);
1291
Christopher Tate16aa9732012-09-17 16:23:44 -07001292 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -08001294 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1295 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 continue;
1297 }
1298
Christopher Tate16aa9732012-09-17 16:23:44 -07001299 // Does this observer match the target user?
1300 if (targetUserHandle == UserHandle.USER_ALL
1301 || entry.userHandle == UserHandle.USER_ALL
1302 || targetUserHandle == entry.userHandle) {
1303 // Make sure the observer is interested in the notification
1304 if (leaf || (!leaf && entry.notifyForDescendants)) {
1305 calls.add(new ObserverCall(this, entry.observer, selfChange));
1306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 }
1308 }
1309 }
1310
Christopher Tate16aa9732012-09-17 16:23:44 -07001311 /**
1312 * targetUserHandle is either a hard user handle or is USER_ALL
1313 */
Fred Quintana002ffad52010-03-02 11:18:16 -08001314 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001315 boolean observerWantsSelfNotifications, int targetUserHandle,
1316 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 String segment = null;
1318 int segmentCount = countUriSegments(uri);
1319 if (index >= segmentCount) {
1320 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -07001321 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1322 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 } else if (index < segmentCount){
1324 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -07001325 // Notify any observers at this level who are interested in descendants
1326 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1327 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 }
1329
1330 int N = mChildren.size();
1331 for (int i = 0; i < N; i++) {
1332 ObserverNode node = mChildren.get(i);
1333 if (segment == null || node.mName.equals(segment)) {
1334 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -08001335 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -07001336 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 if (segment != null) {
1338 break;
1339 }
1340 }
1341 }
1342 }
1343 }
1344}