blob: 023bf2b20614040f0d15bfbdfb5ef4935dc4bef9 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.content;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Jeff Sharkey7a96c392012-11-15 14:01:46 -080019import android.Manifest;
Dianne Hackborn7a135592009-05-06 00:28:37 -070020import android.accounts.Account;
Christopher Tate16aa9732012-09-17 16:23:44 -070021import android.app.ActivityManager;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080022import android.content.ContentResolver;
23import android.content.Context;
24import android.content.IContentService;
25import android.content.ISyncStatusObserver;
26import android.content.PeriodicSync;
27import android.content.SyncAdapterType;
28import android.content.SyncInfo;
Matthew Williamsfa774182013-06-18 15:44:11 -070029import android.content.SyncRequest;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080030import android.content.SyncStatusInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.database.IContentObserver;
32import android.database.sqlite.SQLiteException;
33import android.net.Uri;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070034import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.os.Bundle;
36import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070037import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.RemoteException;
39import android.os.ServiceManager;
Dan Morrille4d9a012013-03-28 18:10:43 -070040import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070041import android.os.UserHandle;
Matthew Williams632515b2013-10-10 15:51:00 -070042import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.util.Log;
Matthew Williamsfa774182013-06-18 15:44:11 -070044import android.util.Pair;
Jeff Sharkey60094592013-03-21 18:14:24 -070045import android.util.Slog;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070046import android.util.SparseIntArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48import java.io.FileDescriptor;
49import java.io.PrintWriter;
Christopher Tate16aa9732012-09-17 16:23:44 -070050import java.security.InvalidParameterException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import java.util.ArrayList;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070052import java.util.Collections;
53import java.util.Comparator;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080054import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
56/**
57 * {@hide}
58 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070059public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 private static final String TAG = "ContentService";
61 private Context mContext;
62 private boolean mFactoryTest;
63 private final ObserverNode mRootNode = new ObserverNode("");
64 private SyncManager mSyncManager = null;
65 private final Object mSyncManagerLock = new Object();
66
67 private SyncManager getSyncManager() {
Dan Morrille4d9a012013-03-28 18:10:43 -070068 if (SystemProperties.getBoolean("config.disable_network", false)) {
69 return null;
70 }
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 synchronized(mSyncManagerLock) {
73 try {
74 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
75 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
76 } catch (SQLiteException e) {
77 Log.e(TAG, "Can't create SyncManager", e);
78 }
79 return mSyncManager;
80 }
81 }
82
83 @Override
84 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
85 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
86 "caller doesn't have the DUMP permission");
87
88 // This makes it so that future permission checks will be in the context of this
89 // process rather than the caller's process. We will restore this before returning.
90 long identityToken = clearCallingIdentity();
91 try {
92 if (mSyncManager == null) {
93 pw.println("No SyncManager created! (Disk full?)");
94 } else {
95 mSyncManager.dump(fd, pw);
96 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -070097 pw.println();
98 pw.println("Observer tree:");
99 synchronized (mRootNode) {
100 int[] counts = new int[2];
101 final SparseIntArray pidCounts = new SparseIntArray();
102 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
103 pw.println();
104 ArrayList<Integer> sorted = new ArrayList<Integer>();
105 for (int i=0; i<pidCounts.size(); i++) {
106 sorted.add(pidCounts.keyAt(i));
107 }
108 Collections.sort(sorted, new Comparator<Integer>() {
109 @Override
110 public int compare(Integer lhs, Integer rhs) {
111 int lc = pidCounts.get(lhs);
112 int rc = pidCounts.get(rhs);
113 if (lc < rc) {
114 return 1;
115 } else if (lc > rc) {
116 return -1;
117 }
118 return 0;
119 }
120
121 });
122 for (int i=0; i<sorted.size(); i++) {
123 int pid = sorted.get(i);
124 pw.print(" pid "); pw.print(pid); pw.print(": ");
125 pw.print(pidCounts.get(pid)); pw.println(" observers");
126 }
127 pw.println();
128 pw.print(" Total number of nodes: "); pw.println(counts[0]);
129 pw.print(" Total number of observers: "); pw.println(counts[1]);
130 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 } finally {
132 restoreCallingIdentity(identityToken);
133 }
134 }
135
Dianne Hackborn231cc602009-04-27 17:10:36 -0700136 @Override
137 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
138 throws RemoteException {
139 try {
140 return super.onTransact(code, data, reply, flags);
141 } catch (RuntimeException e) {
142 // The content service only throws security exceptions, so let's
143 // log all others.
144 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700145 Slog.wtf(TAG, "Content Service Crash", e);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700146 }
147 throw e;
148 }
149 }
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 /*package*/ ContentService(Context context, boolean factoryTest) {
152 mContext = context;
153 mFactoryTest = factoryTest;
Kenny Root26ff6622012-07-30 12:58:03 -0700154 }
155
156 public void systemReady() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 getSyncManager();
158 }
159
Christopher Tate16aa9732012-09-17 16:23:44 -0700160 /**
161 * Register a content observer tied to a specific user's view of the provider.
162 * @param userHandle the user whose view of the provider is to be observed. May be
163 * the calling user without requiring any permission, otherwise the caller needs to
164 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
165 * USER_CURRENT are properly handled; all other pseudousers are forbidden.
166 */
167 @Override
168 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
169 IContentObserver observer, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 if (observer == null || uri == null) {
171 throw new IllegalArgumentException("You must pass a valid uri and observer");
172 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700173
174 final int callingUser = UserHandle.getCallingUserId();
175 if (callingUser != userHandle) {
176 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
177 "no permission to observe other users' provider view");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
Christopher Tate16aa9732012-09-17 16:23:44 -0700179
180 if (userHandle < 0) {
181 if (userHandle == UserHandle.USER_CURRENT) {
182 userHandle = ActivityManager.getCurrentUser();
183 } else if (userHandle != UserHandle.USER_ALL) {
184 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
185 + userHandle);
186 }
187 }
188
189 synchronized (mRootNode) {
190 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
191 Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
192 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
193 " with notifyForDescendants " + notifyForDescendants);
194 }
195 }
196
197 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
198 IContentObserver observer) {
199 registerContentObserver(uri, notifyForDescendants, observer,
200 UserHandle.getCallingUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 }
202
203 public void unregisterContentObserver(IContentObserver observer) {
204 if (observer == null) {
205 throw new IllegalArgumentException("You must pass a valid observer");
206 }
207 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800208 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700209 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
211 }
212
Christopher Tate16aa9732012-09-17 16:23:44 -0700213 /**
214 * Notify observers of a particular user's view of the provider.
215 * @param userHandle the user whose view of the provider is to be notified. May be
216 * the calling user without requiring any permission, otherwise the caller needs to
217 * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
218 * USER_CURRENT are properly interpreted; no other pseudousers are allowed.
219 */
220 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 public void notifyChange(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700222 boolean observerWantsSelfNotifications, boolean syncToNetwork,
223 int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700225 Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
226 + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800228
Christopher Tate16aa9732012-09-17 16:23:44 -0700229 // Notify for any user other than the caller's own requires permission.
230 final int callingUserHandle = UserHandle.getCallingUserId();
231 if (userHandle != callingUserHandle) {
232 mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
233 "no permission to notify other users");
234 }
235
236 // We passed the permission check; resolve pseudouser targets as appropriate
237 if (userHandle < 0) {
238 if (userHandle == UserHandle.USER_CURRENT) {
239 userHandle = ActivityManager.getCurrentUser();
240 } else if (userHandle != UserHandle.USER_ALL) {
241 throw new InvalidParameterException("Bad user handle for notifyChange: "
242 + userHandle);
243 }
244 }
245
Alon Albert57286f92012-10-09 14:21:38 -0700246 final int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 // This makes it so that future permission checks will be in the context of this
248 // process rather than the caller's process. We will restore this before returning.
249 long identityToken = clearCallingIdentity();
250 try {
251 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
252 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800253 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
Christopher Tate16aa9732012-09-17 16:23:44 -0700254 userHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 }
256 final int numCalls = calls.size();
257 for (int i=0; i<numCalls; i++) {
258 ObserverCall oc = calls.get(i);
259 try {
Jeff Brown655e66b2012-01-23 15:51:41 -0800260 oc.mObserver.onChange(oc.mSelfChange, uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 if (Log.isLoggable(TAG, Log.VERBOSE)) {
262 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
263 }
264 } catch (RemoteException ex) {
265 synchronized (mRootNode) {
266 Log.w(TAG, "Found dead observer, removing");
267 IBinder binder = oc.mObserver.asBinder();
268 final ArrayList<ObserverNode.ObserverEntry> list
269 = oc.mNode.mObservers;
270 int numList = list.size();
271 for (int j=0; j<numList; j++) {
272 ObserverNode.ObserverEntry oe = list.get(j);
273 if (oe.observer.asBinder() == binder) {
274 list.remove(j);
275 j--;
276 numList--;
277 }
278 }
279 }
280 }
281 }
282 if (syncToNetwork) {
283 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700284 if (syncManager != null) {
Alon Albert57286f92012-10-09 14:21:38 -0700285 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800286 uri.getAuthority());
Fred Quintanaac9385e2009-06-22 18:00:59 -0700287 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
289 } finally {
290 restoreCallingIdentity(identityToken);
291 }
292 }
293
Christopher Tate16aa9732012-09-17 16:23:44 -0700294 public void notifyChange(Uri uri, IContentObserver observer,
295 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
296 notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
297 UserHandle.getCallingUserId());
298 }
299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 /**
301 * Hide this class since it is not part of api,
302 * but current unittest framework requires it to be public
303 * @hide
304 *
305 */
306 public static final class ObserverCall {
307 final ObserverNode mNode;
308 final IContentObserver mObserver;
Jeff Brown86de0592012-01-23 13:01:18 -0800309 final boolean mSelfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310
Jeff Brown86de0592012-01-23 13:01:18 -0800311 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 mNode = node;
313 mObserver = observer;
Jeff Brown86de0592012-01-23 13:01:18 -0800314 mSelfChange = selfChange;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 }
316 }
317
Matthew Williamsfa774182013-06-18 15:44:11 -0700318 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700319 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 ContentResolver.validateSyncExtrasBundle(extras);
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700321 int userId = UserHandle.getCallingUserId();
Alon Albert57286f92012-10-09 14:21:38 -0700322 int uId = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 // This makes it so that future permission checks will be in the context of this
325 // process rather than the caller's process. We will restore this before returning.
326 long identityToken = clearCallingIdentity();
327 try {
328 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700329 if (syncManager != null) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700330 syncManager.scheduleSync(account, userId, uId, authority, extras,
331 0 /* no delay */, 0 /* no delay */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700332 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700333 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 } finally {
335 restoreCallingIdentity(identityToken);
336 }
337 }
338
339 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700340 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
341 * either:
342 * periodic OR one-off sync.
343 * and
344 * anonymous OR provider sync.
345 * Depending on the request, we enqueue to suit in the SyncManager.
Matthew Williams632515b2013-10-10 15:51:00 -0700346 * @param request The request object. Validation of this object is done by its builder.
Matthew Williamsfa774182013-06-18 15:44:11 -0700347 */
348 @Override
349 public void sync(SyncRequest request) {
350 Bundle extras = request.getBundle();
Matthew Williamsfa774182013-06-18 15:44:11 -0700351 long flextime = request.getSyncFlexTime();
352 long runAtTime = request.getSyncRunTime();
353 int userId = UserHandle.getCallingUserId();
354 int uId = Binder.getCallingUid();
355
356 // This makes it so that future permission checks will be in the context of this
357 // process rather than the caller's process. We will restore this before returning.
358 long identityToken = clearCallingIdentity();
359 try {
360 SyncManager syncManager = getSyncManager();
361 if (syncManager != null) {
362 if (request.hasAuthority()) {
363 // Sync Adapter registered with the system - old API.
Matthew Williams62222882013-08-20 15:32:20 -0700364 final Account account = request.getAccount();
365 final String provider = request.getProvider();
Matthew Williamsfa774182013-06-18 15:44:11 -0700366 if (request.isPeriodic()) {
367 mContext.enforceCallingOrSelfPermission(
368 Manifest.permission.WRITE_SYNC_SETTINGS,
369 "no permission to write the sync settings");
370 if (runAtTime < 60) {
371 Slog.w(TAG, "Requested poll frequency of " + runAtTime
372 + " seconds being rounded up to 60 seconds.");
373 runAtTime = 60;
374 }
375 PeriodicSync syncToAdd =
376 new PeriodicSync(account, provider, extras, runAtTime, flextime);
377 getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId);
378 } else {
379 long beforeRuntimeMillis = (flextime) * 1000;
380 long runtimeMillis = runAtTime * 1000;
381 syncManager.scheduleSync(
382 account, userId, uId, provider, extras,
383 beforeRuntimeMillis, runtimeMillis,
384 false /* onlyThoseWithUnknownSyncableState */);
385 }
386 } else {
Matthew Williams62222882013-08-20 15:32:20 -0700387 Log.w(TAG, "Unrecognised sync parameters, doing nothing.");
Matthew Williamsfa774182013-06-18 15:44:11 -0700388 }
389 }
390 } finally {
391 restoreCallingIdentity(identityToken);
392 }
393 }
394
395 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700397 * if they match the authority and account, if they are present.
398 * @param account filter the pending and active syncs to cancel using this account
399 * @param authority filter the pending and active syncs to cancel using this authority
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700401 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700402 public void cancelSync(Account account, String authority) {
Matthew Williams632515b2013-10-10 15:51:00 -0700403 if (authority != null && authority.length() == 0) {
404 throw new IllegalArgumentException("Authority must be non-empty");
405 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800406
Matthew Williams632515b2013-10-10 15:51:00 -0700407 int userId = UserHandle.getCallingUserId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 // This makes it so that future permission checks will be in the context of this
409 // process rather than the caller's process. We will restore this before returning.
410 long identityToken = clearCallingIdentity();
411 try {
412 SyncManager syncManager = getSyncManager();
413 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800414 syncManager.clearScheduledSyncOperations(account, userId, authority);
415 syncManager.cancelActiveSync(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417 } finally {
418 restoreCallingIdentity(identityToken);
419 }
420 }
421
Fred Quintanaac9385e2009-06-22 18:00:59 -0700422 /**
423 * Get information about the SyncAdapters that are known to the system.
424 * @return an array of SyncAdapters that have registered with the system
425 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700426 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700427 public SyncAdapterType[] getSyncAdapterTypes() {
428 // This makes it so that future permission checks will be in the context of this
429 // process rather than the caller's process. We will restore this before returning.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700430 final int userId = UserHandle.getCallingUserId();
431 final long identityToken = clearCallingIdentity();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700432 try {
433 SyncManager syncManager = getSyncManager();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700434 return syncManager.getSyncAdapterTypes(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700435 } finally {
436 restoreCallingIdentity(identityToken);
437 }
438 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700439
Matthew Williamsfa774182013-06-18 15:44:11 -0700440 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700441 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700442 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
443 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800444
Matthew Williams632515b2013-10-10 15:51:00 -0700445 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700446 long identityToken = clearCallingIdentity();
447 try {
448 SyncManager syncManager = getSyncManager();
449 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700450 return syncManager.getSyncStorageEngine().getSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800451 account, userId, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700452 }
453 } finally {
454 restoreCallingIdentity(identityToken);
455 }
456 return false;
457 }
458
Matthew Williamsfa774182013-06-18 15:44:11 -0700459 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700460 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Matthew Williams632515b2013-10-10 15:51:00 -0700461 if (TextUtils.isEmpty(providerName)) {
462 throw new IllegalArgumentException("Authority must be non-empty");
463 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700464 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
465 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800466
Matthew Williams632515b2013-10-10 15:51:00 -0700467 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700468 long identityToken = clearCallingIdentity();
469 try {
470 SyncManager syncManager = getSyncManager();
471 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700472 syncManager.getSyncStorageEngine().setSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800473 account, userId, providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700474 }
475 } finally {
476 restoreCallingIdentity(identityToken);
477 }
478 }
479
Matthew Williams632515b2013-10-10 15:51:00 -0700480 /** Old API. Schedule periodic sync with default flex time. */
Matthew Williamsfa774182013-06-18 15:44:11 -0700481 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800482 public void addPeriodicSync(Account account, String authority, Bundle extras,
483 long pollFrequency) {
Matthew Williams632515b2013-10-10 15:51:00 -0700484 if (account == null) {
485 throw new IllegalArgumentException("Account must not be null");
486 }
487 if (TextUtils.isEmpty(authority)) {
488 throw new IllegalArgumentException("Authority must not be empty.");
489 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800490 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
491 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800492
Matthew Williams632515b2013-10-10 15:51:00 -0700493 int userId = UserHandle.getCallingUserId();
Jeff Sharkey51366be2013-03-27 14:46:55 -0700494 if (pollFrequency < 60) {
Jeff Sharkey60094592013-03-21 18:14:24 -0700495 Slog.w(TAG, "Requested poll frequency of " + pollFrequency
Jeff Sharkey51366be2013-03-27 14:46:55 -0700496 + " seconds being rounded up to 60 seconds.");
497 pollFrequency = 60;
Jeff Sharkey60094592013-03-21 18:14:24 -0700498 }
499
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800500 long identityToken = clearCallingIdentity();
501 try {
Matthew Williamsfa774182013-06-18 15:44:11 -0700502 // Add default flex time to this sync.
503 PeriodicSync syncToAdd =
504 new PeriodicSync(account, authority, extras,
505 pollFrequency,
506 SyncStorageEngine.calculateDefaultFlexTime(pollFrequency));
507 getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800508 } finally {
509 restoreCallingIdentity(identityToken);
510 }
511 }
512
Matthew Williamsfa774182013-06-18 15:44:11 -0700513 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800514 public void removePeriodicSync(Account account, String authority, Bundle extras) {
Matthew Williams632515b2013-10-10 15:51:00 -0700515 if (account == null) {
516 throw new IllegalArgumentException("Account must not be null");
517 }
518 if (TextUtils.isEmpty(authority)) {
519 throw new IllegalArgumentException("Authority must not be empty");
520 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800521 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
522 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800523
Matthew Williams632515b2013-10-10 15:51:00 -0700524 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800525 long identityToken = clearCallingIdentity();
526 try {
Matthew Williamsfa774182013-06-18 15:44:11 -0700527 PeriodicSync syncToRemove = new PeriodicSync(account, authority, extras,
528 0 /* Not read for removal */, 0 /* Not read for removal */);
529 getSyncManager().getSyncStorageEngine().removePeriodicSync(syncToRemove, userId);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800530 } finally {
531 restoreCallingIdentity(identityToken);
532 }
533 }
534
Matthew Williamsfa774182013-06-18 15:44:11 -0700535 /**
536 * TODO: Implement.
537 * @param request Sync to remove.
538 */
539 public void removeSync(SyncRequest request) {
540
541 }
542
543 @Override
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800544 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
Matthew Williams632515b2013-10-10 15:51:00 -0700545 if (account == null) {
546 throw new IllegalArgumentException("Account must not be null");
547 }
548 if (TextUtils.isEmpty(providerName)) {
549 throw new IllegalArgumentException("Authority must not be empty");
550 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800551 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
552 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800553
Matthew Williams632515b2013-10-10 15:51:00 -0700554 int userId = UserHandle.getCallingUserId();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800555 long identityToken = clearCallingIdentity();
556 try {
557 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800558 account, userId, providerName);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800559 } finally {
560 restoreCallingIdentity(identityToken);
561 }
562 }
563
Fred Quintana5e787c42009-08-16 23:13:53 -0700564 public int getIsSyncable(Account account, String providerName) {
565 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
566 "no permission to read the sync settings");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700567 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800568
Fred Quintana5e787c42009-08-16 23:13:53 -0700569 long identityToken = clearCallingIdentity();
570 try {
571 SyncManager syncManager = getSyncManager();
572 if (syncManager != null) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700573 return syncManager.getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800574 account, userId, providerName);
Fred Quintana5e787c42009-08-16 23:13:53 -0700575 }
576 } finally {
577 restoreCallingIdentity(identityToken);
578 }
579 return -1;
580 }
581
Matthew Williamsfa774182013-06-18 15:44:11 -0700582 @Override
Fred Quintana5e787c42009-08-16 23:13:53 -0700583 public void setIsSyncable(Account account, String providerName, int syncable) {
Matthew Williams632515b2013-10-10 15:51:00 -0700584 if (TextUtils.isEmpty(providerName)) {
585 throw new IllegalArgumentException("Authority must not be empty");
586 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700587 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
588 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800589
Matthew Williams632515b2013-10-10 15:51:00 -0700590 int userId = UserHandle.getCallingUserId();
Fred Quintana5e787c42009-08-16 23:13:53 -0700591 long identityToken = clearCallingIdentity();
592 try {
593 SyncManager syncManager = getSyncManager();
594 if (syncManager != null) {
595 syncManager.getSyncStorageEngine().setIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800596 account, userId, providerName, syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700597 }
598 } finally {
599 restoreCallingIdentity(identityToken);
600 }
601 }
602
Matthew Williamsfa774182013-06-18 15:44:11 -0700603 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700604 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700605 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
606 "no permission to read the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800607
Matthew Williams632515b2013-10-10 15:51:00 -0700608 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700609 long identityToken = clearCallingIdentity();
610 try {
611 SyncManager syncManager = getSyncManager();
612 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800613 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700614 }
615 } finally {
616 restoreCallingIdentity(identityToken);
617 }
618 return false;
619 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700620
Matthew Williamsfa774182013-06-18 15:44:11 -0700621 @Override
Fred Quintanaac9385e2009-06-22 18:00:59 -0700622 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700623 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
624 "no permission to write the sync settings");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800625
Matthew Williams632515b2013-10-10 15:51:00 -0700626 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700627 long identityToken = clearCallingIdentity();
628 try {
629 SyncManager syncManager = getSyncManager();
630 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800631 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700632 }
633 } finally {
634 restoreCallingIdentity(identityToken);
635 }
636 }
637
Dianne Hackborn7a135592009-05-06 00:28:37 -0700638 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700639 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
640 "no permission to read the sync stats");
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700641 int userId = UserHandle.getCallingUserId();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800642
Dianne Hackborn231cc602009-04-27 17:10:36 -0700643 long identityToken = clearCallingIdentity();
644 try {
645 SyncManager syncManager = getSyncManager();
646 if (syncManager != null) {
647 return syncManager.getSyncStorageEngine().isSyncActive(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800648 account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700649 }
650 } finally {
651 restoreCallingIdentity(identityToken);
652 }
653 return false;
654 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700655
Fred Quintanac6a69552010-09-27 17:05:04 -0700656 public List<SyncInfo> getCurrentSyncs() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700657 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
658 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800659
Matthew Williams632515b2013-10-10 15:51:00 -0700660 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700661 long identityToken = clearCallingIdentity();
662 try {
Matthew Williamsa7456e42013-11-12 14:41:02 -0800663 return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700664 } finally {
665 restoreCallingIdentity(identityToken);
666 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700667 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700668
Fred Quintanaac9385e2009-06-22 18:00:59 -0700669 public SyncStatusInfo getSyncStatus(Account account, String authority) {
Matthew Williams632515b2013-10-10 15:51:00 -0700670 if (TextUtils.isEmpty(authority)) {
671 throw new IllegalArgumentException("Authority must not be empty");
672 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700673 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
674 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800675
Matthew Williams632515b2013-10-10 15:51:00 -0700676 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700677 long identityToken = clearCallingIdentity();
678 try {
679 SyncManager syncManager = getSyncManager();
680 if (syncManager != null) {
Costin Manolacheb7520982009-09-02 18:03:05 -0700681 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800682 account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700683 }
684 } finally {
685 restoreCallingIdentity(identityToken);
686 }
687 return null;
688 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700689
Fred Quintanaac9385e2009-06-22 18:00:59 -0700690 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700691 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
692 "no permission to read the sync stats");
Amith Yamasani04e0d262012-02-14 11:50:53 -0800693
Matthew Williams632515b2013-10-10 15:51:00 -0700694 int userId = UserHandle.getCallingUserId();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700695 long identityToken = clearCallingIdentity();
696 try {
697 SyncManager syncManager = getSyncManager();
698 if (syncManager != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800699 return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700700 }
701 } finally {
702 restoreCallingIdentity(identityToken);
703 }
704 return false;
705 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700706
Dianne Hackborn231cc602009-04-27 17:10:36 -0700707 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
708 long identityToken = clearCallingIdentity();
709 try {
710 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800711 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700712 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700713 }
714 } finally {
715 restoreCallingIdentity(identityToken);
716 }
717 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700718
Dianne Hackborn231cc602009-04-27 17:10:36 -0700719 public void removeStatusChangeListener(ISyncStatusObserver callback) {
720 long identityToken = clearCallingIdentity();
721 try {
722 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800723 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700724 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700725 }
726 } finally {
727 restoreCallingIdentity(identityToken);
728 }
729 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700730
Kenny Root26ff6622012-07-30 12:58:03 -0700731 public static ContentService main(Context context, boolean factoryTest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700733 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 return service;
735 }
736
737 /**
738 * Hide this class since it is not part of api,
739 * but current unittest framework requires it to be public
740 * @hide
741 */
742 public static final class ObserverNode {
743 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800744 public final IContentObserver observer;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700745 public final int uid;
746 public final int pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700747 public final boolean notifyForDescendants;
748 private final int userHandle;
Fred Quintana002ffad52010-03-02 11:18:16 -0800749 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700751 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
Christopher Tate16aa9732012-09-17 16:23:44 -0700752 int _uid, int _pid, int _userHandle) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800753 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 observer = o;
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700755 uid = _uid;
756 pid = _pid;
Christopher Tate16aa9732012-09-17 16:23:44 -0700757 userHandle = _userHandle;
758 notifyForDescendants = n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 try {
760 observer.asBinder().linkToDeath(this, 0);
761 } catch (RemoteException e) {
762 binderDied();
763 }
764 }
765
766 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800767 synchronized (observersLock) {
768 removeObserverLocked(observer);
769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 }
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700771
772 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
773 String name, String prefix, SparseIntArray pidCounts) {
774 pidCounts.put(pid, pidCounts.get(pid)+1);
775 pw.print(prefix); pw.print(name); pw.print(": pid=");
776 pw.print(pid); pw.print(" uid=");
Christopher Tate16aa9732012-09-17 16:23:44 -0700777 pw.print(uid); pw.print(" user=");
778 pw.print(userHandle); pw.print(" target=");
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700779 pw.println(Integer.toHexString(System.identityHashCode(
780 observer != null ? observer.asBinder() : null)));
781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
783
784 public static final int INSERT_TYPE = 0;
785 public static final int UPDATE_TYPE = 1;
786 public static final int DELETE_TYPE = 2;
787
788 private String mName;
789 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
790 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
791
792 public ObserverNode(String name) {
793 mName = name;
794 }
795
Dianne Hackborn1b64e0d2011-07-17 15:23:59 -0700796 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
797 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
798 String innerName = null;
799 if (mObservers.size() > 0) {
800 if ("".equals(name)) {
801 innerName = mName;
802 } else {
803 innerName = name + "/" + mName;
804 }
805 for (int i=0; i<mObservers.size(); i++) {
806 counts[1]++;
807 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
808 pidCounts);
809 }
810 }
811 if (mChildren.size() > 0) {
812 if (innerName == null) {
813 if ("".equals(name)) {
814 innerName = mName;
815 } else {
816 innerName = name + "/" + mName;
817 }
818 }
819 for (int i=0; i<mChildren.size(); i++) {
820 counts[0]++;
821 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
822 counts, pidCounts);
823 }
824 }
825 }
826
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 private String getUriSegment(Uri uri, int index) {
828 if (uri != null) {
829 if (index == 0) {
830 return uri.getAuthority();
831 } else {
832 return uri.getPathSegments().get(index - 1);
833 }
834 } else {
835 return null;
836 }
837 }
838
839 private int countUriSegments(Uri uri) {
840 if (uri == null) {
841 return 0;
842 }
843 return uri.getPathSegments().size() + 1;
844 }
845
Christopher Tate16aa9732012-09-17 16:23:44 -0700846 // Invariant: userHandle is either a hard user number or is USER_ALL
Fred Quintana002ffad52010-03-02 11:18:16 -0800847 public void addObserverLocked(Uri uri, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700848 boolean notifyForDescendants, Object observersLock,
849 int uid, int pid, int userHandle) {
850 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
851 uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
853
Fred Quintana002ffad52010-03-02 11:18:16 -0800854 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700855 boolean notifyForDescendants, Object observersLock,
856 int uid, int pid, int userHandle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 // If this is the leaf node add the observer
858 if (index == countUriSegments(uri)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700859 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
860 uid, pid, userHandle));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 return;
862 }
863
864 // Look to see if the proper child already exists
865 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -0500866 if (segment == null) {
867 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
868 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 int N = mChildren.size();
870 for (int i = 0; i < N; i++) {
871 ObserverNode node = mChildren.get(i);
872 if (node.mName.equals(segment)) {
Christopher Tate16aa9732012-09-17 16:23:44 -0700873 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
874 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 return;
876 }
877 }
878
879 // No child found, create one
880 ObserverNode node = new ObserverNode(segment);
881 mChildren.add(node);
Christopher Tate16aa9732012-09-17 16:23:44 -0700882 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
883 observersLock, uid, pid, userHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 }
885
Fred Quintana002ffad52010-03-02 11:18:16 -0800886 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 int size = mChildren.size();
888 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800889 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 if (empty) {
891 mChildren.remove(i);
892 i--;
893 size--;
894 }
895 }
896
897 IBinder observerBinder = observer.asBinder();
898 size = mObservers.size();
899 for (int i = 0; i < size; i++) {
900 ObserverEntry entry = mObservers.get(i);
901 if (entry.observer.asBinder() == observerBinder) {
902 mObservers.remove(i);
903 // We no longer need to listen for death notifications. Remove it.
904 observerBinder.unlinkToDeath(entry, 0);
905 break;
906 }
907 }
908
909 if (mChildren.size() == 0 && mObservers.size() == 0) {
910 return true;
911 }
912 return false;
913 }
914
Fred Quintana002ffad52010-03-02 11:18:16 -0800915 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700916 boolean observerWantsSelfNotifications, int targetUserHandle,
917 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 int N = mObservers.size();
919 IBinder observerBinder = observer == null ? null : observer.asBinder();
920 for (int i = 0; i < N; i++) {
921 ObserverEntry entry = mObservers.get(i);
922
Christopher Tate16aa9732012-09-17 16:23:44 -0700923 // Don't notify the observer if it sent the notification and isn't interested
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 // in self notifications
Jeff Brown86de0592012-01-23 13:01:18 -0800925 boolean selfChange = (entry.observer.asBinder() == observerBinder);
926 if (selfChange && !observerWantsSelfNotifications) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 continue;
928 }
929
Christopher Tate16aa9732012-09-17 16:23:44 -0700930 // Does this observer match the target user?
931 if (targetUserHandle == UserHandle.USER_ALL
932 || entry.userHandle == UserHandle.USER_ALL
933 || targetUserHandle == entry.userHandle) {
934 // Make sure the observer is interested in the notification
935 if (leaf || (!leaf && entry.notifyForDescendants)) {
936 calls.add(new ObserverCall(this, entry.observer, selfChange));
937 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 }
939 }
940 }
941
Christopher Tate16aa9732012-09-17 16:23:44 -0700942 /**
943 * targetUserHandle is either a hard user handle or is USER_ALL
944 */
Fred Quintana002ffad52010-03-02 11:18:16 -0800945 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
Christopher Tate16aa9732012-09-17 16:23:44 -0700946 boolean observerWantsSelfNotifications, int targetUserHandle,
947 ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 String segment = null;
949 int segmentCount = countUriSegments(uri);
950 if (index >= segmentCount) {
951 // This is the leaf node, notify all observers
Christopher Tate16aa9732012-09-17 16:23:44 -0700952 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
953 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 } else if (index < segmentCount){
955 segment = getUriSegment(uri, index);
Christopher Tate16aa9732012-09-17 16:23:44 -0700956 // Notify any observers at this level who are interested in descendants
957 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
958 targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 }
960
961 int N = mChildren.size();
962 for (int i = 0; i < N; i++) {
963 ObserverNode node = mChildren.get(i);
964 if (segment == null || node.mName.equals(segment)) {
965 // We found the child,
Jeff Brown86de0592012-01-23 13:01:18 -0800966 node.collectObserversLocked(uri, index + 1,
Christopher Tate16aa9732012-09-17 16:23:44 -0700967 observer, observerWantsSelfNotifications, targetUserHandle, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 if (segment != null) {
969 break;
970 }
971 }
972 }
973 }
974 }
975}