blob: 886c97f4ce1793d597703d2c95d679ceaa19f5c4 [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 Sharkey923e0b82016-11-16 17:22:48 -070023import android.app.ActivityManagerInternal;
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;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.content.ContentResolver;
29import android.content.Context;
30import android.content.IContentService;
Jeff Sharkey7732e1e2016-03-30 17:14:23 -060031import android.content.ISyncStatusObserver;
Benjamin Franzadea1912015-06-19 16:03:38 +010032import android.content.Intent;
Jeff Sharkey87314082016-03-11 17:25:11 -070033import android.content.IntentFilter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080034import android.content.PeriodicSync;
35import android.content.SyncAdapterType;
36import android.content.SyncInfo;
Matthew Williamsfa774182013-06-18 15:44:11 -070037import android.content.SyncRequest;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080038import android.content.SyncStatusInfo;
Benjamin Franzadea1912015-06-19 16:03:38 +010039import android.content.pm.PackageManager;
Svetoslav0010b702015-06-30 18:05:26 -070040import android.content.pm.PackageManagerInternal;
Jeff Sharkey87314082016-03-11 17:25:11 -070041import android.content.pm.ProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.database.IContentObserver;
43import android.database.sqlite.SQLiteException;
44import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070045import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Bundle;
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060047import android.os.FactoryTest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070049import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.RemoteException;
Dan Morrille4d9a012013-03-28 18:10:43 -070051import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070052import android.os.UserHandle;
Matthew Williams632515b2013-10-10 15:51:00 -070053import android.text.TextUtils;
Jeff Sharkey87314082016-03-11 17:25:11 -070054import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.util.Log;
Jeff Sharkey87314082016-03-11 17:25:11 -070056import android.util.Pair;
Jeff Sharkey60094592013-03-21 18:14:24 -070057import android.util.Slog;
Jeff Sharkey87314082016-03-11 17:25:11 -070058import android.util.SparseArray;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070059import android.util.SparseIntArray;
Jeff Sharkey87314082016-03-11 17:25:11 -070060
61import com.android.internal.annotations.GuardedBy;
62import com.android.internal.util.IndentingPrintWriter;
Svetoslav0010b702015-06-30 18:05:26 -070063import com.android.server.LocalServices;
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060064import com.android.server.SystemService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66import java.io.FileDescriptor;
67import java.io.PrintWriter;
68import java.util.ArrayList;
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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73/**
74 * {@hide}
75 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070076public final class ContentService extends IContentService.Stub {
Dianne Hackborn141f11c2016-04-05 15:46:12 -070077 static final String TAG = "ContentService";
78 static final boolean DEBUG = false;
Jeff Sharkey87314082016-03-11 17:25:11 -070079
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060080 public static class Lifecycle extends SystemService {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -060081 private ContentService mService;
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -060082
83 public Lifecycle(Context context) {
84 super(context);
85 }
86
87 @Override
88 public void onStart() {
89 final boolean factoryTest = (FactoryTest
90 .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
Jeff Sharkey1cab76a2016-04-12 18:23:31 -060091 mService = new ContentService(getContext(), factoryTest);
92 publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
93 }
94
95 @Override
96 public void onBootPhase(int phase) {
97 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
98 mService.systemReady();
99 }
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600100 }
101
102 @Override
103 public void onCleanupUser(int userHandle) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600104 synchronized (mService.mCache) {
105 mService.mCache.remove(userHandle);
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600106 }
107 }
108 }
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private Context mContext;
111 private boolean mFactoryTest;
Jeff Sharkey87314082016-03-11 17:25:11 -0700112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 private final ObserverNode mRootNode = new ObserverNode("");
Jeff Sharkey87314082016-03-11 17:25:11 -0700114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private SyncManager mSyncManager = null;
116 private final Object mSyncManagerLock = new Object();
117
Jeff Sharkey87314082016-03-11 17:25:11 -0700118 /**
119 * Map from userId to providerPackageName to [clientPackageName, uri] to
120 * value. This structure is carefully optimized to keep invalidation logic
121 * as cheap as possible.
122 */
123 @GuardedBy("mCache")
124 private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
125 mCache = new SparseArray<>();
126
127 private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
128 @Override
129 public void onReceive(Context context, Intent intent) {
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600130 synchronized (mCache) {
131 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
132 mCache.clear();
133 } else {
134 final Uri data = intent.getData();
135 if (data != null) {
136 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
137 UserHandle.USER_NULL);
138 final String packageName = data.getSchemeSpecificPart();
139 invalidateCacheLocked(userId, packageName, null);
140 }
141 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700142 }
143 }
144 };
145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -0700147 if (SystemProperties.getBoolean("config.disable_network", false)) {
148 return null;
149 }
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 synchronized(mSyncManagerLock) {
152 try {
153 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
154 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
155 } catch (SQLiteException e) {
156 Log.e(TAG, "Can't create SyncManager", e);
157 }
158 return mSyncManager;
159 }
160 }
161
162 @Override
Jeff Sharkey87314082016-03-11 17:25:11 -0700163 protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
165 "caller doesn't have the DUMP permission");
166
Jeff Sharkey87314082016-03-11 17:25:11 -0700167 final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 // This makes it so that future permission checks will be in the context of this
170 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey87314082016-03-11 17:25:11 -0700171 final long identityToken = clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 try {
173 if (mSyncManager == null) {
174 pw.println("No SyncManager created! (Disk full?)");
175 } else {
176 mSyncManager.dump(fd, pw);
177 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700178 pw.println();
179 pw.println("Observer tree:");
180 synchronized (mRootNode) {
181 int[] counts = new int[2];
182 final SparseIntArray pidCounts = new SparseIntArray();
183 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
184 pw.println();
185 ArrayList<Integer> sorted = new ArrayList<Integer>();
186 for (int i=0; i<pidCounts.size(); i++) {
187 sorted.add(pidCounts.keyAt(i));
188 }
189 Collections.sort(sorted, new Comparator<Integer>() {
190 @Override
191 public int compare(Integer lhs, Integer rhs) {
192 int lc = pidCounts.get(lhs);
193 int rc = pidCounts.get(rhs);
194 if (lc < rc) {
195 return 1;
196 } else if (lc > rc) {
197 return -1;
198 }
199 return 0;
200 }
201
202 });
203 for (int i=0; i<sorted.size(); i++) {
204 int pid = sorted.get(i);
205 pw.print(" pid "); pw.print(pid); pw.print(": ");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000206 pw.print(pidCounts.get(pid)); pw.println(" observers");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700207 }
208 pw.println();
209 pw.print(" Total number of nodes: "); pw.println(counts[0]);
210 pw.print(" Total number of observers: "); pw.println(counts[1]);
211 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700212
213 synchronized (mCache) {
214 pw.println();
215 pw.println("Cached content:");
216 pw.increaseIndent();
217 for (int i = 0; i < mCache.size(); i++) {
218 pw.println("User " + mCache.keyAt(i) + ":");
219 pw.increaseIndent();
220 pw.println(mCache.valueAt(i));
221 pw.decreaseIndent();
222 }
223 pw.decreaseIndent();
224 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 } finally {
226 restoreCallingIdentity(identityToken);
227 }
228 }
229
Dianne Hackborn231cc602009-04-27 17:10:36 -0700230 @Override
231 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
232 throws RemoteException {
233 try {
234 return super.onTransact(code, data, reply, flags);
235 } catch (RuntimeException e) {
236 // The content service only throws security exceptions, so let's
237 // log all others.
238 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700239 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700240 }
241 throw e;
242 }
243 }
244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 /*package*/ ContentService(Context context, boolean factoryTest) {
246 mContext = context;
247 mFactoryTest = factoryTest;
Svetoslav0010b702015-06-30 18:05:26 -0700248
249 // Let the package manager query for the sync adapters for a given authority
Svet Ganov52153f42015-08-11 08:59:12 -0700250 // as we grant default permissions to sync adapters for specific authorities.
Svetoslav0010b702015-06-30 18:05:26 -0700251 PackageManagerInternal packageManagerInternal = LocalServices.getService(
252 PackageManagerInternal.class);
253 packageManagerInternal.setSyncAdapterPackagesprovider(
254 new PackageManagerInternal.SyncAdapterPackagesProvider() {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000255 @Override
256 public String[] getPackages(String authority, int userId) {
257 return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
258 }
259 });
Jeff Sharkey87314082016-03-11 17:25:11 -0700260
261 final IntentFilter packageFilter = new IntentFilter();
262 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
263 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
264 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
265 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
266 packageFilter.addDataScheme("package");
267 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
268 packageFilter, null, null);
Jeff Sharkeya3ebfec2016-04-04 08:58:04 -0600269
270 final IntentFilter localeFilter = new IntentFilter();
271 localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
272 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
273 localeFilter, null, null);
Kenny Root26ff6622012-07-30 12:58:03 -0700274 }
275
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600276 void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 getSyncManager();
278 }
279
Christopher Tate16aa9732012-09-17 16:23:44 -0700280 /**
281 * Register a content observer tied to a specific user's view of the provider.
282 * @param userHandle the user whose view of the provider is to be observed. May be
283 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100284 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
285 * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
286 * are forbidden.
Christopher Tate16aa9732012-09-17 16:23:44 -0700287 */
288 @Override
289 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000290 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 if (observer == null || uri == null) {
292 throw new IllegalArgumentException("You must pass a valid uri and observer");
293 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700294
Benjamin Franzadea1912015-06-19 16:03:38 +0100295 final int uid = Binder.getCallingUid();
296 final int pid = Binder.getCallingPid();
Christopher Tate16aa9732012-09-17 16:23:44 -0700297
Jeff Sharkey923e0b82016-11-16 17:22:48 -0700298 userHandle = handleIncomingUser(uri, pid, uid,
299 Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle);
300
301 final String msg = LocalServices.getService(ActivityManagerInternal.class)
302 .checkContentProviderAccess(uri.getAuthority(), userHandle);
303 if (msg != null) {
304 Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
305 return;
Christopher Tate16aa9732012-09-17 16:23:44 -0700306 }
307
308 synchronized (mRootNode) {
309 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
Benjamin Franzadea1912015-06-19 16:03:38 +0100310 uid, pid, userHandle);
Christopher Tate16aa9732012-09-17 16:23:44 -0700311 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
312 " with notifyForDescendants " + notifyForDescendants);
313 }
314 }
315
316 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000317 IContentObserver observer) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700318 registerContentObserver(uri, notifyForDescendants, observer,
319 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 }
321
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600322 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 public void unregisterContentObserver(IContentObserver observer) {
324 if (observer == null) {
325 throw new IllegalArgumentException("You must pass a valid observer");
326 }
327 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800328 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700329 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331 }
332
Christopher Tate16aa9732012-09-17 16:23:44 -0700333 /**
334 * Notify observers of a particular user's view of the provider.
335 * @param userHandle the user whose view of the provider is to be notified. May be
336 * the calling user without requiring any permission, otherwise the caller needs to
Benjamin Franzadea1912015-06-19 16:03:38 +0100337 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
338 * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
339 * allowed.
Christopher Tate16aa9732012-09-17 16:23:44 -0700340 */
341 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 public void notifyChange(Uri uri, IContentObserver observer,
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700343 boolean observerWantsSelfNotifications, int flags,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000344 int userHandle) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700345 if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
346 + " from observer " + observer + ", flags " + Integer.toHexString(flags));
Amith Yamasani04e0d262012-02-14 11:50:53 -0800347
Dianne Hackborne7617772016-04-27 17:03:52 -0700348 if (uri == null) {
349 throw new NullPointerException("Uri must not be null");
350 }
351
Benjamin Franzadea1912015-06-19 16:03:38 +0100352 final int uid = Binder.getCallingUid();
353 final int pid = Binder.getCallingPid();
Christopher Tate16aa9732012-09-17 16:23:44 -0700354 final int callingUserHandle = UserHandle.getCallingUserId();
Christopher Tate16aa9732012-09-17 16:23:44 -0700355
Jeff Sharkey923e0b82016-11-16 17:22:48 -0700356 userHandle = handleIncomingUser(uri, pid, uid,
357 Intent.FLAG_GRANT_WRITE_URI_PERMISSION, userHandle);
358
359 final String msg = LocalServices.getService(ActivityManagerInternal.class)
360 .checkContentProviderAccess(uri.getAuthority(), userHandle);
361 if (msg != null) {
362 Log.w(TAG, "Ignoring notify for " + uri + " from " + uid + ": " + msg);
363 return;
Christopher Tate16aa9732012-09-17 16:23:44 -0700364 }
365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 // This makes it so that future permission checks will be in the context of this
367 // process rather than the caller's process. We will restore this before returning.
368 long identityToken = clearCallingIdentity();
369 try {
370 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
371 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800372 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700373 flags, userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
375 final int numCalls = calls.size();
376 for (int i=0; i<numCalls; i++) {
377 ObserverCall oc = calls.get(i);
378 try {
Svet Ganov3138a992016-06-15 16:57:32 -0700379 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700380 if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
Svet Ganov3138a992016-06-15 16:57:32 -0700381 + uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 } catch (RemoteException ex) {
383 synchronized (mRootNode) {
384 Log.w(TAG, "Found dead observer, removing");
385 IBinder binder = oc.mObserver.asBinder();
386 final ArrayList<ObserverNode.ObserverEntry> list
387 = oc.mNode.mObservers;
388 int numList = list.size();
389 for (int j=0; j<numList; j++) {
390 ObserverNode.ObserverEntry oe = list.get(j);
391 if (oe.observer.asBinder() == binder) {
392 list.remove(j);
393 j--;
394 numList--;
395 }
396 }
397 }
398 }
399 }
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700400 if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700402 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700403 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800404 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 }
Jeff Sharkey87314082016-03-11 17:25:11 -0700407
408 synchronized (mCache) {
409 final String providerPackageName = getProviderPackageName(uri);
410 invalidateCacheLocked(userHandle, providerPackageName, uri);
411 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 } finally {
413 restoreCallingIdentity(identityToken);
414 }
415 }
416
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100417 private int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle) {
418 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800419 return ActivityManager.getService().checkUriPermission(
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100420 uri, pid, uid, modeFlags, userHandle, null);
421 } catch (RemoteException e) {
422 return PackageManager.PERMISSION_DENIED;
423 }
424 }
425
Christopher Tate16aa9732012-09-17 16:23:44 -0700426 public void notifyChange(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000427 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700428 notifyChange(uri, observer, observerWantsSelfNotifications,
429 syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
Christopher Tate16aa9732012-09-17 16:23:44 -0700430 UserHandle.getCallingUserId());
431 }
432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 /**
434 * Hide this class since it is not part of api,
435 * but current unittest framework requires it to be public
436 * @hide
437 *
438 */
439 public static final class ObserverCall {
440 final ObserverNode mNode;
441 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800442 final boolean mSelfChange;
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100443 final int mObserverUserId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100445 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange, int observerUserId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 mNode = node;
447 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800448 mSelfChange = selfChange;
Nicolas Prevot051f3b72016-05-18 18:44:00 +0100449 mObserverUserId = observerUserId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 }
451 }
452
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600453 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700454 public void requestSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600455 Bundle.setDefusable(extras, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700457 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700458 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 // This makes it so that future permission checks will be in the context of this
461 // process rather than the caller's process. We will restore this before returning.
462 long identityToken = clearCallingIdentity();
463 try {
464 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700465 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700466 syncManager.scheduleSync(account, userId, uId, authority, extras,
Svet Ganov91d37f42016-09-03 19:36:26 -0700467 SyncStorageEngine.AuthorityInfo.UNDEFINED);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700468 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 } finally {
470 restoreCallingIdentity(identityToken);
471 }
472 }
473
474 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700475 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
476 * either:
477 * periodic OR one-off sync.
478 * and
479 * anonymous OR provider sync.
480 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700481 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700482 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600483 @Override
Matthew Williamsfa774182013-06-18 15:44:11 -0700484 public void sync(SyncRequest request) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100485 syncAsUser(request, UserHandle.getCallingUserId());
486 }
487
Shreyas Basarge3b840e32016-06-14 15:32:02 +0100488 private long clampPeriod(long period) {
489 long minPeriod = JobInfo.getMinPeriodMillis() / 1000;
490 if (period < minPeriod) {
491 Slog.w(TAG, "Requested poll frequency of " + period
492 + " seconds being rounded up to " + minPeriod + "s.");
493 period = minPeriod;
494 }
495 return period;
496 }
497
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100498 /**
499 * If the user id supplied is different to the calling user, the caller must hold the
500 * INTERACT_ACROSS_USERS_FULL permission.
501 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600502 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100503 public void syncAsUser(SyncRequest request, int userId) {
504 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700505 int callerUid = Binder.getCallingUid();
Matthew Williamsfa774182013-06-18 15:44:11 -0700506 // This makes it so that future permission checks will be in the context of this
507 // process rather than the caller's process. We will restore this before returning.
508 long identityToken = clearCallingIdentity();
509 try {
510 SyncManager syncManager = getSyncManager();
Matthew Williamsd08d6682013-10-14 10:39:41 -0700511 if (syncManager == null) {
512 return;
513 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700514
Matthew Williamsd08d6682013-10-14 10:39:41 -0700515 Bundle extras = request.getBundle();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700516 long flextime = request.getSyncFlexTime();
517 long runAtTime = request.getSyncRunTime();
518 if (request.isPeriodic()) {
519 mContext.enforceCallingOrSelfPermission(
520 Manifest.permission.WRITE_SYNC_SETTINGS,
521 "no permission to write the sync settings");
522 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700523 info = new SyncStorageEngine.EndPoint(
524 request.getAccount(), request.getProvider(), userId);
Shreyas Basarge3b840e32016-06-14 15:32:02 +0100525
526 runAtTime = clampPeriod(runAtTime);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700527 // Schedule periodic sync.
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000528 getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
529 flextime, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700530 } else {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700531 syncManager.scheduleSync(
532 request.getAccount(), userId, callerUid, request.getProvider(), extras,
Svet Ganov91d37f42016-09-03 19:36:26 -0700533 SyncStorageEngine.AuthorityInfo.UNDEFINED);
Matthew Williamsfa774182013-06-18 15:44:11 -0700534 }
535 } finally {
536 restoreCallingIdentity(identityToken);
537 }
538 }
539
540 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700542 * if they match the authority and account, if they are present.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700543 *
544 * @param account filter the pending and active syncs to cancel using this account, or null.
545 * @param authority filter the pending and active syncs to cancel using this authority, or
546 * null.
547 * @param cname cancel syncs running on this service, or null for provider/account.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700549 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700550 public void cancelSync(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100551 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
552 }
553
554 /**
555 * Clear all scheduled sync operations that match the uri and cancel the active sync
556 * if they match the authority and account, if they are present.
557 *
558 * <p> If the user id supplied is different to the calling user, the caller must hold the
559 * INTERACT_ACROSS_USERS_FULL permission.
560 *
561 * @param account filter the pending and active syncs to cancel using this account, or null.
562 * @param authority filter the pending and active syncs to cancel using this authority, or
563 * null.
564 * @param userId the user id for which to cancel sync operations.
565 * @param cname cancel syncs running on this service, or null for provider/account.
566 */
567 @Override
568 public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000569 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700570 if (authority != null && authority.length() == 0) {
571 throw new IllegalArgumentException("Authority must be non-empty");
572 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100573 enforceCrossUserPermission(userId,
574 "no permission to modify the sync settings for user " + userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 // This makes it so that future permission checks will be in the context of this
576 // process rather than the caller's process. We will restore this before returning.
577 long identityToken = clearCallingIdentity();
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000578 if (cname != null) {
579 Slog.e(TAG, "cname not null.");
580 return;
581 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 try {
583 SyncManager syncManager = getSyncManager();
584 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700585 SyncStorageEngine.EndPoint info;
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000586 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700587 syncManager.clearScheduledSyncOperations(info);
588 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
590 } finally {
591 restoreCallingIdentity(identityToken);
592 }
593 }
594
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600595 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700596 public void cancelRequest(SyncRequest request) {
597 SyncManager syncManager = getSyncManager();
598 if (syncManager == null) return;
599 int userId = UserHandle.getCallingUserId();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700600
601 long identityToken = clearCallingIdentity();
602 try {
603 SyncStorageEngine.EndPoint info;
604 Bundle extras = new Bundle(request.getBundle());
Matthew Williams5a9decd2014-06-04 09:25:11 -0700605 Account account = request.getAccount();
606 String provider = request.getProvider();
607 info = new SyncStorageEngine.EndPoint(account, provider, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700608 if (request.isPeriodic()) {
609 // Remove periodic sync.
610 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
611 "no permission to write the sync settings");
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000612 getSyncManager().removePeriodicSync(info, extras);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700613 }
614 // Cancel active syncs and clear pending syncs from the queue.
615 syncManager.cancelScheduledSyncOperation(info, extras);
616 syncManager.cancelActiveSync(info, extras);
617 } finally {
618 restoreCallingIdentity(identityToken);
619 }
620 }
621
Fred Quintanaac9385e2009-06-22 18:00:59 -0700622 /**
623 * Get information about the SyncAdapters that are known to the system.
624 * @return an array of SyncAdapters that have registered with the system
625 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700626 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700627 public SyncAdapterType[] getSyncAdapterTypes() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100628 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
629 }
630
631 /**
632 * Get information about the SyncAdapters that are known to the system for a particular user.
633 *
634 * <p> If the user id supplied is different to the calling user, the caller must hold the
635 * INTERACT_ACROSS_USERS_FULL permission.
636 *
637 * @return an array of SyncAdapters that have registered with the system
638 */
639 @Override
640 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
641 enforceCrossUserPermission(userId,
642 "no permission to read sync settings for user " + userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700643 // This makes it so that future permission checks will be in the context of this
644 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700645 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700646 try {
647 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700648 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700649 } finally {
650 restoreCallingIdentity(identityToken);
651 }
652 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700653
Matthew Williamsfa774182013-06-18 15:44:11 -0700654 @Override
Amith Yamasani37a40c22015-06-17 13:25:42 -0700655 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
656 enforceCrossUserPermission(userId,
657 "no permission to read sync settings for user " + userId);
658 // This makes it so that future permission checks will be in the context of this
659 // process rather than the caller's process. We will restore this before returning.
660 final long identityToken = clearCallingIdentity();
661 try {
662 SyncManager syncManager = getSyncManager();
663 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
664 } finally {
665 restoreCallingIdentity(identityToken);
666 }
667 }
668
669 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700670 public boolean getSyncAutomatically(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100671 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
672 }
673
674 /**
675 * If the user id supplied is different to the calling user, the caller must hold the
676 * INTERACT_ACROSS_USERS_FULL permission.
677 */
678 @Override
679 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
680 enforceCrossUserPermission(userId,
681 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700682 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
683 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800684
Dianne Hackborn231cc602009-04-27 17:10:36 -0700685 long identityToken = clearCallingIdentity();
686 try {
687 SyncManager syncManager = getSyncManager();
688 if (syncManager != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700689 return syncManager.getSyncStorageEngine()
690 .getSyncAutomatically(account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700691 }
692 } finally {
693 restoreCallingIdentity(identityToken);
694 }
695 return false;
696 }
697
Matthew Williamsfa774182013-06-18 15:44:11 -0700698 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700699 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100700 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
701 }
702
703 @Override
704 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000705 int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700706 if (TextUtils.isEmpty(providerName)) {
707 throw new IllegalArgumentException("Authority must be non-empty");
708 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700709 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
710 "no permission to write the sync settings");
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100711 enforceCrossUserPermission(userId,
712 "no permission to modify the sync settings for user " + userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800713
Dianne Hackborn231cc602009-04-27 17:10:36 -0700714 long identityToken = clearCallingIdentity();
715 try {
716 SyncManager syncManager = getSyncManager();
717 if (syncManager != null) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100718 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
719 providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700720 }
721 } finally {
722 restoreCallingIdentity(identityToken);
723 }
724 }
725
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000726 /** Old API. Schedule periodic sync with default flexMillis time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700727 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800728 public void addPeriodicSync(Account account, String authority, Bundle extras,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000729 long pollFrequency) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600730 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700731 if (account == null) {
732 throw new IllegalArgumentException("Account must not be null");
733 }
734 if (TextUtils.isEmpty(authority)) {
735 throw new IllegalArgumentException("Authority must not be empty.");
736 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800737 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
738 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800739
Matthew Williams632515b2013-10-10 15:51:00 -0700740 int userId = UserHandle.getCallingUserId();
Shreyas Basarge3b840e32016-06-14 15:32:02 +0100741
742 pollFrequency = clampPeriod(pollFrequency);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700743 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
Jeff Sharkey60094592013-03-21 18:14:24 -0700744
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800745 long identityToken = clearCallingIdentity();
746 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700747 SyncStorageEngine.EndPoint info =
748 new SyncStorageEngine.EndPoint(account, authority, userId);
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000749 getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
750 defaultFlex, extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800751 } finally {
752 restoreCallingIdentity(identityToken);
753 }
754 }
755
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600756 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800757 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600758 Bundle.setDefusable(extras, true);
Matthew Williams632515b2013-10-10 15:51:00 -0700759 if (account == null) {
760 throw new IllegalArgumentException("Account must not be null");
761 }
762 if (TextUtils.isEmpty(authority)) {
763 throw new IllegalArgumentException("Authority must not be empty");
764 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800765 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
766 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800767
Matthew Williams632515b2013-10-10 15:51:00 -0700768 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800769 long identityToken = clearCallingIdentity();
770 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000771 getSyncManager()
772 .removePeriodicSync(
773 new SyncStorageEngine.EndPoint(account, authority, userId),
774 extras);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800775 } finally {
776 restoreCallingIdentity(identityToken);
777 }
778 }
779
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600780 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700781 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000782 ComponentName cname) {
Matthew Williams632515b2013-10-10 15:51:00 -0700783 if (account == null) {
784 throw new IllegalArgumentException("Account must not be null");
785 }
786 if (TextUtils.isEmpty(providerName)) {
787 throw new IllegalArgumentException("Authority must not be empty");
788 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800789 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
790 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800791
Matthew Williams632515b2013-10-10 15:51:00 -0700792 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800793 long identityToken = clearCallingIdentity();
794 try {
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000795 return getSyncManager().getPeriodicSyncs(
Matthew Williams5a9decd2014-06-04 09:25:11 -0700796 new SyncStorageEngine.EndPoint(account, providerName, userId));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800797 } finally {
798 restoreCallingIdentity(identityToken);
799 }
800 }
801
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600802 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700803 public int getIsSyncable(Account account, String providerName) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100804 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
805 }
806
807 /**
808 * If the user id supplied is different to the calling user, the caller must hold the
809 * INTERACT_ACROSS_USERS_FULL permission.
810 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600811 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100812 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
813 enforceCrossUserPermission(userId,
814 "no permission to read the sync settings for user " + userId);
Fred Quintana5e787c42009-08-16 23:13:53 -0700815 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
816 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800817
Fred Quintana5e787c42009-08-16 23:13:53 -0700818 long identityToken = clearCallingIdentity();
819 try {
820 SyncManager syncManager = getSyncManager();
821 if (syncManager != null) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700822 return syncManager.computeSyncable(
Svet Ganov96b9c752016-10-17 19:29:58 -0700823 account, userId, providerName, false);
Fred Quintana5e787c42009-08-16 23:13:53 -0700824 }
825 } finally {
826 restoreCallingIdentity(identityToken);
827 }
828 return -1;
829 }
830
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600831 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700832 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700833 if (TextUtils.isEmpty(providerName)) {
834 throw new IllegalArgumentException("Authority must not be empty");
835 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700836 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
837 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800838
Svet Ganov96b9c752016-10-17 19:29:58 -0700839 syncable = normalizeSyncable(syncable);
840
Matthew Williams632515b2013-10-10 15:51:00 -0700841 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700842 long identityToken = clearCallingIdentity();
843 try {
844 SyncManager syncManager = getSyncManager();
845 if (syncManager != null) {
846 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800847 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700848 }
849 } finally {
850 restoreCallingIdentity(identityToken);
851 }
852 }
853
Matthew Williamsfa774182013-06-18 15:44:11 -0700854 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700855 public boolean getMasterSyncAutomatically() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100856 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
857 }
858
859 /**
860 * If the user id supplied is different to the calling user, the caller must hold the
861 * INTERACT_ACROSS_USERS_FULL permission.
862 */
863 @Override
864 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
865 enforceCrossUserPermission(userId,
866 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700867 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
868 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800869
Dianne Hackborn231cc602009-04-27 17:10:36 -0700870 long identityToken = clearCallingIdentity();
871 try {
872 SyncManager syncManager = getSyncManager();
873 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800874 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700875 }
876 } finally {
877 restoreCallingIdentity(identityToken);
878 }
879 return false;
880 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700881
Matthew Williamsfa774182013-06-18 15:44:11 -0700882 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700883 public void setMasterSyncAutomatically(boolean flag) {
Alexandra Gherghina0e9ac202014-07-15 23:11:48 +0100884 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
885 }
886
887 @Override
888 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
889 enforceCrossUserPermission(userId,
890 "no permission to set the sync status for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700891 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
892 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800893
Dianne Hackborn231cc602009-04-27 17:10:36 -0700894 long identityToken = clearCallingIdentity();
895 try {
896 SyncManager syncManager = getSyncManager();
897 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800898 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700899 }
900 } finally {
901 restoreCallingIdentity(identityToken);
902 }
903 }
904
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600905 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700906 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700907 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
908 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700909 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700910 long identityToken = clearCallingIdentity();
911 try {
912 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700913 if (syncManager == null) {
914 return false;
915 }
Matthew Williams5a9decd2014-06-04 09:25:11 -0700916 return syncManager.getSyncStorageEngine().isSyncActive(
917 new SyncStorageEngine.EndPoint(account, authority, userId));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700918 } finally {
919 restoreCallingIdentity(identityToken);
920 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700921 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700922
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600923 @Override
Fred Quintanac6a69552010-09-27 17:05:04 -0700924 public List<SyncInfo> getCurrentSyncs() {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100925 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
926 }
927
928 /**
929 * If the user id supplied is different to the calling user, the caller must hold the
930 * INTERACT_ACROSS_USERS_FULL permission.
931 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600932 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100933 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
934 enforceCrossUserPermission(userId,
935 "no permission to read the sync settings for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700936 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
937 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800938
Matthew Williamsf39549e2016-01-19 23:04:04 +0000939 final boolean canAccessAccounts =
940 mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
941 == PackageManager.PERMISSION_GRANTED;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700942 long identityToken = clearCallingIdentity();
943 try {
Matthew Williamsf39549e2016-01-19 23:04:04 +0000944 return getSyncManager().getSyncStorageEngine()
945 .getCurrentSyncsCopy(userId, canAccessAccounts);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700946 } finally {
947 restoreCallingIdentity(identityToken);
948 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700949 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700950
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600951 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700952 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100953 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
954 }
955
956 /**
957 * If the user id supplied is different to the calling user, the caller must hold the
958 * INTERACT_ACROSS_USERS_FULL permission.
959 */
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600960 @Override
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100961 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000962 ComponentName cname, int userId) {
Matthew Williams632515b2013-10-10 15:51:00 -0700963 if (TextUtils.isEmpty(authority)) {
964 throw new IllegalArgumentException("Authority must not be empty");
965 }
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +0100966
967 enforceCrossUserPermission(userId,
968 "no permission to read the sync stats for user " + userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700969 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
970 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800971
Dianne Hackborn231cc602009-04-27 17:10:36 -0700972 long identityToken = clearCallingIdentity();
973 try {
974 SyncManager syncManager = getSyncManager();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700975 if (syncManager == null) {
976 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700977 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700978 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -0700979 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700980 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700981 } else {
982 throw new IllegalArgumentException("Must call sync status with valid authority");
983 }
984 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700985 } finally {
986 restoreCallingIdentity(identityToken);
987 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700988 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700989
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600990 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700991 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
Alexandra Gherghinacb228072014-07-01 15:14:11 +0100992 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
993 }
994
995 @Override
996 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
Shreyas Basarge8c834c02016-01-07 13:53:16 +0000997 int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700998 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
999 "no permission to read the sync stats");
Alexandra Gherghinacb228072014-07-01 15:14:11 +01001000 enforceCrossUserPermission(userId,
1001 "no permission to retrieve the sync settings for user " + userId);
Torne (Richard Coles)4890b082013-08-12 10:26:57 +00001002 long identityToken = clearCallingIdentity();
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001003 SyncManager syncManager = getSyncManager();
1004 if (syncManager == null) return false;
1005
Dianne Hackborn231cc602009-04-27 17:10:36 -07001006 try {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001007 SyncStorageEngine.EndPoint info;
Matthew Williams5a9decd2014-06-04 09:25:11 -07001008 if (!(account == null || authority == null)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001009 info = new SyncStorageEngine.EndPoint(account, authority, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001010 } else {
1011 throw new IllegalArgumentException("Invalid authority specified");
Dianne Hackborn231cc602009-04-27 17:10:36 -07001012 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001013 return syncManager.getSyncStorageEngine().isSyncPending(info);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001014 } finally {
1015 restoreCallingIdentity(identityToken);
1016 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001017 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001018
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001019 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -07001020 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
1021 long identityToken = clearCallingIdentity();
1022 try {
1023 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -08001024 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001025 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001026 }
1027 } finally {
1028 restoreCallingIdentity(identityToken);
1029 }
1030 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001031
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001032 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -07001033 public void removeStatusChangeListener(ISyncStatusObserver callback) {
1034 long identityToken = clearCallingIdentity();
1035 try {
1036 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -08001037 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001038 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001039 }
1040 } finally {
1041 restoreCallingIdentity(identityToken);
1042 }
1043 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001044
Jeff Sharkey87314082016-03-11 17:25:11 -07001045 private @Nullable String getProviderPackageName(Uri uri) {
1046 final ProviderInfo pi = mContext.getPackageManager()
1047 .resolveContentProvider(uri.getAuthority(), 0);
1048 return (pi != null) ? pi.packageName : null;
1049 }
1050
1051 private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1052 String providerPackageName) {
1053 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1054 if (userCache == null) {
1055 userCache = new ArrayMap<>();
1056 mCache.put(userId, userCache);
1057 }
1058 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1059 if (packageCache == null) {
1060 packageCache = new ArrayMap<>();
1061 userCache.put(providerPackageName, packageCache);
1062 }
1063 return packageCache;
1064 }
1065
1066 private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1067 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1068 if (userCache == null) return;
1069
1070 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1071 if (packageCache == null) return;
1072
1073 if (uri != null) {
1074 for (int i = 0; i < packageCache.size();) {
Jeff Sharkey7732e1e2016-03-30 17:14:23 -06001075 final Pair<String, Uri> key = packageCache.keyAt(i);
1076 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001077 if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
Jeff Sharkey87314082016-03-11 17:25:11 -07001078 packageCache.removeAt(i);
1079 } else {
1080 i++;
1081 }
1082 }
1083 } else {
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001084 if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
Jeff Sharkey87314082016-03-11 17:25:11 -07001085 packageCache.clear();
1086 }
1087 }
1088
1089 @Override
1090 public void putCache(String packageName, Uri key, Bundle value, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001091 Bundle.setDefusable(value, true);
Jeff Sharkey87314082016-03-11 17:25:11 -07001092 enforceCrossUserPermission(userId, TAG);
1093 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1094 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1095 packageName);
1096
1097 final String providerPackageName = getProviderPackageName(key);
1098 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1099
1100 synchronized (mCache) {
1101 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1102 providerPackageName);
1103 if (value != null) {
1104 cache.put(fullKey, value);
1105 } else {
1106 cache.remove(fullKey);
1107 }
1108 }
1109 }
1110
1111 @Override
1112 public Bundle getCache(String packageName, Uri key, int userId) {
1113 enforceCrossUserPermission(userId, TAG);
1114 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1115 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1116 packageName);
1117
1118 final String providerPackageName = getProviderPackageName(key);
1119 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1120
1121 synchronized (mCache) {
1122 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1123 providerPackageName);
1124 return cache.get(fullKey);
1125 }
1126 }
1127
Jeff Sharkey923e0b82016-11-16 17:22:48 -07001128 private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, int userId) {
1129 if (userId == UserHandle.USER_CURRENT) {
1130 userId = ActivityManager.getCurrentUser();
1131 }
1132
1133 if (userId == UserHandle.USER_ALL) {
1134 mContext.enforceCallingOrSelfPermission(
1135 Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
1136 } else if (userId < 0) {
1137 throw new IllegalArgumentException("Invalid user: " + userId);
1138 } else if (userId != UserHandle.getCallingUserId()) {
1139 if (checkUriPermission(uri, pid, uid, modeFlags,
1140 userId) != PackageManager.PERMISSION_GRANTED) {
1141 mContext.enforceCallingOrSelfPermission(
1142 Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
1143 }
1144 }
1145
1146 return userId;
1147 }
1148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 /**
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +01001150 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1151 * permission, if the userHandle is not for the caller.
1152 *
1153 * @param userHandle the user handle of the user we want to act on behalf of.
1154 * @param message the message to log on security exception.
1155 */
1156 private void enforceCrossUserPermission(int userHandle, String message) {
1157 final int callingUser = UserHandle.getCallingUserId();
1158 if (callingUser != userHandle) {
1159 mContext.enforceCallingOrSelfPermission(
1160 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1161 }
1162 }
1163
Svet Ganov96b9c752016-10-17 19:29:58 -07001164 private static int normalizeSyncable(int syncable) {
1165 if (syncable > 0) {
1166 return SyncStorageEngine.AuthorityInfo.SYNCABLE;
1167 } else if (syncable == 0) {
1168 return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
1169 }
1170 return SyncStorageEngine.AuthorityInfo.UNDEFINED;
1171 }
1172
Alexandra Gherghina0363c3e2014-06-23 13:34:59 +01001173 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 * Hide this class since it is not part of api,
1175 * but current unittest framework requires it to be public
1176 * @hide
1177 */
1178 public static final class ObserverNode {
1179 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -08001180 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001181 public final int uid;
1182 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001183 public final boolean notifyForDescendants;
1184 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -08001185 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001187 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001188 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001189 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001191 uid = _uid;
1192 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -07001193 userHandle = _userHandle;
1194 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 try {
1196 observer.asBinder().linkToDeath(this, 0);
1197 } catch (RemoteException e) {
1198 binderDied();
1199 }
1200 }
1201
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001202 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -08001204 synchronized (observersLock) {
1205 removeObserverLocked(observer);
1206 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001208
1209 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001210 String name, String prefix, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001211 pidCounts.put(pid, pidCounts.get(pid)+1);
1212 pw.print(prefix); pw.print(name); pw.print(": pid=");
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001213 pw.print(pid); pw.print(" uid=");
1214 pw.print(uid); pw.print(" user=");
1215 pw.print(userHandle); pw.print(" target=");
1216 pw.println(Integer.toHexString(System.identityHashCode(
1217 observer != null ? observer.asBinder() : null)));
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 }
1220
1221 public static final int INSERT_TYPE = 0;
1222 public static final int UPDATE_TYPE = 1;
1223 public static final int DELETE_TYPE = 2;
1224
1225 private String mName;
1226 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1227 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1228
1229 public ObserverNode(String name) {
1230 mName = name;
1231 }
1232
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001233 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001234 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -07001235 String innerName = null;
1236 if (mObservers.size() > 0) {
1237 if ("".equals(name)) {
1238 innerName = mName;
1239 } else {
1240 innerName = name + "/" + mName;
1241 }
1242 for (int i=0; i<mObservers.size(); i++) {
1243 counts[1]++;
1244 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1245 pidCounts);
1246 }
1247 }
1248 if (mChildren.size() > 0) {
1249 if (innerName == null) {
1250 if ("".equals(name)) {
1251 innerName = mName;
1252 } else {
1253 innerName = name + "/" + mName;
1254 }
1255 }
1256 for (int i=0; i<mChildren.size(); i++) {
1257 counts[0]++;
1258 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1259 counts, pidCounts);
1260 }
1261 }
1262 }
1263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 private String getUriSegment(Uri uri, int index) {
1265 if (uri != null) {
1266 if (index == 0) {
1267 return uri.getAuthority();
1268 } else {
1269 return uri.getPathSegments().get(index - 1);
1270 }
1271 } else {
1272 return null;
1273 }
1274 }
1275
1276 private int countUriSegments(Uri uri) {
1277 if (uri == null) {
1278 return 0;
1279 }
1280 return uri.getPathSegments().size() + 1;
1281 }
1282
Christopher Tate16aa9732012-09-17 16:23:44 -07001283 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -08001284 public void addObserverLocked(Uri uri, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001285 boolean notifyForDescendants, Object observersLock,
1286 int uid, int pid, int userHandle) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001287 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1288 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 }
1290
Fred Quintana002ffad52010-03-02 11:18:16 -08001291 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Shreyas Basarge8c834c02016-01-07 13:53:16 +00001292 boolean notifyForDescendants, Object observersLock,
1293 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 // If this is the leaf node add the observer
1295 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001296 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1297 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 return;
1299 }
1300
1301 // Look to see if the proper child already exists
1302 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -05001303 if (segment == null) {
1304 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 int N = mChildren.size();
1307 for (int i = 0; i < N; i++) {
1308 ObserverNode node = mChildren.get(i);
1309 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -07001310 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1311 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 return;
1313 }
1314 }
1315
1316 // No child found, create one
1317 ObserverNode node = new ObserverNode(segment);
1318 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -07001319 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1320 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 }
1322
Fred Quintana002ffad52010-03-02 11:18:16 -08001323 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 int size = mChildren.size();
1325 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -08001326 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 if (empty) {
1328 mChildren.remove(i);
1329 i--;
1330 size--;
1331 }
1332 }
1333
1334 IBinder observerBinder = observer.asBinder();
1335 size = mObservers.size();
1336 for (int i = 0; i < size; i++) {
1337 ObserverEntry entry = mObservers.get(i);
1338 if (entry.observer.asBinder() == observerBinder) {
1339 mObservers.remove(i);
1340 // We no longer need to listen for death notifications. Remove it.
1341 observerBinder.unlinkToDeath(entry, 0);
1342 break;
1343 }
1344 }
1345
1346 if (mChildren.size() == 0 && mObservers.size() == 0) {
1347 return true;
1348 }
1349 return false;
1350 }
1351
Fred Quintana002ffad52010-03-02 11:18:16 -08001352 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001353 boolean observerWantsSelfNotifications, int flags,
1354 int targetUserHandle, ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 int N = mObservers.size();
1356 IBinder observerBinder = observer == null ? null : observer.asBinder();
1357 for (int i = 0; i < N; i++) {
1358 ObserverEntry entry = mObservers.get(i);
1359
Christopher Tate16aa9732012-09-17 16:23:44 -07001360 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -08001362 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1363 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 continue;
1365 }
1366
Christopher Tate16aa9732012-09-17 16:23:44 -07001367 // Does this observer match the target user?
1368 if (targetUserHandle == UserHandle.USER_ALL
1369 || entry.userHandle == UserHandle.USER_ALL
1370 || targetUserHandle == entry.userHandle) {
1371 // Make sure the observer is interested in the notification
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001372 if (leaf) {
1373 // If we are at the leaf: we always report, unless the sender has asked
1374 // to skip observers that are notifying for descendants (since they will
1375 // be sending another more specific URI for them).
1376 if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
1377 && entry.notifyForDescendants) {
1378 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1379 + ": skip notify for descendants");
1380 continue;
1381 }
1382 } else {
1383 // If we are not at the leaf: we report if the observer says it wants
1384 // to be notified for all descendants.
1385 if (!entry.notifyForDescendants) {
1386 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1387 + ": not monitor descendants");
1388 continue;
1389 }
Christopher Tate16aa9732012-09-17 16:23:44 -07001390 }
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001391 if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
1392 + " flags=" + Integer.toHexString(flags)
1393 + " desc=" + entry.notifyForDescendants);
Nicolas Prevot051f3b72016-05-18 18:44:00 +01001394 calls.add(new ObserverCall(this, entry.observer, selfChange,
1395 UserHandle.getUserId(entry.uid)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 }
1397 }
1398 }
1399
Christopher Tate16aa9732012-09-17 16:23:44 -07001400 /**
1401 * targetUserHandle is either a hard user handle or is USER_ALL
1402 */
Fred Quintana002ffad52010-03-02 11:18:16 -08001403 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001404 boolean observerWantsSelfNotifications, int flags,
1405 int targetUserHandle, ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 String segment = null;
1407 int segmentCount = countUriSegments(uri);
1408 if (index >= segmentCount) {
1409 // This is the leaf node, notify all observers
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001410 if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
Christopher Tate16aa9732012-09-17 16:23:44 -07001411 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001412 flags, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 } else if (index < segmentCount){
1414 segment = getUriSegment(uri, index);
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001415 if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
1416 + segment);
Christopher Tate16aa9732012-09-17 16:23:44 -07001417 // Notify any observers at this level who are interested in descendants
1418 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001419 flags, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 }
1421
1422 int N = mChildren.size();
1423 for (int i = 0; i < N; i++) {
1424 ObserverNode node = mChildren.get(i);
1425 if (segment == null || node.mName.equals(segment)) {
1426 // We found the child,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001427 node.collectObserversLocked(uri, index + 1, observer,
1428 observerWantsSelfNotifications, flags, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 if (segment != null) {
1430 break;
1431 }
1432 }
1433 }
1434 }
1435 }
1436}