blob: a2af55817bd567dfd77a74e06469289b7a9edefe [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.Log;
29import android.Manifest;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33import java.util.ArrayList;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080034import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36/**
37 * {@hide}
38 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070039public final class ContentService extends IContentService.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 private static final String TAG = "ContentService";
41 private Context mContext;
42 private boolean mFactoryTest;
43 private final ObserverNode mRootNode = new ObserverNode("");
44 private SyncManager mSyncManager = null;
45 private final Object mSyncManagerLock = new Object();
46
47 private SyncManager getSyncManager() {
48 synchronized(mSyncManagerLock) {
49 try {
50 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
51 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
52 } catch (SQLiteException e) {
53 Log.e(TAG, "Can't create SyncManager", e);
54 }
55 return mSyncManager;
56 }
57 }
58
59 @Override
60 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
61 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
62 "caller doesn't have the DUMP permission");
63
64 // This makes it so that future permission checks will be in the context of this
65 // process rather than the caller's process. We will restore this before returning.
66 long identityToken = clearCallingIdentity();
67 try {
68 if (mSyncManager == null) {
69 pw.println("No SyncManager created! (Disk full?)");
70 } else {
71 mSyncManager.dump(fd, pw);
72 }
73 } finally {
74 restoreCallingIdentity(identityToken);
75 }
76 }
77
Dianne Hackborn231cc602009-04-27 17:10:36 -070078 @Override
79 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
80 throws RemoteException {
81 try {
82 return super.onTransact(code, data, reply, flags);
83 } catch (RuntimeException e) {
84 // The content service only throws security exceptions, so let's
85 // log all others.
86 if (!(e instanceof SecurityException)) {
87 Log.e(TAG, "Content Service Crash", e);
88 }
89 throw e;
90 }
91 }
92
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 /*package*/ ContentService(Context context, boolean factoryTest) {
94 mContext = context;
95 mFactoryTest = factoryTest;
96 getSyncManager();
97 }
98
99 public void registerContentObserver(Uri uri, boolean notifyForDescendents,
100 IContentObserver observer) {
101 if (observer == null || uri == null) {
102 throw new IllegalArgumentException("You must pass a valid uri and observer");
103 }
104 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800105 mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
Joe Onorato43a17652011-04-06 19:22:23 -0700106 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 " with notifyForDescendents " + notifyForDescendents);
108 }
109 }
110
111 public void unregisterContentObserver(IContentObserver observer) {
112 if (observer == null) {
113 throw new IllegalArgumentException("You must pass a valid observer");
114 }
115 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800116 mRootNode.removeObserverLocked(observer);
Joe Onorato43a17652011-04-06 19:22:23 -0700117 if (false) Log.v(TAG, "Unregistered observer " + observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 }
119 }
120
121 public void notifyChange(Uri uri, IContentObserver observer,
122 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
123 if (Log.isLoggable(TAG, Log.VERBOSE)) {
124 Log.v(TAG, "Notifying update of " + uri + " from observer " + observer
125 + ", syncToNetwork " + syncToNetwork);
126 }
127 // This makes it so that future permission checks will be in the context of this
128 // process rather than the caller's process. We will restore this before returning.
129 long identityToken = clearCallingIdentity();
130 try {
131 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
132 synchronized (mRootNode) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800133 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 calls);
135 }
136 final int numCalls = calls.size();
137 for (int i=0; i<numCalls; i++) {
138 ObserverCall oc = calls.get(i);
139 try {
140 oc.mObserver.onChange(oc.mSelfNotify);
141 if (Log.isLoggable(TAG, Log.VERBOSE)) {
142 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
143 }
144 } catch (RemoteException ex) {
145 synchronized (mRootNode) {
146 Log.w(TAG, "Found dead observer, removing");
147 IBinder binder = oc.mObserver.asBinder();
148 final ArrayList<ObserverNode.ObserverEntry> list
149 = oc.mNode.mObservers;
150 int numList = list.size();
151 for (int j=0; j<numList; j++) {
152 ObserverNode.ObserverEntry oe = list.get(j);
153 if (oe.observer.asBinder() == binder) {
154 list.remove(j);
155 j--;
156 numList--;
157 }
158 }
159 }
160 }
161 }
162 if (syncToNetwork) {
163 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700164 if (syncManager != null) {
165 syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority());
166 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 }
168 } finally {
169 restoreCallingIdentity(identityToken);
170 }
171 }
172
173 /**
174 * Hide this class since it is not part of api,
175 * but current unittest framework requires it to be public
176 * @hide
177 *
178 */
179 public static final class ObserverCall {
180 final ObserverNode mNode;
181 final IContentObserver mObserver;
182 final boolean mSelfNotify;
183
184 ObserverCall(ObserverNode node, IContentObserver observer,
185 boolean selfNotify) {
186 mNode = node;
187 mObserver = observer;
188 mSelfNotify = selfNotify;
189 }
190 }
191
Fred Quintanaac9385e2009-06-22 18:00:59 -0700192 public void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 ContentResolver.validateSyncExtrasBundle(extras);
194 // This makes it so that future permission checks will be in the context of this
195 // process rather than the caller's process. We will restore this before returning.
196 long identityToken = clearCallingIdentity();
197 try {
198 SyncManager syncManager = getSyncManager();
Fred Quintanaac9385e2009-06-22 18:00:59 -0700199 if (syncManager != null) {
Fred Quintana4a6679b2009-08-17 13:05:39 -0700200 syncManager.scheduleSync(account, authority, extras, 0 /* no delay */,
201 false /* onlyThoseWithUnkownSyncableState */);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700202 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 } finally {
204 restoreCallingIdentity(identityToken);
205 }
206 }
207
208 /**
209 * Clear all scheduled sync operations that match the uri and cancel the active sync
Fred Quintanaac9385e2009-06-22 18:00:59 -0700210 * if they match the authority and account, if they are present.
211 * @param account filter the pending and active syncs to cancel using this account
212 * @param authority filter the pending and active syncs to cancel using this authority
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700214 public void cancelSync(Account account, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 // This makes it so that future permission checks will be in the context of this
216 // process rather than the caller's process. We will restore this before returning.
217 long identityToken = clearCallingIdentity();
218 try {
219 SyncManager syncManager = getSyncManager();
220 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700221 syncManager.clearScheduledSyncOperations(account, authority);
222 syncManager.cancelActiveSync(account, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
224 } finally {
225 restoreCallingIdentity(identityToken);
226 }
227 }
228
Fred Quintanaac9385e2009-06-22 18:00:59 -0700229 /**
230 * Get information about the SyncAdapters that are known to the system.
231 * @return an array of SyncAdapters that have registered with the system
232 */
233 public SyncAdapterType[] getSyncAdapterTypes() {
234 // This makes it so that future permission checks will be in the context of this
235 // process rather than the caller's process. We will restore this before returning.
236 long identityToken = clearCallingIdentity();
237 try {
238 SyncManager syncManager = getSyncManager();
239 return syncManager.getSyncAdapterTypes();
240 } finally {
241 restoreCallingIdentity(identityToken);
242 }
243 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700244
Fred Quintanaac9385e2009-06-22 18:00:59 -0700245 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700246 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
247 "no permission to read the sync settings");
248 long identityToken = clearCallingIdentity();
249 try {
250 SyncManager syncManager = getSyncManager();
251 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700252 return syncManager.getSyncStorageEngine().getSyncAutomatically(
253 account, providerName);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700254 }
255 } finally {
256 restoreCallingIdentity(identityToken);
257 }
258 return false;
259 }
260
Fred Quintanaac9385e2009-06-22 18:00:59 -0700261 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700262 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
263 "no permission to write the sync settings");
264 long identityToken = clearCallingIdentity();
265 try {
266 SyncManager syncManager = getSyncManager();
267 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700268 syncManager.getSyncStorageEngine().setSyncAutomatically(
269 account, providerName, sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700270 }
271 } finally {
272 restoreCallingIdentity(identityToken);
273 }
274 }
275
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800276 public void addPeriodicSync(Account account, String authority, Bundle extras,
277 long pollFrequency) {
278 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
279 "no permission to write the sync settings");
280 long identityToken = clearCallingIdentity();
281 try {
282 getSyncManager().getSyncStorageEngine().addPeriodicSync(
283 account, authority, extras, pollFrequency);
284 } finally {
285 restoreCallingIdentity(identityToken);
286 }
287 }
288
289 public void removePeriodicSync(Account account, String authority, Bundle extras) {
290 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
291 "no permission to write the sync settings");
292 long identityToken = clearCallingIdentity();
293 try {
294 getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras);
295 } finally {
296 restoreCallingIdentity(identityToken);
297 }
298 }
299
300 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
301 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
302 "no permission to read the sync settings");
303 long identityToken = clearCallingIdentity();
304 try {
305 return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
306 account, providerName);
307 } finally {
308 restoreCallingIdentity(identityToken);
309 }
310 }
311
Fred Quintana5e787c42009-08-16 23:13:53 -0700312 public int getIsSyncable(Account account, String providerName) {
313 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
314 "no permission to read the sync settings");
315 long identityToken = clearCallingIdentity();
316 try {
317 SyncManager syncManager = getSyncManager();
318 if (syncManager != null) {
319 return syncManager.getSyncStorageEngine().getIsSyncable(
320 account, providerName);
321 }
322 } finally {
323 restoreCallingIdentity(identityToken);
324 }
325 return -1;
326 }
327
328 public void setIsSyncable(Account account, String providerName, int syncable) {
329 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
330 "no permission to write the sync settings");
331 long identityToken = clearCallingIdentity();
332 try {
333 SyncManager syncManager = getSyncManager();
334 if (syncManager != null) {
335 syncManager.getSyncStorageEngine().setIsSyncable(
336 account, providerName, syncable);
337 }
338 } finally {
339 restoreCallingIdentity(identityToken);
340 }
341 }
342
Fred Quintanaac9385e2009-06-22 18:00:59 -0700343 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700344 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
345 "no permission to read the sync settings");
346 long identityToken = clearCallingIdentity();
347 try {
348 SyncManager syncManager = getSyncManager();
349 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700350 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700351 }
352 } finally {
353 restoreCallingIdentity(identityToken);
354 }
355 return false;
356 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700357
Fred Quintanaac9385e2009-06-22 18:00:59 -0700358 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700359 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
360 "no permission to write the sync settings");
361 long identityToken = clearCallingIdentity();
362 try {
363 SyncManager syncManager = getSyncManager();
364 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700365 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700366 }
367 } finally {
368 restoreCallingIdentity(identityToken);
369 }
370 }
371
Dianne Hackborn7a135592009-05-06 00:28:37 -0700372 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700373 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
374 "no permission to read the sync stats");
375 long identityToken = clearCallingIdentity();
376 try {
377 SyncManager syncManager = getSyncManager();
378 if (syncManager != null) {
379 return syncManager.getSyncStorageEngine().isSyncActive(
380 account, authority);
381 }
382 } finally {
383 restoreCallingIdentity(identityToken);
384 }
385 return false;
386 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700387
Fred Quintanac6a69552010-09-27 17:05:04 -0700388 public List<SyncInfo> getCurrentSyncs() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700389 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
390 "no permission to read the sync stats");
391 long identityToken = clearCallingIdentity();
392 try {
Fred Quintanac6a69552010-09-27 17:05:04 -0700393 return getSyncManager().getSyncStorageEngine().getCurrentSyncs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700394 } finally {
395 restoreCallingIdentity(identityToken);
396 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700397 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700398
Fred Quintanaac9385e2009-06-22 18:00:59 -0700399 public SyncStatusInfo getSyncStatus(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700400 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
401 "no permission to read the sync stats");
402 long identityToken = clearCallingIdentity();
403 try {
404 SyncManager syncManager = getSyncManager();
405 if (syncManager != null) {
Costin Manolacheb7520982009-09-02 18:03:05 -0700406 return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
407 account, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700408 }
409 } finally {
410 restoreCallingIdentity(identityToken);
411 }
412 return null;
413 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700414
Fred Quintanaac9385e2009-06-22 18:00:59 -0700415 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700416 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
417 "no permission to read the sync stats");
418 long identityToken = clearCallingIdentity();
419 try {
420 SyncManager syncManager = getSyncManager();
421 if (syncManager != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700422 return syncManager.getSyncStorageEngine().isSyncPending(account, authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700423 }
424 } finally {
425 restoreCallingIdentity(identityToken);
426 }
427 return false;
428 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700429
Dianne Hackborn231cc602009-04-27 17:10:36 -0700430 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
431 long identityToken = clearCallingIdentity();
432 try {
433 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800434 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700435 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700436 }
437 } finally {
438 restoreCallingIdentity(identityToken);
439 }
440 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700441
Dianne Hackborn231cc602009-04-27 17:10:36 -0700442 public void removeStatusChangeListener(ISyncStatusObserver callback) {
443 long identityToken = clearCallingIdentity();
444 try {
445 SyncManager syncManager = getSyncManager();
Fred Quintana1b487ec2010-02-26 10:57:55 -0800446 if (syncManager != null && callback != null) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700447 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700448 }
449 } finally {
450 restoreCallingIdentity(identityToken);
451 }
452 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 public static IContentService main(Context context, boolean factoryTest) {
455 ContentService service = new ContentService(context, factoryTest);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700456 ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 return service;
458 }
459
460 /**
461 * Hide this class since it is not part of api,
462 * but current unittest framework requires it to be public
463 * @hide
464 */
465 public static final class ObserverNode {
466 private class ObserverEntry implements IBinder.DeathRecipient {
Fred Quintana002ffad52010-03-02 11:18:16 -0800467 public final IContentObserver observer;
468 public final boolean notifyForDescendents;
469 private final Object observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470
Fred Quintana002ffad52010-03-02 11:18:16 -0800471 public ObserverEntry(IContentObserver o, boolean n, Object observersLock) {
472 this.observersLock = observersLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 observer = o;
474 notifyForDescendents = n;
475 try {
476 observer.asBinder().linkToDeath(this, 0);
477 } catch (RemoteException e) {
478 binderDied();
479 }
480 }
481
482 public void binderDied() {
Fred Quintana002ffad52010-03-02 11:18:16 -0800483 synchronized (observersLock) {
484 removeObserverLocked(observer);
485 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 }
488
489 public static final int INSERT_TYPE = 0;
490 public static final int UPDATE_TYPE = 1;
491 public static final int DELETE_TYPE = 2;
492
493 private String mName;
494 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
495 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
496
497 public ObserverNode(String name) {
498 mName = name;
499 }
500
501 private String getUriSegment(Uri uri, int index) {
502 if (uri != null) {
503 if (index == 0) {
504 return uri.getAuthority();
505 } else {
506 return uri.getPathSegments().get(index - 1);
507 }
508 } else {
509 return null;
510 }
511 }
512
513 private int countUriSegments(Uri uri) {
514 if (uri == null) {
515 return 0;
516 }
517 return uri.getPathSegments().size() + 1;
518 }
519
Fred Quintana002ffad52010-03-02 11:18:16 -0800520 public void addObserverLocked(Uri uri, IContentObserver observer,
521 boolean notifyForDescendents, Object observersLock) {
522 addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 }
524
Fred Quintana002ffad52010-03-02 11:18:16 -0800525 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
526 boolean notifyForDescendents, Object observersLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 // If this is the leaf node add the observer
528 if (index == countUriSegments(uri)) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800529 mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 return;
531 }
532
533 // Look to see if the proper child already exists
534 String segment = getUriSegment(uri, index);
Anthony Newnamf5126642010-03-22 17:29:06 -0500535 if (segment == null) {
536 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
537 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 int N = mChildren.size();
539 for (int i = 0; i < N; i++) {
540 ObserverNode node = mChildren.get(i);
541 if (node.mName.equals(segment)) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800542 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 return;
544 }
545 }
546
547 // No child found, create one
548 ObserverNode node = new ObserverNode(segment);
549 mChildren.add(node);
Fred Quintana002ffad52010-03-02 11:18:16 -0800550 node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 }
552
Fred Quintana002ffad52010-03-02 11:18:16 -0800553 public boolean removeObserverLocked(IContentObserver observer) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 int size = mChildren.size();
555 for (int i = 0; i < size; i++) {
Fred Quintana002ffad52010-03-02 11:18:16 -0800556 boolean empty = mChildren.get(i).removeObserverLocked(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 if (empty) {
558 mChildren.remove(i);
559 i--;
560 size--;
561 }
562 }
563
564 IBinder observerBinder = observer.asBinder();
565 size = mObservers.size();
566 for (int i = 0; i < size; i++) {
567 ObserverEntry entry = mObservers.get(i);
568 if (entry.observer.asBinder() == observerBinder) {
569 mObservers.remove(i);
570 // We no longer need to listen for death notifications. Remove it.
571 observerBinder.unlinkToDeath(entry, 0);
572 break;
573 }
574 }
575
576 if (mChildren.size() == 0 && mObservers.size() == 0) {
577 return true;
578 }
579 return false;
580 }
581
Fred Quintana002ffad52010-03-02 11:18:16 -0800582 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
583 boolean selfNotify, ArrayList<ObserverCall> calls) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 int N = mObservers.size();
585 IBinder observerBinder = observer == null ? null : observer.asBinder();
586 for (int i = 0; i < N; i++) {
587 ObserverEntry entry = mObservers.get(i);
588
589 // Don't notify the observer if it sent the notification and isn't interesed
590 // in self notifications
591 if (entry.observer.asBinder() == observerBinder && !selfNotify) {
592 continue;
593 }
594
595 // Make sure the observer is interested in the notification
596 if (leaf || (!leaf && entry.notifyForDescendents)) {
597 calls.add(new ObserverCall(this, entry.observer, selfNotify));
598 }
599 }
600 }
601
Fred Quintana002ffad52010-03-02 11:18:16 -0800602 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 boolean selfNotify, ArrayList<ObserverCall> calls) {
604 String segment = null;
605 int segmentCount = countUriSegments(uri);
606 if (index >= segmentCount) {
607 // This is the leaf node, notify all observers
Fred Quintana002ffad52010-03-02 11:18:16 -0800608 collectMyObserversLocked(true, observer, selfNotify, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 } else if (index < segmentCount){
610 segment = getUriSegment(uri, index);
611 // Notify any observers at this level who are interested in descendents
Fred Quintana002ffad52010-03-02 11:18:16 -0800612 collectMyObserversLocked(false, observer, selfNotify, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 }
614
615 int N = mChildren.size();
616 for (int i = 0; i < N; i++) {
617 ObserverNode node = mChildren.get(i);
618 if (segment == null || node.mName.equals(segment)) {
619 // We found the child,
Fred Quintana002ffad52010-03-02 11:18:16 -0800620 node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 if (segment != null) {
622 break;
623 }
624 }
625 }
626 }
627 }
628}