blob: b0288681b2b7a42f79dfd8ecfee2fcac2feffa08 [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
19import android.database.IContentObserver;
20import android.database.sqlite.SQLiteException;
21import android.net.Uri;
22import android.os.Bundle;
23import android.os.IBinder;
24import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.util.Config;
27import android.util.Log;
28import android.Manifest;
29
30import java.io.FileDescriptor;
31import java.io.PrintWriter;
32import java.util.ArrayList;
33
34/**
35 * {@hide}
36 */
37public final class ContentService extends ContentServiceNative {
38 private static final String TAG = "ContentService";
39 private Context mContext;
40 private boolean mFactoryTest;
41 private final ObserverNode mRootNode = new ObserverNode("");
42 private SyncManager mSyncManager = null;
43 private final Object mSyncManagerLock = new Object();
44
45 private SyncManager getSyncManager() {
46 synchronized(mSyncManagerLock) {
47 try {
48 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
49 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
50 } catch (SQLiteException e) {
51 Log.e(TAG, "Can't create SyncManager", e);
52 }
53 return mSyncManager;
54 }
55 }
56
57 @Override
58 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
59 mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
60 "caller doesn't have the DUMP permission");
61
62 // This makes it so that future permission checks will be in the context of this
63 // process rather than the caller's process. We will restore this before returning.
64 long identityToken = clearCallingIdentity();
65 try {
66 if (mSyncManager == null) {
67 pw.println("No SyncManager created! (Disk full?)");
68 } else {
69 mSyncManager.dump(fd, pw);
70 }
71 } finally {
72 restoreCallingIdentity(identityToken);
73 }
74 }
75
76 /*package*/ ContentService(Context context, boolean factoryTest) {
77 mContext = context;
78 mFactoryTest = factoryTest;
79 getSyncManager();
80 }
81
82 public void registerContentObserver(Uri uri, boolean notifyForDescendents,
83 IContentObserver observer) {
84 if (observer == null || uri == null) {
85 throw new IllegalArgumentException("You must pass a valid uri and observer");
86 }
87 synchronized (mRootNode) {
88 mRootNode.addObserver(uri, observer, notifyForDescendents);
89 if (Config.LOGV) Log.v(TAG, "Registered observer " + observer + " at " + uri +
90 " with notifyForDescendents " + notifyForDescendents);
91 }
92 }
93
94 public void unregisterContentObserver(IContentObserver observer) {
95 if (observer == null) {
96 throw new IllegalArgumentException("You must pass a valid observer");
97 }
98 synchronized (mRootNode) {
99 mRootNode.removeObserver(observer);
100 if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer);
101 }
102 }
103
104 public void notifyChange(Uri uri, IContentObserver observer,
105 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
106 if (Log.isLoggable(TAG, Log.VERBOSE)) {
107 Log.v(TAG, "Notifying update of " + uri + " from observer " + observer
108 + ", syncToNetwork " + syncToNetwork);
109 }
110 // This makes it so that future permission checks will be in the context of this
111 // process rather than the caller's process. We will restore this before returning.
112 long identityToken = clearCallingIdentity();
113 try {
114 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
115 synchronized (mRootNode) {
116 mRootNode.collectObservers(uri, 0, observer, observerWantsSelfNotifications,
117 calls);
118 }
119 final int numCalls = calls.size();
120 for (int i=0; i<numCalls; i++) {
121 ObserverCall oc = calls.get(i);
122 try {
123 oc.mObserver.onChange(oc.mSelfNotify);
124 if (Log.isLoggable(TAG, Log.VERBOSE)) {
125 Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
126 }
127 } catch (RemoteException ex) {
128 synchronized (mRootNode) {
129 Log.w(TAG, "Found dead observer, removing");
130 IBinder binder = oc.mObserver.asBinder();
131 final ArrayList<ObserverNode.ObserverEntry> list
132 = oc.mNode.mObservers;
133 int numList = list.size();
134 for (int j=0; j<numList; j++) {
135 ObserverNode.ObserverEntry oe = list.get(j);
136 if (oe.observer.asBinder() == binder) {
137 list.remove(j);
138 j--;
139 numList--;
140 }
141 }
142 }
143 }
144 }
145 if (syncToNetwork) {
146 SyncManager syncManager = getSyncManager();
147 if (syncManager != null) syncManager.scheduleLocalSync(uri);
148 }
149 } finally {
150 restoreCallingIdentity(identityToken);
151 }
152 }
153
154 /**
155 * Hide this class since it is not part of api,
156 * but current unittest framework requires it to be public
157 * @hide
158 *
159 */
160 public static final class ObserverCall {
161 final ObserverNode mNode;
162 final IContentObserver mObserver;
163 final boolean mSelfNotify;
164
165 ObserverCall(ObserverNode node, IContentObserver observer,
166 boolean selfNotify) {
167 mNode = node;
168 mObserver = observer;
169 mSelfNotify = selfNotify;
170 }
171 }
172
173 public void startSync(Uri url, Bundle extras) {
174 ContentResolver.validateSyncExtrasBundle(extras);
175 // This makes it so that future permission checks will be in the context of this
176 // process rather than the caller's process. We will restore this before returning.
177 long identityToken = clearCallingIdentity();
178 try {
179 SyncManager syncManager = getSyncManager();
180 if (syncManager != null) syncManager.startSync(url, extras);
181 } finally {
182 restoreCallingIdentity(identityToken);
183 }
184 }
185
186 /**
187 * Clear all scheduled sync operations that match the uri and cancel the active sync
188 * if it matches the uri. If the uri is null, clear all scheduled syncs and cancel
189 * the active one, if there is one.
190 * @param uri Filter on the sync operations to cancel, or all if null.
191 */
192 public void cancelSync(Uri uri) {
193 // This makes it so that future permission checks will be in the context of this
194 // process rather than the caller's process. We will restore this before returning.
195 long identityToken = clearCallingIdentity();
196 try {
197 SyncManager syncManager = getSyncManager();
198 if (syncManager != null) {
199 syncManager.clearScheduledSyncOperations(uri);
200 syncManager.cancelActiveSync(uri);
201 }
202 } finally {
203 restoreCallingIdentity(identityToken);
204 }
205 }
206
207 public static IContentService main(Context context, boolean factoryTest) {
208 ContentService service = new ContentService(context, factoryTest);
209 ServiceManager.addService("content", service);
210 return service;
211 }
212
213 /**
214 * Hide this class since it is not part of api,
215 * but current unittest framework requires it to be public
216 * @hide
217 */
218 public static final class ObserverNode {
219 private class ObserverEntry implements IBinder.DeathRecipient {
220 public IContentObserver observer;
221 public boolean notifyForDescendents;
222
223 public ObserverEntry(IContentObserver o, boolean n) {
224 observer = o;
225 notifyForDescendents = n;
226 try {
227 observer.asBinder().linkToDeath(this, 0);
228 } catch (RemoteException e) {
229 binderDied();
230 }
231 }
232
233 public void binderDied() {
234 removeObserver(observer);
235 }
236 }
237
238 public static final int INSERT_TYPE = 0;
239 public static final int UPDATE_TYPE = 1;
240 public static final int DELETE_TYPE = 2;
241
242 private String mName;
243 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
244 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
245
246 public ObserverNode(String name) {
247 mName = name;
248 }
249
250 private String getUriSegment(Uri uri, int index) {
251 if (uri != null) {
252 if (index == 0) {
253 return uri.getAuthority();
254 } else {
255 return uri.getPathSegments().get(index - 1);
256 }
257 } else {
258 return null;
259 }
260 }
261
262 private int countUriSegments(Uri uri) {
263 if (uri == null) {
264 return 0;
265 }
266 return uri.getPathSegments().size() + 1;
267 }
268
269 public void addObserver(Uri uri, IContentObserver observer, boolean notifyForDescendents) {
270 addObserver(uri, 0, observer, notifyForDescendents);
271 }
272
273 private void addObserver(Uri uri, int index, IContentObserver observer,
274 boolean notifyForDescendents) {
275
276 // If this is the leaf node add the observer
277 if (index == countUriSegments(uri)) {
278 mObservers.add(new ObserverEntry(observer, notifyForDescendents));
279 return;
280 }
281
282 // Look to see if the proper child already exists
283 String segment = getUriSegment(uri, index);
284 int N = mChildren.size();
285 for (int i = 0; i < N; i++) {
286 ObserverNode node = mChildren.get(i);
287 if (node.mName.equals(segment)) {
288 node.addObserver(uri, index + 1, observer, notifyForDescendents);
289 return;
290 }
291 }
292
293 // No child found, create one
294 ObserverNode node = new ObserverNode(segment);
295 mChildren.add(node);
296 node.addObserver(uri, index + 1, observer, notifyForDescendents);
297 }
298
299 public boolean removeObserver(IContentObserver observer) {
300 int size = mChildren.size();
301 for (int i = 0; i < size; i++) {
302 boolean empty = mChildren.get(i).removeObserver(observer);
303 if (empty) {
304 mChildren.remove(i);
305 i--;
306 size--;
307 }
308 }
309
310 IBinder observerBinder = observer.asBinder();
311 size = mObservers.size();
312 for (int i = 0; i < size; i++) {
313 ObserverEntry entry = mObservers.get(i);
314 if (entry.observer.asBinder() == observerBinder) {
315 mObservers.remove(i);
316 // We no longer need to listen for death notifications. Remove it.
317 observerBinder.unlinkToDeath(entry, 0);
318 break;
319 }
320 }
321
322 if (mChildren.size() == 0 && mObservers.size() == 0) {
323 return true;
324 }
325 return false;
326 }
327
328 private void collectMyObservers(Uri uri,
329 boolean leaf, IContentObserver observer, boolean selfNotify,
330 ArrayList<ObserverCall> calls)
331 {
332 int N = mObservers.size();
333 IBinder observerBinder = observer == null ? null : observer.asBinder();
334 for (int i = 0; i < N; i++) {
335 ObserverEntry entry = mObservers.get(i);
336
337 // Don't notify the observer if it sent the notification and isn't interesed
338 // in self notifications
339 if (entry.observer.asBinder() == observerBinder && !selfNotify) {
340 continue;
341 }
342
343 // Make sure the observer is interested in the notification
344 if (leaf || (!leaf && entry.notifyForDescendents)) {
345 calls.add(new ObserverCall(this, entry.observer, selfNotify));
346 }
347 }
348 }
349
350 public void collectObservers(Uri uri, int index, IContentObserver observer,
351 boolean selfNotify, ArrayList<ObserverCall> calls) {
352 String segment = null;
353 int segmentCount = countUriSegments(uri);
354 if (index >= segmentCount) {
355 // This is the leaf node, notify all observers
356 collectMyObservers(uri, true, observer, selfNotify, calls);
357 } else if (index < segmentCount){
358 segment = getUriSegment(uri, index);
359 // Notify any observers at this level who are interested in descendents
360 collectMyObservers(uri, false, observer, selfNotify, calls);
361 }
362
363 int N = mChildren.size();
364 for (int i = 0; i < N; i++) {
365 ObserverNode node = mChildren.get(i);
366 if (segment == null || node.mName.equals(segment)) {
367 // We found the child,
368 node.collectObservers(uri, index + 1, observer, selfNotify, calls);
369 if (segment != null) {
370 break;
371 }
372 }
373 }
374 }
375 }
376}