blob: 07276298d66d8a607b5e8a44f0b25769138feffe [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;
Nicolas Prevot051f3b72016-05-18 18:44:00 +010023import android.app.ActivityManagerNative;
Jeff Sharkey87314082016-03-11 17:25:11 -070024import android.app.AppOpsManager;
Shreyas Basarge3b840e32016-06-14 15:32:02 +010025import android.app.job.JobInfo;
Jeff Sharkey87314082016-03-11 17:25:11 -070026import android.content.BroadcastReceiver;
Matthew Williamsfa774182013-06-18 15:44:11 -070027import android.content.ComponentName;
Nicolas Prevot051f3b72016-05-18 18:44:00 +010028import android.content.ContentProvider;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080029import android.content.ContentResolver;
30import android.content.Context;
31import android.content.IContentService;
Jeff Sharkey7732e1e2016-03-30 17:14:23 -060032import android.content.ISyncStatusObserver;
Benjamin Franzadea1912015-06-19 16:03:38 +010033import android.content.Intent;
Jeff Sharkey87314082016-03-11 17:25:11 -070034import android.content.IntentFilter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080035import android.content.PeriodicSync;
36import android.content.SyncAdapterType;
37import android.content.SyncInfo;
Matthew Williamsfa774182013-06-18 15:44:11 -070038import android.content.SyncRequest;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080039import android.content.SyncStatusInfo;
Benjamin Franzadea1912015-06-19 16:03:38 +010040import android.content.pm.PackageManager;
Svetoslav0010b702015-06-30 18:05:26 -070041import android.content.pm.PackageManagerInternal;
Jeff Sharkey87314082016-03-11 17:25:11 -070042import android.content.pm.ProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.database.IContentObserver;
44import android.database.sqlite.SQLiteException;
45import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070046import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.Bundle;
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060048import android.os.FactoryTest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070050import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.RemoteException;
Dan Morrille4d9a012013-03-28 18:10:43 -070052import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070053import android.os.UserHandle;
Matthew Williams632515b2013-10-10 15:51:00 -070054import android.text.TextUtils;
Jeff Sharkey87314082016-03-11 17:25:11 -070055import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.util.Log;
Jeff Sharkey87314082016-03-11 17:25:11 -070057import android.util.Pair;
Jeff Sharkey60094592013-03-21 18:14:24 -070058import android.util.Slog;
Jeff Sharkey87314082016-03-11 17:25:11 -070059import android.util.SparseArray;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070060import android.util.SparseIntArray;
Jeff Sharkey87314082016-03-11 17:25:11 -070061
62import com.android.internal.annotations.GuardedBy;
63import com.android.internal.util.IndentingPrintWriter;
Svetoslav0010b702015-06-30 18:05:26 -070064import com.android.server.LocalServices;
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060065import com.android.server.SystemService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67import java.io.FileDescriptor;
68import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070069import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070071import java.util.Collections;
72import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080073import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
75/**
76 * {@hide}
77 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070078public final class ContentService extends IContentService.Stub {
Dianne Hackborn141f11c2016-04-05 15:46:12 -070079 static final String TAG = "ContentService";
80 static final boolean DEBUG = false;
Jeff Sharkey87314082016-03-11 17:25:11 -070081
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060082 public static class Lifecycle extends SystemService {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -060083 private ContentService mService;
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060084
85 public Lifecycle(Context context) {
86 super(context);
87 }
88
89 @Override
90 public void onStart() {
91 final boolean factoryTest = (FactoryTest
92 .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
Jeff Sharkey1cab76a2016-04-12 18:23:31 -060093 mService = new ContentService(getContext(), factoryTest);
94 publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
95 }
96
97 @Override
98 public void onBootPhase(int phase) {
99 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
100 mService.systemReady();
101 }
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600102 }
103
104 @Override
105 public void onCleanupUser(int userHandle) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600106 synchronized (mService.mCache) {
107 mService.mCache.remove(userHandle);
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600108 }
109 }
110 }
111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 private Context mContext;
113 private boolean mFactoryTest;
Jeff Sharkey87314082016-03-11 17:25:11 -0700114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private final ObserverNode mRootNode = new ObserverNode("");
Jeff Sharkey87314082016-03-11 17:25:11 -0700116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 private SyncManager mSyncManager = null;
118 private final Object mSyncManagerLock = new Object();
119
Jeff Sharkey87314082016-03-11 17:25:11 -0700120 /**
121 * Map from userId to providerPackageName to [clientPackageName, uri] to
122 * value. This structure is carefully optimized to keep invalidation logic
123 * as cheap as possible.
124 */
125 @GuardedBy("mCache")
126 private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
127 mCache = new SparseArray<>();
128
129 private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
130 @Override
131 public void onReceive(Context context, Intent intent) {
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600132 synchronized (mCache) {
133 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
134 mCache.clear();
135 } else {
136 final Uri data = intent.getData();
137 if (data != null) {
138 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
139 UserHandle.USER_NULL);
140 final String packageName = data.getSchemeSpecificPart();
141 invalidateCacheLocked(userId, packageName, null);
142 }
143 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700144 }
145 }
146 };
147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -0700149 if (SystemProperties.getBoolean("config.disable_network", false)) {
150 return null;
151 }
152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 synchronized(mSyncManagerLock) {
154 try {
155 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
156 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
157 } catch (SQLiteException e) {
158 Log.e(TAG, "Can't create SyncManager", e);
159 }
160 return mSyncManager;
161 }
162 }
163
164 @Override
Jeff Sharkey87314082016-03-11 17:25:11 -0700165 protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
167 "caller doesn't have the DUMP permission");
168
Jeff Sharkey87314082016-03-11 17:25:11 -0700169 final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 // This makes it so that future permission checks will be in the context of this
172 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey87314082016-03-11 17:25:11 -0700173 final long identityToken = clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 try {
175 if (mSyncManager == null) {
176 pw.println("No SyncManager created! (Disk full?)");
177 } else {
178 mSyncManager.dump(fd, pw);
179 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700180 pw.println();
181 pw.println("Observer tree:");
182 synchronized (mRootNode) {
183 int[] counts = new int[2];
184 final SparseIntArray pidCounts = new SparseIntArray();
185 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
186 pw.println();
187 ArrayList<Integer> sorted = new ArrayList<Integer>();
188 for (int i=0; i<pidCounts.size(); i++) {
189 sorted.add(pidCounts.keyAt(i));
190 }
191 Collections.sort(sorted, new Comparator<Integer>() {
192 @Override
193 public int compare(Integer lhs, Integer rhs) {
194 int lc = pidCounts.get(lhs);
195 int rc = pidCounts.get(rhs);
196 if (lc < rc) {
197 return 1;
198 } else if (lc > rc) {
199 return -1;
200 }
201 return 0;
202 }
203
204 });
205 for (int i=0; i<sorted.size(); i++) {
206 int pid = sorted.get(i);
207 pw.print(" pid "); pw.print(pid); pw.print(": ");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000208 pw.print(pidCounts.get(pid)); pw.println(" observers");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700209 }
210 pw.println();
211 pw.print(" Total number of nodes: "); pw.println(counts[0]);
212 pw.print(" Total number of observers: "); pw.println(counts[1]);
213 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700214
215 synchronized (mCache) {
216 pw.println();
217 pw.println("Cached content:");
218 pw.increaseIndent();
219 for (int i = 0; i < mCache.size(); i++) {
220 pw.println("User " + mCache.keyAt(i) + ":");
221 pw.increaseIndent();
222 pw.println(mCache.valueAt(i));
223 pw.decreaseIndent();
224 }
225 pw.decreaseIndent();
226 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 } finally {
228 restoreCallingIdentity(identityToken);
229 }
230 }
231
Dianne Hackborn231cc602009-04-27 17:10:36 -0700232 @Override
233 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
234 throws RemoteException {
235 try {
236 return super.onTransact(code, data, reply, flags);
237 } catch (RuntimeException e) {
238 // The content service only throws security exceptions, so let's
239 // log all others.
240 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700241 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700242 }
243 throw e;
244 }
245 }
246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 /*package*/ ContentService(Context context, boolean factoryTest) {
248 mContext = context;
249 mFactoryTest = factoryTest;
Svetoslav0010b702015-06-30 18:05:26 -0700250
251 // Let the package manager query for the sync adapters for a given authority
Svet Ganov52153f42015-08-11 08:59:12 -0700252 // as we grant default permissions to sync adapters for specific authorities.
Svetoslav0010b702015-06-30 18:05:26 -0700253 PackageManagerInternal packageManagerInternal = LocalServices.getService(
254 PackageManagerInternal.class);
255 packageManagerInternal.setSyncAdapterPackagesprovider(
256 new PackageManagerInternal.SyncAdapterPackagesProvider() {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000257 @Override
258 public String[] getPackages(String authority, int userId) {
259 return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
260 }
261 });
Jeff Sharkey87314082016-03-11 17:25:11 -0700262
263 final IntentFilter packageFilter = new IntentFilter();
264 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
265 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
266 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
267 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
268 packageFilter.addDataScheme("package");
269 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
270 packageFilter, null, null);
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600271
272 final IntentFilter localeFilter = new IntentFilter();
273 localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
274 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
275 localeFilter, null, null);
Kenny Root26ff6622012-07-30 12:58:03 -0700276 }
277
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600278 void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 getSyncManager();
280 }
281
Christopher Tate16aa9732012-09-17 16:23:44 -0700282 /**
283 * Register a content observer tied to a specific user's view of the provider.
284 * @param userHandle the user whose view of the provider is to be observed. May be
285 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100286 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
287 * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
288 * are forbidden.
Christopher Tate16aa9732012-09-17 16:23:44 -0700289 */
290 @Override
291 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000292 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 if (observer == null || uri == null) {
294 throw new IllegalArgumentException("You must pass a valid uri and observer");
295 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700296
Benjamin Franzadea1912015-06-19 16:03:38 +0100297 final int uid = Binder.getCallingUid();
298 final int pid = Binder.getCallingPid();
299 final int callingUserHandle = UserHandle.getCallingUserId();
300 // Registering an observer for any user other than the calling user requires uri grant or
301 // cross user permission
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100302 if (callingUserHandle != userHandle) {
303 if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle)
304 != PackageManager.PERMISSION_GRANTED) {
305 enforceCrossUserPermission(userHandle,
306 "no permission to observe other users' provider view");
307 }
Benjamin Franzadea1912015-06-19 16:03:38 +0100308 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700309
310 if (userHandle < 0) {
311 if (userHandle == UserHandle.USER_CURRENT) {
312 userHandle = ActivityManager.getCurrentUser();
313 } else if (userHandle != UserHandle.USER_ALL) {
314 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
315 + userHandle);
316 }
317 }
318
319 synchronized (mRootNode) {
320 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
Benjamin Franzadea1912015-06-19 16:03:38 +0100321 uid, pid, userHandle);
Christopher Tate16aa9732012-09-17 16:23:44 -0700322 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
323 " with notifyForDescendants " + notifyForDescendants);
324 }
325 }
326
327 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000328 IContentObserver observer) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700329 registerContentObserver(uri, notifyForDescendants, observer,
330 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600333 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 public void unregisterContentObserver(IContentObserver observer) {
335 if (observer == null) {
336 throw new IllegalArgumentException("You must pass a valid observer");
337 }
338 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800339 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700340 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 }
343
Christopher Tate16aa9732012-09-17 16:23:44 -0700344 /**
345 * Notify observers of a particular user's view of the provider.
346 * @param userHandle the user whose view of the provider is to be notified. May be
347 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100348 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
349 * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
350 * allowed.
Christopher Tate16aa9732012-09-17 16:23:44 -0700351 */
352 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 public void notifyChange(Uri uri, IContentObserver observer,
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700354 boolean observerWantsSelfNotifications, int flags,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000355 int userHandle) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700356 if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
357 + " from observer " + observer + ", flags " + Integer.toHexString(flags));
Amith Yamasani04e0d262012-02-14 11:50:53 -0800358
Dianne Hackborne7617772016-04-27 17:03:52 -0700359 if (uri == null) {
360 throw new NullPointerException("Uri must not be null");
361 }
362
Benjamin Franzadea1912015-06-19 16:03:38 +0100363 final int uid = Binder.getCallingUid();
364 final int pid = Binder.getCallingPid();
Christopher Tate16aa9732012-09-17 16:23:44 -0700365 final int callingUserHandle = UserHandle.getCallingUserId();
Benjamin Franzadea1912015-06-19 16:03:38 +0100366 // Notify for any user other than the caller requires uri grant or cross user permission
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100367 if (callingUserHandle != userHandle) {
368 if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
369 userHandle) != PackageManager.PERMISSION_GRANTED) {
370 enforceCrossUserPermission(userHandle, "no permission to notify other users");
371 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700372 }
373
374 // We passed the permission check; resolve pseudouser targets as appropriate
375 if (userHandle < 0) {
376 if (userHandle == UserHandle.USER_CURRENT) {
377 userHandle = ActivityManager.getCurrentUser();
378 } else if (userHandle != UserHandle.USER_ALL) {
379 throw new InvalidParameterException("Bad user handle for notifyChange: "
380 + userHandle);
381 }
382 }
383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 // This makes it so that future permission checks will be in the context of this
385 // process rather than the caller's process. We will restore this before returning.
386 long identityToken = clearCallingIdentity();
387 try {
388 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
389 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800390 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700391 flags, userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
393 final int numCalls = calls.size();
394 for (int i=0; i<numCalls; i++) {
395 ObserverCall oc = calls.get(i);
396 try {
Svet Ganov3138a992016-06-15 16:57:32 -0700397 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700398 if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
Svet Ganov3138a992016-06-15 16:57:32 -0700399 + uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 } catch (RemoteException ex) {
401 synchronized (mRootNode) {
402 Log.w(TAG, "Found dead observer, removing");
403 IBinder binder = oc.mObserver.asBinder();
404 final ArrayList<ObserverNode.ObserverEntry> list
405 = oc.mNode.mObservers;
406 int numList = list.size();
407 for (int j=0; j<numList; j++) {
408 ObserverNode.ObserverEntry oe = list.get(j);
409 if (oe.observer.asBinder() == binder) {
410 list.remove(j);
411 j--;
412 numList--;
413 }
414 }
415 }
416 }
417 }
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700418 if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700420 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700421 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800422 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700423 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700425
426 synchronized (mCache) {
427 final String providerPackageName = getProviderPackageName(uri);
428 invalidateCacheLocked(userHandle, providerPackageName, uri);
429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 } finally {
431 restoreCallingIdentity(identityToken);
432 }
433 }
434
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100435 private int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle) {
436 try {
437 return ActivityManagerNative.getDefault().checkUriPermission(
438 uri, pid, uid, modeFlags, userHandle, null);
439 } catch (RemoteException e) {
440 return PackageManager.PERMISSION_DENIED;
441 }
442 }
443
Christopher Tate16aa9732012-09-17 16:23:44 -0700444 public void notifyChange(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000445 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700446 notifyChange(uri, observer, observerWantsSelfNotifications,
447 syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
Christopher Tate16aa9732012-09-17 16:23:44 -0700448 UserHandle.getCallingUserId());
449 }
450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 /**
452 * Hide this class since it is not part of api,
453 * but current unittest framework requires it to be public
454 * @hide
455 *
456 */
457 public static final class ObserverCall {
458 final ObserverNode mNode;
459 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800460 final boolean mSelfChange;
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100461 final int mObserverUserId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100463 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange, int observerUserId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 mNode = node;
465 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800466 mSelfChange = selfChange;
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100467 mObserverUserId = observerUserId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 }
469 }
470
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600471 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700472 public void requestSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600473 Bundle.setDefusable(extras, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700475 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700476 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 // This makes it so that future permission checks will be in the context of this
479 // process rather than the caller's process. We will restore this before returning.
480 long identityToken = clearCallingIdentity();
481 try {
482 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700483 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700484 syncManager.scheduleSync(account, userId, uId, authority, extras,
Svet Ganov91d37f42016-09-03 19:36:26 -0700485 SyncStorageEngine.AuthorityInfo.UNDEFINED);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 } finally {
488 restoreCallingIdentity(identityToken);
489 }
490 }
491
492 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700493 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
494 * either:
495 * periodic OR one-off sync.
496 * and
497 * anonymous OR provider sync.
498 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700499 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700500 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600501 @Override
Matthew Williamsfa774182013-06-18 15:44:11 -0700502 public void sync(SyncRequest request) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100503 syncAsUser(request, UserHandle.getCallingUserId());
504 }
505
Shreyas Basarge3b840e32016-06-14 15:32:02 +0100506 private long clampPeriod(long period) {
507 long minPeriod = JobInfo.getMinPeriodMillis() / 1000;
508 if (period < minPeriod) {
509 Slog.w(TAG, "Requested poll frequency of " + period
510 + " seconds being rounded up to " + minPeriod + "s.");
511 period = minPeriod;
512 }
513 return period;
514 }
515
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100516 /**
517 * If the user id supplied is different to the calling user, the caller must hold the
518 * INTERACT_ACROSS_USERS_FULL permission.
519 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600520 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100521 public void syncAsUser(SyncRequest request, int userId) {
522 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700523 int callerUid = Binder.getCallingUid();
Matthew Williamsfa774182013-06-18 15:44:11 -0700524 // This makes it so that future permission checks will be in the context of this
525 // process rather than the caller's process. We will restore this before returning.
526 long identityToken = clearCallingIdentity();
527 try {
528 SyncManager syncManager = getSyncManager();
Matthew Williamsd08d6682013-10-14 10:39:41 -0700529 if (syncManager == null) {
530 return;
531 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700532
Matthew Williamsd08d6682013-10-14 10:39:41 -0700533 Bundle extras = request.getBundle();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700534 long flextime = request.getSyncFlexTime();
535 long runAtTime = request.getSyncRunTime();
536 if (request.isPeriodic()) {
537 mContext.enforceCallingOrSelfPermission(
538 Manifest.permission.WRITE_SYNC_SETTINGS,
539 "no permission to write the sync settings");
540 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700541 info = new SyncStorageEngine.EndPoint(
542 request.getAccount(), request.getProvider(), userId);
Shreyas Basarge3b840e32016-06-14 15:32:02 +0100543
544 runAtTime = clampPeriod(runAtTime);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700545 // Schedule periodic sync.
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000546 getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
547 flextime, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700548 } else {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700549 syncManager.scheduleSync(
550 request.getAccount(), userId, callerUid, request.getProvider(), extras,
Svet Ganov91d37f42016-09-03 19:36:26 -0700551 SyncStorageEngine.AuthorityInfo.UNDEFINED);
Matthew Williamsfa774182013-06-18 15:44:11 -0700552 }
553 } finally {
554 restoreCallingIdentity(identityToken);
555 }
556 }
557
558 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700560 * if they match the authority and account, if they are present.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700561 *
562 * @param account filter the pending and active syncs to cancel using this account, or null.
563 * @param authority filter the pending and active syncs to cancel using this authority, or
564 * null.
565 * @param cname cancel syncs running on this service, or null for provider/account.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700567 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700568 public void cancelSync(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100569 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
570 }
571
572 /**
573 * Clear all scheduled sync operations that match the uri and cancel the active sync
574 * if they match the authority and account, if they are present.
575 *
576 * <p> If the user id supplied is different to the calling user, the caller must hold the
577 * INTERACT_ACROSS_USERS_FULL permission.
578 *
579 * @param account filter the pending and active syncs to cancel using this account, or null.
580 * @param authority filter the pending and active syncs to cancel using this authority, or
581 * null.
582 * @param userId the user id for which to cancel sync operations.
583 * @param cname cancel syncs running on this service, or null for provider/account.
584 */
585 @Override
586 public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000587 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700588 if (authority != null && authority.length() == 0) {
589 throw new IllegalArgumentException("Authority must be non-empty");
590 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100591 enforceCrossUserPermission(userId,
592 "no permission to modify the sync settings for user " + userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 // This makes it so that future permission checks will be in the context of this
594 // process rather than the caller's process. We will restore this before returning.
595 long identityToken = clearCallingIdentity();
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000596 if (cname != null) {
597 Slog.e(TAG, "cname not null.");
598 return;
599 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 try {
601 SyncManager syncManager = getSyncManager();
602 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700603 SyncStorageEngine.EndPoint info;
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000604 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700605 syncManager.clearScheduledSyncOperations(info);
606 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 }
608 } finally {
609 restoreCallingIdentity(identityToken);
610 }
611 }
612
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600613 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700614 public void cancelRequest(SyncRequest request) {
615 SyncManager syncManager = getSyncManager();
616 if (syncManager == null) return;
617 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700618
619 long identityToken = clearCallingIdentity();
620 try {
621 SyncStorageEngine.EndPoint info;
622 Bundle extras = new Bundle(request.getBundle());
Matthew Williams5a9decd2014-06-04 09:25:11 -0700623 Account account = request.getAccount();
624 String provider = request.getProvider();
625 info = new SyncStorageEngine.EndPoint(account, provider, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700626 if (request.isPeriodic()) {
627 // Remove periodic sync.
628 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
629 "no permission to write the sync settings");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000630 getSyncManager().removePeriodicSync(info, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700631 }
632 // Cancel active syncs and clear pending syncs from the queue.
633 syncManager.cancelScheduledSyncOperation(info, extras);
634 syncManager.cancelActiveSync(info, extras);
635 } finally {
636 restoreCallingIdentity(identityToken);
637 }
638 }
639
Fred Quintanaac9385e2009-06-22 18:00:59 -0700640 /**
641 * Get information about the SyncAdapters that are known to the system.
642 * @return an array of SyncAdapters that have registered with the system
643 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700644 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700645 public SyncAdapterType[] getSyncAdapterTypes() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100646 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
647 }
648
649 /**
650 * Get information about the SyncAdapters that are known to the system for a particular user.
651 *
652 * <p> If the user id supplied is different to the calling user, the caller must hold the
653 * INTERACT_ACROSS_USERS_FULL permission.
654 *
655 * @return an array of SyncAdapters that have registered with the system
656 */
657 @Override
658 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
659 enforceCrossUserPermission(userId,
660 "no permission to read sync settings for user " + userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700661 // This makes it so that future permission checks will be in the context of this
662 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700663 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700664 try {
665 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700666 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700667 } finally {
668 restoreCallingIdentity(identityToken);
669 }
670 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700671
Matthew Williamsfa774182013-06-18 15:44:11 -0700672 @Override
Amith Yamasani37a40c22015-06-17 13:25:42 -0700673 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
674 enforceCrossUserPermission(userId,
675 "no permission to read sync settings for user " + userId);
676 // This makes it so that future permission checks will be in the context of this
677 // process rather than the caller's process. We will restore this before returning.
678 final long identityToken = clearCallingIdentity();
679 try {
680 SyncManager syncManager = getSyncManager();
681 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
682 } finally {
683 restoreCallingIdentity(identityToken);
684 }
685 }
686
687 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700688 public boolean getSyncAutomatically(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100689 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
690 }
691
692 /**
693 * If the user id supplied is different to the calling user, the caller must hold the
694 * INTERACT_ACROSS_USERS_FULL permission.
695 */
696 @Override
697 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
698 enforceCrossUserPermission(userId,
699 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700700 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
701 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800702
Dianne Hackborn231cc602009-04-27 17:10:36 -0700703 long identityToken = clearCallingIdentity();
704 try {
705 SyncManager syncManager = getSyncManager();
706 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700707 return syncManager.getSyncStorageEngine()
708 .getSyncAutomatically(account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700709 }
710 } finally {
711 restoreCallingIdentity(identityToken);
712 }
713 return false;
714 }
715
Matthew Williamsfa774182013-06-18 15:44:11 -0700716 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700717 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100718 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
719 }
720
721 @Override
722 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000723 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700724 if (TextUtils.isEmpty(providerName)) {
725 throw new IllegalArgumentException("Authority must be non-empty");
726 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700727 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
728 "no permission to write the sync settings");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100729 enforceCrossUserPermission(userId,
730 "no permission to modify the sync settings for user " + userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800731
Dianne Hackborn231cc602009-04-27 17:10:36 -0700732 long identityToken = clearCallingIdentity();
733 try {
734 SyncManager syncManager = getSyncManager();
735 if (syncManager != null) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100736 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
737 providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700738 }
739 } finally {
740 restoreCallingIdentity(identityToken);
741 }
742 }
743
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000744 /** Old API. Schedule periodic sync with default flexMillis time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700745 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800746 public void addPeriodicSync(Account account, String authority, Bundle extras,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000747 long pollFrequency) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600748 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700749 if (account == null) {
750 throw new IllegalArgumentException("Account must not be null");
751 }
752 if (TextUtils.isEmpty(authority)) {
753 throw new IllegalArgumentException("Authority must not be empty.");
754 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800755 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
756 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800757
Matthew Williams632515b2013-10-10 15:51:00 -0700758 int userId = UserHandle.getCallingUserId();
Shreyas Basarge3b840e32016-06-14 15:32:02 +0100759
760 pollFrequency = clampPeriod(pollFrequency);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700761 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
Jeff Sharkey60094592013-03-21 18:14:24 -0700762
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800763 long identityToken = clearCallingIdentity();
764 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700765 SyncStorageEngine.EndPoint info =
766 new SyncStorageEngine.EndPoint(account, authority, userId);
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000767 getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
768 defaultFlex, extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800769 } finally {
770 restoreCallingIdentity(identityToken);
771 }
772 }
773
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600774 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800775 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600776 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700777 if (account == null) {
778 throw new IllegalArgumentException("Account must not be null");
779 }
780 if (TextUtils.isEmpty(authority)) {
781 throw new IllegalArgumentException("Authority must not be empty");
782 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800783 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
784 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800785
Matthew Williams632515b2013-10-10 15:51:00 -0700786 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800787 long identityToken = clearCallingIdentity();
788 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000789 getSyncManager()
790 .removePeriodicSync(
791 new SyncStorageEngine.EndPoint(account, authority, userId),
792 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800793 } finally {
794 restoreCallingIdentity(identityToken);
795 }
796 }
797
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600798 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700799 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000800 ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700801 if (account == null) {
802 throw new IllegalArgumentException("Account must not be null");
803 }
804 if (TextUtils.isEmpty(providerName)) {
805 throw new IllegalArgumentException("Authority must not be empty");
806 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800807 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
808 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800809
Matthew Williams632515b2013-10-10 15:51:00 -0700810 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800811 long identityToken = clearCallingIdentity();
812 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000813 return getSyncManager().getPeriodicSyncs(
Matthew Williams5a9decd2014-06-04 09:25:11 -0700814 new SyncStorageEngine.EndPoint(account, providerName, userId));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800815 } finally {
816 restoreCallingIdentity(identityToken);
817 }
818 }
819
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600820 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700821 public int getIsSyncable(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100822 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
823 }
824
825 /**
826 * If the user id supplied is different to the calling user, the caller must hold the
827 * INTERACT_ACROSS_USERS_FULL permission.
828 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600829 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100830 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
831 enforceCrossUserPermission(userId,
832 "no permission to read the sync settings for user " + userId);
Fred Quintana5e787c42009-08-16 23:13:53 -0700833 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
834 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800835
Fred Quintana5e787c42009-08-16 23:13:53 -0700836 long identityToken = clearCallingIdentity();
837 try {
838 SyncManager syncManager = getSyncManager();
839 if (syncManager != null) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700840 return syncManager.computeSyncable(
Svet Ganov96b9c752016-10-17 19:29:58 -0700841 account, userId, providerName, false);
Fred Quintana5e787c42009-08-16 23:13:53 -0700842 }
843 } finally {
844 restoreCallingIdentity(identityToken);
845 }
846 return -1;
847 }
848
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600849 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700850 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700851 if (TextUtils.isEmpty(providerName)) {
852 throw new IllegalArgumentException("Authority must not be empty");
853 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700854 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
855 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800856
Svet Ganov96b9c752016-10-17 19:29:58 -0700857 syncable = normalizeSyncable(syncable);
858
Matthew Williams632515b2013-10-10 15:51:00 -0700859 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700860 long identityToken = clearCallingIdentity();
861 try {
862 SyncManager syncManager = getSyncManager();
863 if (syncManager != null) {
864 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800865 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700866 }
867 } finally {
868 restoreCallingIdentity(identityToken);
869 }
870 }
871
Matthew Williamsfa774182013-06-18 15:44:11 -0700872 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700873 public boolean getMasterSyncAutomatically() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100874 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
875 }
876
877 /**
878 * If the user id supplied is different to the calling user, the caller must hold the
879 * INTERACT_ACROSS_USERS_FULL permission.
880 */
881 @Override
882 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
883 enforceCrossUserPermission(userId,
884 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700885 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
886 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800887
Dianne Hackborn231cc602009-04-27 17:10:36 -0700888 long identityToken = clearCallingIdentity();
889 try {
890 SyncManager syncManager = getSyncManager();
891 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800892 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700893 }
894 } finally {
895 restoreCallingIdentity(identityToken);
896 }
897 return false;
898 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700899
Matthew Williamsfa774182013-06-18 15:44:11 -0700900 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700901 public void setMasterSyncAutomatically(boolean flag) {
Alexandra Gherghina0e9ac202014-07-15 23:11:48 +0100902 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
903 }
904
905 @Override
906 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
907 enforceCrossUserPermission(userId,
908 "no permission to set the sync status for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700909 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
910 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800911
Dianne Hackborn231cc602009-04-27 17:10:36 -0700912 long identityToken = clearCallingIdentity();
913 try {
914 SyncManager syncManager = getSyncManager();
915 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800916 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700917 }
918 } finally {
919 restoreCallingIdentity(identityToken);
920 }
921 }
922
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600923 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700924 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700925 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
926 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700927 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700928 long identityToken = clearCallingIdentity();
929 try {
930 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700931 if (syncManager == null) {
932 return false;
933 }
Matthew Williams5a9decd2014-06-04 09:25:11 -0700934 return syncManager.getSyncStorageEngine().isSyncActive(
935 new SyncStorageEngine.EndPoint(account, authority, userId));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700936 } finally {
937 restoreCallingIdentity(identityToken);
938 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700939 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700940
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600941 @Override
Fred Quintanac6a69552010-09-27 17:05:04 -0700942 public List<SyncInfo> getCurrentSyncs() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100943 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
944 }
945
946 /**
947 * If the user id supplied is different to the calling user, the caller must hold the
948 * INTERACT_ACROSS_USERS_FULL permission.
949 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600950 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100951 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
952 enforceCrossUserPermission(userId,
953 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700954 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
955 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800956
Matthew Williamsf39549e2016-01-19 23:04:04 +0000957 final boolean canAccessAccounts =
958 mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
959 == PackageManager.PERMISSION_GRANTED;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700960 long identityToken = clearCallingIdentity();
961 try {
Matthew Williamsf39549e2016-01-19 23:04:04 +0000962 return getSyncManager().getSyncStorageEngine()
963 .getCurrentSyncsCopy(userId, canAccessAccounts);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700964 } finally {
965 restoreCallingIdentity(identityToken);
966 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700967 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700968
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600969 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700970 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100971 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
972 }
973
974 /**
975 * If the user id supplied is different to the calling user, the caller must hold the
976 * INTERACT_ACROSS_USERS_FULL permission.
977 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600978 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100979 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000980 ComponentName cname, int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700981 if (TextUtils.isEmpty(authority)) {
982 throw new IllegalArgumentException("Authority must not be empty");
983 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100984
985 enforceCrossUserPermission(userId,
986 "no permission to read the sync stats for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700987 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
988 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800989
Dianne Hackborn231cc602009-04-27 17:10:36 -0700990 long identityToken = clearCallingIdentity();
991 try {
992 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700993 if (syncManager == null) {
994 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700995 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700996 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700997 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700998 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700999 } else {
1000 throw new IllegalArgumentException("Must call sync status with valid authority");
1001 }
1002 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001003 } finally {
1004 restoreCallingIdentity(identityToken);
1005 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001006 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001007
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001008 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001009 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +01001010 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
1011 }
1012
1013 @Override
1014 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001015 int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001016 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1017 "no permission to read the sync stats");
Alexandra Gherghinacb228072014-07-01 15:14:11 +01001018 enforceCrossUserPermission(userId,
1019 "no permission to retrieve the sync settings for user " + userId);
Torne (Richard Coles)4890b082013-08-12 10:26:57 +00001020 long identityToken = clearCallingIdentity();
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001021 SyncManager syncManager = getSyncManager();
1022 if (syncManager == null) return false;
1023
Dianne Hackborn231cc602009-04-27 17:10:36 -07001024 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001025 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -07001026 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001027 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001028 } else {
1029 throw new IllegalArgumentException("Invalid authority specified");
Dianne Hackborn231cc602009-04-27 17:10:36 -07001030 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001031 return syncManager.getSyncStorageEngine().isSyncPending(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001032 } finally {
1033 restoreCallingIdentity(identityToken);
1034 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001035 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001036
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001037 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -07001038 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
1039 long identityToken = clearCallingIdentity();
1040 try {
1041 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -08001042 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001043 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001044 }
1045 } finally {
1046 restoreCallingIdentity(identityToken);
1047 }
1048 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001049
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001050 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -07001051 public void removeStatusChangeListener(ISyncStatusObserver callback) {
1052 long identityToken = clearCallingIdentity();
1053 try {
1054 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -08001055 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001056 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001057 }
1058 } finally {
1059 restoreCallingIdentity(identityToken);
1060 }
1061 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001062
Jeff Sharkey87314082016-03-11 17:25:11 -07001063 private @Nullable String getProviderPackageName(Uri uri) {
1064 final ProviderInfo pi = mContext.getPackageManager()
1065 .resolveContentProvider(uri.getAuthority(), 0);
1066 return (pi != null) ? pi.packageName : null;
1067 }
1068
1069 private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1070 String providerPackageName) {
1071 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1072 if (userCache == null) {
1073 userCache = new ArrayMap<>();
1074 mCache.put(userId, userCache);
1075 }
1076 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1077 if (packageCache == null) {
1078 packageCache = new ArrayMap<>();
1079 userCache.put(providerPackageName, packageCache);
1080 }
1081 return packageCache;
1082 }
1083
1084 private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1085 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1086 if (userCache == null) return;
1087
1088 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1089 if (packageCache == null) return;
1090
1091 if (uri != null) {
1092 for (int i = 0; i < packageCache.size();) {
Jeff Sharkey7732e1e2016-03-30 17:14:23 -06001093 final Pair<String, Uri> key = packageCache.keyAt(i);
1094 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001095 if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
Jeff Sharkey87314082016-03-11 17:25:11 -07001096 packageCache.removeAt(i);
1097 } else {
1098 i++;
1099 }
1100 }
1101 } else {
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001102 if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
Jeff Sharkey87314082016-03-11 17:25:11 -07001103 packageCache.clear();
1104 }
1105 }
1106
1107 @Override
1108 public void putCache(String packageName, Uri key, Bundle value, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001109 Bundle.setDefusable(value, true);
Jeff Sharkey87314082016-03-11 17:25:11 -07001110 enforceCrossUserPermission(userId, TAG);
1111 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1112 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1113 packageName);
1114
1115 final String providerPackageName = getProviderPackageName(key);
1116 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1117
1118 synchronized (mCache) {
1119 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1120 providerPackageName);
1121 if (value != null) {
1122 cache.put(fullKey, value);
1123 } else {
1124 cache.remove(fullKey);
1125 }
1126 }
1127 }
1128
1129 @Override
1130 public Bundle getCache(String packageName, Uri key, int userId) {
1131 enforceCrossUserPermission(userId, TAG);
1132 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1133 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1134 packageName);
1135
1136 final String providerPackageName = getProviderPackageName(key);
1137 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1138
1139 synchronized (mCache) {
1140 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1141 providerPackageName);
1142 return cache.get(fullKey);
1143 }
1144 }
1145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 /**
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +01001147 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1148 * permission, if the userHandle is not for the caller.
1149 *
1150 * @param userHandle the user handle of the user we want to act on behalf of.
1151 * @param message the message to log on security exception.
1152 */
1153 private void enforceCrossUserPermission(int userHandle, String message) {
1154 final int callingUser = UserHandle.getCallingUserId();
1155 if (callingUser != userHandle) {
1156 mContext.enforceCallingOrSelfPermission(
1157 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1158 }
1159 }
1160
Svet Ganov96b9c752016-10-17 19:29:58 -07001161 private static int normalizeSyncable(int syncable) {
1162 if (syncable > 0) {
1163 return SyncStorageEngine.AuthorityInfo.SYNCABLE;
1164 } else if (syncable == 0) {
1165 return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
1166 }
1167 return SyncStorageEngine.AuthorityInfo.UNDEFINED;
1168 }
1169
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +01001170 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 * Hide this class since it is not part of api,
1172 * but current unittest framework requires it to be public
1173 * @hide
1174 */
1175 public static final class ObserverNode {
1176 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -08001177 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001178 public final int uid;
1179 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001180 public final boolean notifyForDescendants;
1181 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -08001182 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001184 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001185 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001186 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001188 uid = _uid;
1189 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001190 userHandle = _userHandle;
1191 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 try {
1193 observer.asBinder().linkToDeath(this, 0);
1194 } catch (RemoteException e) {
1195 binderDied();
1196 }
1197 }
1198
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001199 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -08001201 synchronized (observersLock) {
1202 removeObserverLocked(observer);
1203 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001205
1206 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001207 String name, String prefix, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001208 pidCounts.put(pid, pidCounts.get(pid)+1);
1209 pw.print(prefix); pw.print(name); pw.print(": pid=");
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001210 pw.print(pid); pw.print(" uid=");
1211 pw.print(uid); pw.print(" user=");
1212 pw.print(userHandle); pw.print(" target=");
1213 pw.println(Integer.toHexString(System.identityHashCode(
1214 observer != null ? observer.asBinder() : null)));
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 }
1217
1218 public static final int INSERT_TYPE = 0;
1219 public static final int UPDATE_TYPE = 1;
1220 public static final int DELETE_TYPE = 2;
1221
1222 private String mName;
1223 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1224 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1225
1226 public ObserverNode(String name) {
1227 mName = name;
1228 }
1229
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001230 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001231 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001232 String innerName = null;
1233 if (mObservers.size() > 0) {
1234 if ("".equals(name)) {
1235 innerName = mName;
1236 } else {
1237 innerName = name + "/" + mName;
1238 }
1239 for (int i=0; i<mObservers.size(); i++) {
1240 counts[1]++;
1241 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1242 pidCounts);
1243 }
1244 }
1245 if (mChildren.size() > 0) {
1246 if (innerName == null) {
1247 if ("".equals(name)) {
1248 innerName = mName;
1249 } else {
1250 innerName = name + "/" + mName;
1251 }
1252 }
1253 for (int i=0; i<mChildren.size(); i++) {
1254 counts[0]++;
1255 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1256 counts, pidCounts);
1257 }
1258 }
1259 }
1260
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 private String getUriSegment(Uri uri, int index) {
1262 if (uri != null) {
1263 if (index == 0) {
1264 return uri.getAuthority();
1265 } else {
1266 return uri.getPathSegments().get(index - 1);
1267 }
1268 } else {
1269 return null;
1270 }
1271 }
1272
1273 private int countUriSegments(Uri uri) {
1274 if (uri == null) {
1275 return 0;
1276 }
1277 return uri.getPathSegments().size() + 1;
1278 }
1279
Christopher Tate16aa9732012-09-17 16:23:44 -07001280 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -08001281 public void addObserverLocked(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001282 boolean notifyForDescendants, Object observersLock,
1283 int uid, int pid, int userHandle) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001284 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1285 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287
Fred Quintana002ffad52010-03-02 11:18:16 -08001288 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001289 boolean notifyForDescendants, Object observersLock,
1290 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 // If this is the leaf node add the observer
1292 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001293 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1294 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 return;
1296 }
1297
1298 // Look to see if the proper child already exists
1299 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -05001300 if (segment == null) {
1301 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303 int N = mChildren.size();
1304 for (int i = 0; i < N; i++) {
1305 ObserverNode node = mChildren.get(i);
1306 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001307 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1308 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 return;
1310 }
1311 }
1312
1313 // No child found, create one
1314 ObserverNode node = new ObserverNode(segment);
1315 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -07001316 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1317 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 }
1319
Fred Quintana002ffad52010-03-02 11:18:16 -08001320 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 int size = mChildren.size();
1322 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001323 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 if (empty) {
1325 mChildren.remove(i);
1326 i--;
1327 size--;
1328 }
1329 }
1330
1331 IBinder observerBinder = observer.asBinder();
1332 size = mObservers.size();
1333 for (int i = 0; i < size; i++) {
1334 ObserverEntry entry = mObservers.get(i);
1335 if (entry.observer.asBinder() == observerBinder) {
1336 mObservers.remove(i);
1337 // We no longer need to listen for death notifications. Remove it.
1338 observerBinder.unlinkToDeath(entry, 0);
1339 break;
1340 }
1341 }
1342
1343 if (mChildren.size() == 0 && mObservers.size() == 0) {
1344 return true;
1345 }
1346 return false;
1347 }
1348
Fred Quintana002ffad52010-03-02 11:18:16 -08001349 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001350 boolean observerWantsSelfNotifications, int flags,
1351 int targetUserHandle, ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 int N = mObservers.size();
1353 IBinder observerBinder = observer == null ? null : observer.asBinder();
1354 for (int i = 0; i < N; i++) {
1355 ObserverEntry entry = mObservers.get(i);
1356
Christopher Tate16aa9732012-09-17 16:23:44 -07001357 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -08001359 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1360 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 continue;
1362 }
1363
Christopher Tate16aa9732012-09-17 16:23:44 -07001364 // Does this observer match the target user?
1365 if (targetUserHandle == UserHandle.USER_ALL
1366 || entry.userHandle == UserHandle.USER_ALL
1367 || targetUserHandle == entry.userHandle) {
1368 // Make sure the observer is interested in the notification
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001369 if (leaf) {
1370 // If we are at the leaf: we always report, unless the sender has asked
1371 // to skip observers that are notifying for descendants (since they will
1372 // be sending another more specific URI for them).
1373 if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
1374 && entry.notifyForDescendants) {
1375 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1376 + ": skip notify for descendants");
1377 continue;
1378 }
1379 } else {
1380 // If we are not at the leaf: we report if the observer says it wants
1381 // to be notified for all descendants.
1382 if (!entry.notifyForDescendants) {
1383 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1384 + ": not monitor descendants");
1385 continue;
1386 }
Christopher Tate16aa9732012-09-17 16:23:44 -07001387 }
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001388 if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
1389 + " flags=" + Integer.toHexString(flags)
1390 + " desc=" + entry.notifyForDescendants);
Nicolas Prevot051f3b72016-05-18 18:44:00 +01001391 calls.add(new ObserverCall(this, entry.observer, selfChange,
1392 UserHandle.getUserId(entry.uid)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 }
1394 }
1395 }
1396
Christopher Tate16aa9732012-09-17 16:23:44 -07001397 /**
1398 * targetUserHandle is either a hard user handle or is USER_ALL
1399 */
Fred Quintana002ffad52010-03-02 11:18:16 -08001400 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001401 boolean observerWantsSelfNotifications, int flags,
1402 int targetUserHandle, ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 String segment = null;
1404 int segmentCount = countUriSegments(uri);
1405 if (index >= segmentCount) {
1406 // This is the leaf node, notify all observers
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001407 if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
Christopher Tate16aa9732012-09-17 16:23:44 -07001408 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001409 flags, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 } else if (index < segmentCount){
1411 segment = getUriSegment(uri, index);
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001412 if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
1413 + segment);
Christopher Tate16aa9732012-09-17 16:23:44 -07001414 // Notify any observers at this level who are interested in descendants
1415 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001416 flags, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 }
1418
1419 int N = mChildren.size();
1420 for (int i = 0; i < N; i++) {
1421 ObserverNode node = mChildren.get(i);
1422 if (segment == null || node.mName.equals(segment)) {
1423 // We found the child,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001424 node.collectObserversLocked(uri, index + 1, observer,
1425 observerWantsSelfNotifications, flags, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 if (segment != null) {
1427 break;
1428 }
1429 }
1430 }
1431 }
1432 }
1433}