blob: aaa0917c70dab9a1d14151d62d0a63ad3264c67e [file] [log] [blame]
Fred Quintana718d8a22009-04-29 17:53:20 -07001/*
2 * Copyright (C) 2009 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.pm;
18
Fred Quintana718d8a22009-04-29 17:53:20 -070019import android.content.BroadcastReceiver;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070020import android.content.ComponentName;
21import android.content.Context;
Fred Quintana718d8a22009-04-29 17:53:20 -070022import android.content.Intent;
23import android.content.IntentFilter;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080024import android.content.pm.PackageManager.NameNotFoundException;
25import android.content.res.Resources;
Fred Quintana718d8a22009-04-29 17:53:20 -070026import android.content.res.XmlResourceParser;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080027import android.os.Environment;
28import android.os.Handler;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070029import android.os.UserHandle;
Dianne Hackborn39606a02012-07-31 17:54:35 -070030import android.util.AtomicFile;
Fred Quintana718d8a22009-04-29 17:53:20 -070031import android.util.AttributeSet;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070032import android.util.Log;
33import android.util.Slog;
34import android.util.SparseArray;
Fred Quintana718d8a22009-04-29 17:53:20 -070035import android.util.Xml;
36
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080037import com.android.internal.annotations.GuardedBy;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080038import com.android.internal.util.FastXmlSerializer;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080039import com.google.android.collect.Lists;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070040import com.google.android.collect.Maps;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080041
Fred Quintana718d8a22009-04-29 17:53:20 -070042import org.xmlpull.v1.XmlPullParser;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070043import org.xmlpull.v1.XmlPullParserException;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080044import org.xmlpull.v1.XmlSerializer;
Fred Quintana718d8a22009-04-29 17:53:20 -070045
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070046import java.io.File;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileOutputStream;
50import java.io.IOException;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.Collection;
54import java.util.Collections;
55import java.util.List;
56import java.util.Map;
57
Fred Quintana718d8a22009-04-29 17:53:20 -070058/**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070059 * Cache of registered services. This cache is lazily built by interrogating
60 * {@link PackageManager} on a per-user basis. It's updated as packages are
61 * added, removed and changed. Users are responsible for calling
62 * {@link #invalidateCache(int)} when a user is started, since
63 * {@link PackageManager} broadcasts aren't sent for stopped users.
64 * <p>
65 * The services are referred to by type V and are made available via the
66 * {@link #getServiceInfo} method.
67 *
Fred Quintana718d8a22009-04-29 17:53:20 -070068 * @hide
69 */
70public abstract class RegisteredServicesCache<V> {
71 private static final String TAG = "PackageManager";
Dianne Hackborn40e9f292012-11-27 19:12:23 -080072 private static final boolean DEBUG = false;
Fred Quintana718d8a22009-04-29 17:53:20 -070073
74 public final Context mContext;
75 private final String mInterfaceName;
76 private final String mMetaDataName;
77 private final String mAttributesName;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080078 private final XmlSerializerAndParser<V> mSerializerAndParser;
Fred Quintana718d8a22009-04-29 17:53:20 -070079
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080080 private final Object mServicesLock = new Object();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070081
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080082 @GuardedBy("mServicesLock")
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080083 private boolean mPersistentServicesFileDidNotExist;
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080084 @GuardedBy("mServicesLock")
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070085 private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>();
86
87 private static class UserServices<V> {
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080088 @GuardedBy("mServicesLock")
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070089 public final Map<V, Integer> persistentServices = Maps.newHashMap();
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080090 @GuardedBy("mServicesLock")
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070091 public Map<V, ServiceInfo<V>> services = null;
92 }
93
94 private UserServices<V> findOrCreateUserLocked(int userId) {
95 UserServices<V> services = mUserServices.get(userId);
96 if (services == null) {
97 services = new UserServices<V>();
98 mUserServices.put(userId, services);
99 }
100 return services;
101 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700102
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800103 /**
104 * This file contains the list of known services. We would like to maintain this forever
105 * so we store it as an XML file.
106 */
107 private final AtomicFile mPersistentServicesFile;
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700108
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800109 // the listener and handler are synchronized on "this" and must be updated together
110 private RegisteredServicesCacheListener<V> mListener;
111 private Handler mHandler;
Fred Quintana718d8a22009-04-29 17:53:20 -0700112
113 public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800114 String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
Fred Quintana718d8a22009-04-29 17:53:20 -0700115 mContext = context;
116 mInterfaceName = interfaceName;
117 mMetaDataName = metaDataName;
118 mAttributesName = attributeName;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800119 mSerializerAndParser = serializerAndParser;
120
121 File dataDir = Environment.getDataDirectory();
122 File systemDir = new File(dataDir, "system");
123 File syncDir = new File(systemDir, "registered_services");
124 mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
125
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700126 // Load persisted services from disk
127 readPersistentServicesLocked();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800128
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800129 IntentFilter intentFilter = new IntentFilter();
130 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
131 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
132 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
133 intentFilter.addDataScheme("package");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700134 mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
135
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800136 // Register for events related to sdcard installation.
137 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800138 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
139 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700140 mContext.registerReceiver(mExternalReceiver, sdFilter);
Fred Quintana718d8a22009-04-29 17:53:20 -0700141 }
142
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700143 private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
144 @Override
145 public void onReceive(Context context, Intent intent) {
146 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
147 if (uid != -1) {
148 generateServicesMap(UserHandle.getUserId(uid));
149 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800150 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700151 };
152
153 private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
154 @Override
155 public void onReceive(Context context, Intent intent) {
156 // External apps can't coexist with multi-user, so scan owner
157 generateServicesMap(UserHandle.USER_OWNER);
158 }
159 };
160
161 public void invalidateCache(int userId) {
162 synchronized (mServicesLock) {
163 final UserServices<V> user = findOrCreateUserLocked(userId);
164 user.services = null;
165 }
166 }
167
168 public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
169 synchronized (mServicesLock) {
170 final UserServices<V> user = findOrCreateUserLocked(userId);
171 if (user.services != null) {
172 fout.println("RegisteredServicesCache: " + user.services.size() + " services");
173 for (ServiceInfo<?> info : user.services.values()) {
174 fout.println(" " + info);
175 }
176 } else {
177 fout.println("RegisteredServicesCache: services not loaded");
178 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700179 }
180 }
181
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800182 public RegisteredServicesCacheListener<V> getListener() {
Fred Quintana718d8a22009-04-29 17:53:20 -0700183 synchronized (this) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800184 return mListener;
Fred Quintana718d8a22009-04-29 17:53:20 -0700185 }
186 }
187
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800188 public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
189 if (handler == null) {
190 handler = new Handler(mContext.getMainLooper());
Fred Quintana718d8a22009-04-29 17:53:20 -0700191 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800192 synchronized (this) {
193 mHandler = handler;
194 mListener = listener;
195 }
196 }
197
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700198 private void notifyListener(final V type, final int userId, final boolean removed) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800199 if (DEBUG) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800200 Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
201 }
202 RegisteredServicesCacheListener<V> listener;
203 Handler handler;
204 synchronized (this) {
205 listener = mListener;
206 handler = mHandler;
207 }
208 if (listener == null) {
209 return;
210 }
211
212 final RegisteredServicesCacheListener<V> listener2 = listener;
213 handler.post(new Runnable() {
214 public void run() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700215 listener2.onServiceChanged(type, userId, removed);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800216 }
217 });
Fred Quintana718d8a22009-04-29 17:53:20 -0700218 }
219
220 /**
221 * Value type that describes a Service. The information within can be used
222 * to bind to the service.
223 */
224 public static class ServiceInfo<V> {
225 public final V type;
226 public final ComponentName componentName;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700227 public final int uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700228
Fred Quintana56285a62010-12-02 14:20:51 -0800229 /** @hide */
230 public ServiceInfo(V type, ComponentName componentName, int uid) {
Fred Quintana718d8a22009-04-29 17:53:20 -0700231 this.type = type;
232 this.componentName = componentName;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700233 this.uid = uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700234 }
235
Jeff Hamiltonc42c0dd2009-09-03 09:08:30 -0500236 @Override
Fred Quintana718d8a22009-04-29 17:53:20 -0700237 public String toString() {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800238 return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700239 }
240 }
241
242 /**
243 * Accessor for the registered authenticators.
244 * @param type the account type of the authenticator
245 * @return the AuthenticatorInfo that matches the account type or null if none is present
246 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700247 public ServiceInfo<V> getServiceInfo(V type, int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800248 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700249 // Find user and lazily populate cache
250 final UserServices<V> user = findOrCreateUserLocked(userId);
251 if (user.services == null) {
252 generateServicesMap(userId);
253 }
254 return user.services.get(type);
Fred Quintana718d8a22009-04-29 17:53:20 -0700255 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700256 }
257
258 /**
259 * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
260 * registered authenticators.
261 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700262 public Collection<ServiceInfo<V>> getAllServices(int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800263 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700264 // Find user and lazily populate cache
265 final UserServices<V> user = findOrCreateUserLocked(userId);
266 if (user.services == null) {
267 generateServicesMap(userId);
268 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700269 return Collections.unmodifiableCollection(
270 new ArrayList<ServiceInfo<V>>(user.services.values()));
Fred Quintana718d8a22009-04-29 17:53:20 -0700271 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700272 }
273
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800274 private boolean inSystemImage(int callerUid) {
275 String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
276 for (String name : packages) {
277 try {
278 PackageInfo packageInfo =
279 mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
280 if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
281 return true;
282 }
283 } catch (PackageManager.NameNotFoundException e) {
284 return false;
285 }
286 }
287 return false;
288 }
289
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700290 /**
291 * Populate {@link UserServices#services} by scanning installed packages for
292 * given {@link UserHandle}.
293 */
294 private void generateServicesMap(int userId) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800295 if (DEBUG) {
296 Slog.d(TAG, "generateServicesMap() for " + userId);
297 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700298
299 final PackageManager pm = mContext.getPackageManager();
300 final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
301 final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(
302 new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);
Fred Quintana718d8a22009-04-29 17:53:20 -0700303 for (ResolveInfo resolveInfo : resolveInfos) {
304 try {
305 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800306 if (info == null) {
307 Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
308 continue;
Fred Quintana718d8a22009-04-29 17:53:20 -0700309 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800310 serviceInfos.add(info);
Fred Quintana718d8a22009-04-29 17:53:20 -0700311 } catch (XmlPullParserException e) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800312 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
Fred Quintana718d8a22009-04-29 17:53:20 -0700313 } catch (IOException e) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800314 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
Fred Quintana718d8a22009-04-29 17:53:20 -0700315 }
316 }
317
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800318 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700319 final UserServices<V> user = findOrCreateUserLocked(userId);
320 final boolean firstScan = user.services == null;
321 if (firstScan) {
322 user.services = Maps.newHashMap();
323 } else {
324 user.services.clear();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800325 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700326
Alon Albert3fa51e32010-11-11 09:24:04 -0800327 StringBuilder changes = new StringBuilder();
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800328 boolean changed = false;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800329 for (ServiceInfo<V> info : serviceInfos) {
330 // four cases:
331 // - doesn't exist yet
332 // - add, notify user that it was added
333 // - exists and the UID is the same
334 // - replace, don't notify user
335 // - exists, the UID is different, and the new one is not a system package
336 // - ignore
337 // - exists, the UID is different, and the new one is a system package
338 // - add, notify user that it was added
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700339 Integer previousUid = user.persistentServices.get(info.type);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800340 if (previousUid == null) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800341 if (DEBUG) {
342 changes.append(" New service added: ").append(info).append("\n");
343 }
344 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700345 user.services.put(info.type, info);
346 user.persistentServices.put(info.type, info.uid);
347 if (!(mPersistentServicesFileDidNotExist && firstScan)) {
348 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800349 }
350 } else if (previousUid == info.uid) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800351 if (DEBUG) {
Alon Albert3fa51e32010-11-11 09:24:04 -0800352 changes.append(" Existing service (nop): ").append(info).append("\n");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800353 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700354 user.services.put(info.type, info);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800355 } else if (inSystemImage(info.uid)
356 || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800357 if (DEBUG) {
358 if (inSystemImage(info.uid)) {
359 changes.append(" System service replacing existing: ").append(info)
360 .append("\n");
361 } else {
362 changes.append(" Existing service replacing a removed service: ")
363 .append(info).append("\n");
364 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800365 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800366 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700367 user.services.put(info.type, info);
368 user.persistentServices.put(info.type, info.uid);
369 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800370 } else {
371 // ignore
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800372 if (DEBUG) {
373 changes.append(" Existing service with new uid ignored: ").append(info)
374 .append("\n");
375 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800376 }
377 }
378
379 ArrayList<V> toBeRemoved = Lists.newArrayList();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700380 for (V v1 : user.persistentServices.keySet()) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800381 if (!containsType(serviceInfos, v1)) {
382 toBeRemoved.add(v1);
383 }
384 }
385 for (V v1 : toBeRemoved) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800386 if (DEBUG) {
387 changes.append(" Service removed: ").append(v1).append("\n");
388 }
389 changed = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700390 user.persistentServices.remove(v1);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700391 notifyListener(v1, userId, true /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800392 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800393 if (DEBUG) {
394 if (changes.length() > 0) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700395 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
396 serviceInfos.size() + " services:\n" + changes);
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800397 } else {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700398 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
399 serviceInfos.size() + " services unchanged");
400 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800401 }
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800402 if (changed) {
403 writePersistentServicesLocked();
404 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800405 }
406 }
407
408 private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
409 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
410 if (serviceInfos.get(i).type.equals(type)) {
411 return true;
412 }
413 }
414
415 return false;
416 }
417
418 private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
419 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
420 final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
421 if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
422 return true;
423 }
424 }
425
426 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -0700427 }
428
429 private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
430 throws XmlPullParserException, IOException {
431 android.content.pm.ServiceInfo si = service.serviceInfo;
432 ComponentName componentName = new ComponentName(si.packageName, si.name);
433
434 PackageManager pm = mContext.getPackageManager();
435
436 XmlResourceParser parser = null;
437 try {
438 parser = si.loadXmlMetaData(pm, mMetaDataName);
439 if (parser == null) {
440 throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
441 }
442
443 AttributeSet attrs = Xml.asAttributeSet(parser);
444
445 int type;
446 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
447 && type != XmlPullParser.START_TAG) {
448 }
449
450 String nodeName = parser.getName();
451 if (!mAttributesName.equals(nodeName)) {
452 throw new XmlPullParserException(
453 "Meta-data does not start with " + mAttributesName + " tag");
454 }
455
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800456 V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
457 si.packageName, attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700458 if (v == null) {
459 return null;
460 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700461 final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
462 final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
463 final int uid = applicationInfo.uid;
464 return new ServiceInfo<V>(v, componentName, uid);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800465 } catch (NameNotFoundException e) {
466 throw new XmlPullParserException(
467 "Unable to load resources for pacakge " + si.packageName);
Fred Quintana718d8a22009-04-29 17:53:20 -0700468 } finally {
469 if (parser != null) parser.close();
470 }
471 }
472
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800473 /**
474 * Read all sync status back in to the initial engine state.
475 */
476 private void readPersistentServicesLocked() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700477 mUserServices.clear();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800478 if (mSerializerAndParser == null) {
479 return;
480 }
481 FileInputStream fis = null;
482 try {
483 mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
484 if (mPersistentServicesFileDidNotExist) {
485 return;
486 }
487 fis = mPersistentServicesFile.openRead();
488 XmlPullParser parser = Xml.newPullParser();
489 parser.setInput(fis, null);
490 int eventType = parser.getEventType();
491 while (eventType != XmlPullParser.START_TAG) {
492 eventType = parser.next();
493 }
494 String tagName = parser.getName();
495 if ("services".equals(tagName)) {
496 eventType = parser.next();
497 do {
498 if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
499 tagName = parser.getName();
500 if ("service".equals(tagName)) {
501 V service = mSerializerAndParser.createFromXml(parser);
502 if (service == null) {
503 break;
504 }
505 String uidString = parser.getAttributeValue(null, "uid");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700506 final int uid = Integer.parseInt(uidString);
507 final int userId = UserHandle.getUserId(uid);
508 final UserServices<V> user = findOrCreateUserLocked(userId);
509 user.persistentServices.put(service, uid);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800510 }
511 }
512 eventType = parser.next();
513 } while (eventType != XmlPullParser.END_DOCUMENT);
514 }
515 } catch (Exception e) {
516 Log.w(TAG, "Error reading persistent services, starting from scratch", e);
517 } finally {
518 if (fis != null) {
519 try {
520 fis.close();
521 } catch (java.io.IOException e1) {
522 }
523 }
524 }
525 }
526
527 /**
528 * Write all sync status to the sync status file.
529 */
530 private void writePersistentServicesLocked() {
531 if (mSerializerAndParser == null) {
532 return;
533 }
534 FileOutputStream fos = null;
535 try {
536 fos = mPersistentServicesFile.startWrite();
537 XmlSerializer out = new FastXmlSerializer();
538 out.setOutput(fos, "utf-8");
539 out.startDocument(null, true);
540 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
541 out.startTag(null, "services");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700542 for (int i = 0; i < mUserServices.size(); i++) {
543 final UserServices<V> user = mUserServices.valueAt(i);
544 for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
545 out.startTag(null, "service");
546 out.attribute(null, "uid", Integer.toString(service.getValue()));
547 mSerializerAndParser.writeAsXml(service.getKey(), out);
548 out.endTag(null, "service");
549 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800550 }
551 out.endTag(null, "services");
552 out.endDocument();
553 mPersistentServicesFile.finishWrite(fos);
554 } catch (java.io.IOException e1) {
555 Log.w(TAG, "Error writing accounts", e1);
556 if (fos != null) {
557 mPersistentServicesFile.failWrite(fos);
558 }
559 }
560 }
561
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800562 public abstract V parseServiceAttributes(Resources res,
563 String packageName, AttributeSet attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700564}