blob: afe8483c21b96af3633c8bcf12c77c62f98f33c9 [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
17package android.content;
18
Dianne Hackborn7a135592009-05-06 00:28:37 -070019import android.accounts.Account;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.database.IContentObserver;
21import android.database.sqlite.SQLiteException;
22import android.net.Uri;
23import android.os.Bundle;
24import android.os.IBinder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070025import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.util.Config;
29import android.util.Log;
30import android.Manifest;
31
32import java.io.FileDescriptor;
33import java.io.PrintWriter;
34import java.util.ArrayList;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080035import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
37/**
38 * {@hide}
39 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070040public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 private static final String TAG = "ContentService";
42 private Context mContext;
43 private boolean mFactoryTest;
44 private final ObserverNode mRootNode = new ObserverNode("");
45 private SyncManager mSyncManager = null;
46 private final Object mSyncManagerLock = new Object();
47
48 private SyncManager getSyncManager() {
49 synchronized(mSyncManagerLock) {
50 try {
51 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
52 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
53 } catch (SQLiteException e) {
54 Log.e(TAG, "Can't create SyncManager", e);
55 }
56 return mSyncManager;
57 }
58 }
59
60 @Override
61 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
62 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
63 "caller doesn't have the DUMP permission");
64
65 // This makes it so that future permission checks will be in the context of this
66 // process rather than the caller's process. We will restore this before returning.
67 long identityToken = clearCallingIdentity();
68 try {
69 if (mSyncManager == null) {
70 pw.println("No SyncManager created! (Disk full?)");
71 } else {
72 mSyncManager.dump(fd, pw);
73 }
74 } finally {
75 restoreCallingIdentity(identityToken);
76 }
77 }
78
Dianne Hackborn231cc602009-04-27 17:10:36 -070079 @Override
80 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
81 throws RemoteException {
82 try {
83 return super.onTransact(code, data, reply, flags);
84 } catch (RuntimeException e) {
85 // The content service only throws security exceptions, so let's
86 // log all others.
87 if (!(e instanceof SecurityException)) {
88 Log.e(TAG, "Content Service Crash", e);
89 }
90 throw e;
91 }
92 }
93
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 /*package*/ ContentService(Context context, boolean factoryTest) {
95 mContext = context;
96 mFactoryTest = factoryTest;
97 getSyncManager();
98 }
99
100 public void registerContentObserver(Uri uri, boolean notifyForDescendents,
101 IContentObserver observer) {
102 if (observer == null || uri == null) {
103 throw new IllegalArgumentException("You must pass a valid uri and observer");
104 }
105 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800106 mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 if (Config.LOGV) Log.v(TAG, "Registered observer " + observer + " at " + uri +
108 " with notifyForDescendents " + notifyForDescendents);
109 }
110 }
111
112 public void unregisterContentObserver(IContentObserver observer) {
113 if (observer == null) {
114 throw new IllegalArgumentException("You must pass a valid observer");
115 }
116 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800117 mRootNode.removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer);
119 }
120 }
121
122 public void notifyChange(Uri uri, IContentObserver observer,
123 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
124 if (Log.isLoggable(TAG, Log.VERBOSE)) {
125 Log.v(TAG, "Notifying update of " + uri + " from observer " + observer
126 + ", syncToNetwork " + syncToNetwork);
127 }
128 // This makes it so that future permission checks will be in the context of this
129 // process rather than the caller's process. We will restore this before returning.
130 long identityToken = clearCallingIdentity();
131 try {
132 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
133 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800134 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 calls);
136 }
137 final int numCalls = calls.size();
138 for (int i=0; i<numCalls; i++) {
139 ObserverCall oc = calls.get(i);
140 try {
141 oc.mObserver.onChange(oc.mSelfNotify);
142 if (Log.isLoggable(TAG, Log.VERBOSE)) {
143 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
144 }
145 } catch (RemoteException ex) {
146 synchronized (mRootNode) {
147 Log.w(TAG, "Found dead observer, removing");
148 IBinder binder = oc.mObserver.asBinder();
149 final ArrayList<ObserverNode.ObserverEntry> list
150 = oc.mNode.mObservers;
151 int numList = list.size();
152 for (int j=0; j<numList; j++) {
153 ObserverNode.ObserverEntry oe = list.get(j);
154 if (oe.observer.asBinder() == binder) {
155 list.remove(j);
156 j--;
157 numList--;
158 }
159 }
160 }
161 }
162 }
163 if (syncToNetwork) {
164 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700165 if (syncManager != null) {
166 syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority());
167 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
169 } finally {
170 restoreCallingIdentity(identityToken);
171 }
172 }
173
174 /**
175 * Hide this class since it is not part of api,
176 * but current unittest framework requires it to be public
177 * @hide
178 *
179 */
180 public static final class ObserverCall {
181 final ObserverNode mNode;
182 final IContentObserver mObserver;
183 final boolean mSelfNotify;
184
185 ObserverCall(ObserverNode node, IContentObserver observer,
186 boolean selfNotify) {
187 mNode = node;
188 mObserver = observer;
189 mSelfNotify = selfNotify;
190 }
191 }
192
Fred Quintanaac9385e2009-06-22 18:00:59 -0700193 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 ContentResolver.validateSyncExtrasBundle(extras);
195 // This makes it so that future permission checks will be in the context of this
196 // process rather than the caller's process. We will restore this before returning.
197 long identityToken = clearCallingIdentity();
198 try {
199 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700200 if (syncManager != null) {
Fred Quintana4a6679b2009-08-17 13:05:39 -0700201 syncManager.scheduleSync(account, authority, extras, 0 /* no delay */,
202 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700203 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 } finally {
205 restoreCallingIdentity(identityToken);
206 }
207 }
208
209 /**
210 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700211 * if they match the authority and account, if they are present.
212 * @param account filter the pending and active syncs to cancel using this account
213 * @param authority filter the pending and active syncs to cancel using this authority
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700215 public void cancelSync(Account account, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 // This makes it so that future permission checks will be in the context of this
217 // process rather than the caller's process. We will restore this before returning.
218 long identityToken = clearCallingIdentity();
219 try {
220 SyncManager syncManager = getSyncManager();
221 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700222 syncManager.clearScheduledSyncOperations(account, authority);
223 syncManager.cancelActiveSync(account, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225 } finally {
226 restoreCallingIdentity(identityToken);
227 }
228 }
229
Fred Quintanaac9385e2009-06-22 18:00:59 -0700230 /**
231 * Get information about the SyncAdapters that are known to the system.
232 * @return an array of SyncAdapters that have registered with the system
233 */
234 public SyncAdapterType[] getSyncAdapterTypes() {
235 // This makes it so that future permission checks will be in the context of this
236 // process rather than the caller's process. We will restore this before returning.
237 long identityToken = clearCallingIdentity();
238 try {
239 SyncManager syncManager = getSyncManager();
240 return syncManager.getSyncAdapterTypes();
241 } finally {
242 restoreCallingIdentity(identityToken);
243 }
244 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700245
Fred Quintanaac9385e2009-06-22 18:00:59 -0700246 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700247 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
248 "no permission to read the sync settings");
249 long identityToken = clearCallingIdentity();
250 try {
251 SyncManager syncManager = getSyncManager();
252 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700253 return syncManager.getSyncStorageEngine().getSyncAutomatically(
254 account, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700255 }
256 } finally {
257 restoreCallingIdentity(identityToken);
258 }
259 return false;
260 }
261
Fred Quintanaac9385e2009-06-22 18:00:59 -0700262 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700263 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
264 "no permission to write the sync settings");
265 long identityToken = clearCallingIdentity();
266 try {
267 SyncManager syncManager = getSyncManager();
268 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700269 syncManager.getSyncStorageEngine().setSyncAutomatically(
270 account, providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700271 }
272 } finally {
273 restoreCallingIdentity(identityToken);
274 }
275 }
276
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800277 public void addPeriodicSync(Account account, String authority, Bundle extras,
278 long pollFrequency) {
279 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
280 "no permission to write the sync settings");
281 long identityToken = clearCallingIdentity();
282 try {
283 getSyncManager().getSyncStorageEngine().addPeriodicSync(
284 account, authority, extras, pollFrequency);
285 } finally {
286 restoreCallingIdentity(identityToken);
287 }
288 }
289
290 public void removePeriodicSync(Account account, String authority, Bundle extras) {
291 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
292 "no permission to write the sync settings");
293 long identityToken = clearCallingIdentity();
294 try {
295 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras);
296 } finally {
297 restoreCallingIdentity(identityToken);
298 }
299 }
300
301 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
302 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
303 "no permission to read the sync settings");
304 long identityToken = clearCallingIdentity();
305 try {
306 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
307 account, providerName);
308 } finally {
309 restoreCallingIdentity(identityToken);
310 }
311 }
312
Fred Quintana5e787c42009-08-16 23:13:53 -0700313 public int getIsSyncable(Account account, String providerName) {
314 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
315 "no permission to read the sync settings");
316 long identityToken = clearCallingIdentity();
317 try {
318 SyncManager syncManager = getSyncManager();
319 if (syncManager != null) {
320 return syncManager.getSyncStorageEngine().getIsSyncable(
321 account, providerName);
322 }
323 } finally {
324 restoreCallingIdentity(identityToken);
325 }
326 return -1;
327 }
328
329 public void setIsSyncable(Account account, String providerName, int syncable) {
330 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
331 "no permission to write the sync settings");
332 long identityToken = clearCallingIdentity();
333 try {
334 SyncManager syncManager = getSyncManager();
335 if (syncManager != null) {
336 syncManager.getSyncStorageEngine().setIsSyncable(
337 account, providerName, syncable);
338 }
339 } finally {
340 restoreCallingIdentity(identityToken);
341 }
342 }
343
Fred Quintanaac9385e2009-06-22 18:00:59 -0700344 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700345 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
346 "no permission to read the sync settings");
347 long identityToken = clearCallingIdentity();
348 try {
349 SyncManager syncManager = getSyncManager();
350 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700351 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700352 }
353 } finally {
354 restoreCallingIdentity(identityToken);
355 }
356 return false;
357 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700358
Fred Quintanaac9385e2009-06-22 18:00:59 -0700359 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700360 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
361 "no permission to write the sync settings");
362 long identityToken = clearCallingIdentity();
363 try {
364 SyncManager syncManager = getSyncManager();
365 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700366 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700367 }
368 } finally {
369 restoreCallingIdentity(identityToken);
370 }
371 }
372
Dianne Hackborn7a135592009-05-06 00:28:37 -0700373 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700374 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
375 "no permission to read the sync stats");
376 long identityToken = clearCallingIdentity();
377 try {
378 SyncManager syncManager = getSyncManager();
379 if (syncManager != null) {
380 return syncManager.getSyncStorageEngine().isSyncActive(
381 account, authority);
382 }
383 } finally {
384 restoreCallingIdentity(identityToken);
385 }
386 return false;
387 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700388
Fred Quintanac6a69552010-09-27 17:05:04 -0700389 public List<SyncInfo> getCurrentSyncs() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700390 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
391 "no permission to read the sync stats");
392 long identityToken = clearCallingIdentity();
393 try {
Fred Quintanac6a69552010-09-27 17:05:04 -0700394 return getSyncManager().getSyncStorageEngine().getCurrentSyncs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700395 } finally {
396 restoreCallingIdentity(identityToken);
397 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700398 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700399
Fred Quintanaac9385e2009-06-22 18:00:59 -0700400 public SyncStatusInfo getSyncStatus(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700401 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
402 "no permission to read the sync stats");
403 long identityToken = clearCallingIdentity();
404 try {
405 SyncManager syncManager = getSyncManager();
406 if (syncManager != null) {
Costin Manolacheb7520982009-09-02 18:03:05 -0700407 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
408 account, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700409 }
410 } finally {
411 restoreCallingIdentity(identityToken);
412 }
413 return null;
414 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700415
Fred Quintanaac9385e2009-06-22 18:00:59 -0700416 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700417 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
418 "no permission to read the sync stats");
419 long identityToken = clearCallingIdentity();
420 try {
421 SyncManager syncManager = getSyncManager();
422 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700423 return syncManager.getSyncStorageEngine().isSyncPending(account, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700424 }
425 } finally {
426 restoreCallingIdentity(identityToken);
427 }
428 return false;
429 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700430
Dianne Hackborn231cc602009-04-27 17:10:36 -0700431 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
432 long identityToken = clearCallingIdentity();
433 try {
434 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800435 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700436 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700437 }
438 } finally {
439 restoreCallingIdentity(identityToken);
440 }
441 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700442
Dianne Hackborn231cc602009-04-27 17:10:36 -0700443 public void removeStatusChangeListener(ISyncStatusObserver callback) {
444 long identityToken = clearCallingIdentity();
445 try {
446 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800447 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700448 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700449 }
450 } finally {
451 restoreCallingIdentity(identityToken);
452 }
453 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 public static IContentService main(Context context, boolean factoryTest) {
456 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700457 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 return service;
459 }
460
461 /**
462 * Hide this class since it is not part of api,
463 * but current unittest framework requires it to be public
464 * @hide
465 */
466 public static final class ObserverNode {
467 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800468 public final IContentObserver observer;
469 public final boolean notifyForDescendents;
470 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471
Fred Quintana002ffad52010-03-02 11:18:16 -0800472 public ObserverEntry(IContentObserver o, boolean n, Object observersLock) {
473 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 observer = o;
475 notifyForDescendents = n;
476 try {
477 observer.asBinder().linkToDeath(this, 0);
478 } catch (RemoteException e) {
479 binderDied();
480 }
481 }
482
483 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800484 synchronized (observersLock) {
485 removeObserverLocked(observer);
486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 }
488 }
489
490 public static final int INSERT_TYPE = 0;
491 public static final int UPDATE_TYPE = 1;
492 public static final int DELETE_TYPE = 2;
493
494 private String mName;
495 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
496 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
497
498 public ObserverNode(String name) {
499 mName = name;
500 }
501
502 private String getUriSegment(Uri uri, int index) {
503 if (uri != null) {
504 if (index == 0) {
505 return uri.getAuthority();
506 } else {
507 return uri.getPathSegments().get(index - 1);
508 }
509 } else {
510 return null;
511 }
512 }
513
514 private int countUriSegments(Uri uri) {
515 if (uri == null) {
516 return 0;
517 }
518 return uri.getPathSegments().size() + 1;
519 }
520
Fred Quintana002ffad52010-03-02 11:18:16 -0800521 public void addObserverLocked(Uri uri, IContentObserver observer,
522 boolean notifyForDescendents, Object observersLock) {
523 addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525
Fred Quintana002ffad52010-03-02 11:18:16 -0800526 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
527 boolean notifyForDescendents, Object observersLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 // If this is the leaf node add the observer
529 if (index == countUriSegments(uri)) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800530 mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 return;
532 }
533
534 // Look to see if the proper child already exists
535 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -0500536 if (segment == null) {
537 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
538 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 int N = mChildren.size();
540 for (int i = 0; i < N; i++) {
541 ObserverNode node = mChildren.get(i);
542 if (node.mName.equals(segment)) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800543 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 return;
545 }
546 }
547
548 // No child found, create one
549 ObserverNode node = new ObserverNode(segment);
550 mChildren.add(node);
Fred Quintana002ffad52010-03-02 11:18:16 -0800551 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
553
Fred Quintana002ffad52010-03-02 11:18:16 -0800554 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 int size = mChildren.size();
556 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800557 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 if (empty) {
559 mChildren.remove(i);
560 i--;
561 size--;
562 }
563 }
564
565 IBinder observerBinder = observer.asBinder();
566 size = mObservers.size();
567 for (int i = 0; i < size; i++) {
568 ObserverEntry entry = mObservers.get(i);
569 if (entry.observer.asBinder() == observerBinder) {
570 mObservers.remove(i);
571 // We no longer need to listen for death notifications. Remove it.
572 observerBinder.unlinkToDeath(entry, 0);
573 break;
574 }
575 }
576
577 if (mChildren.size() == 0 && mObservers.size() == 0) {
578 return true;
579 }
580 return false;
581 }
582
Fred Quintana002ffad52010-03-02 11:18:16 -0800583 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
584 boolean selfNotify, ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 int N = mObservers.size();
586 IBinder observerBinder = observer == null ? null : observer.asBinder();
587 for (int i = 0; i < N; i++) {
588 ObserverEntry entry = mObservers.get(i);
589
590 // Don't notify the observer if it sent the notification and isn't interesed
591 // in self notifications
592 if (entry.observer.asBinder() == observerBinder && !selfNotify) {
593 continue;
594 }
595
596 // Make sure the observer is interested in the notification
597 if (leaf || (!leaf && entry.notifyForDescendents)) {
598 calls.add(new ObserverCall(this, entry.observer, selfNotify));
599 }
600 }
601 }
602
Fred Quintana002ffad52010-03-02 11:18:16 -0800603 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 boolean selfNotify, ArrayList<ObserverCall> calls) {
605 String segment = null;
606 int segmentCount = countUriSegments(uri);
607 if (index >= segmentCount) {
608 // This is the leaf node, notify all observers
Fred Quintana002ffad52010-03-02 11:18:16 -0800609 collectMyObserversLocked(true, observer, selfNotify, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 } else if (index < segmentCount){
611 segment = getUriSegment(uri, index);
612 // Notify any observers at this level who are interested in descendents
Fred Quintana002ffad52010-03-02 11:18:16 -0800613 collectMyObserversLocked(false, observer, selfNotify, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
615
616 int N = mChildren.size();
617 for (int i = 0; i < N; i++) {
618 ObserverNode node = mChildren.get(i);
619 if (segment == null || node.mName.equals(segment)) {
620 // We found the child,
Fred Quintana002ffad52010-03-02 11:18:16 -0800621 node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 if (segment != null) {
623 break;
624 }
625 }
626 }
627 }
628 }
629}