blob: 6def4a16970307085fbc9bc99d5412ca7af0005b [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
Dianne Hackborn2269d1572010-02-24 19:54:22 -080037import com.android.internal.util.FastXmlSerializer;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080038import com.google.android.collect.Lists;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070039import com.google.android.collect.Maps;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080040
Fred Quintana718d8a22009-04-29 17:53:20 -070041import org.xmlpull.v1.XmlPullParser;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070042import org.xmlpull.v1.XmlPullParserException;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080043import org.xmlpull.v1.XmlSerializer;
Fred Quintana718d8a22009-04-29 17:53:20 -070044
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070045import java.io.File;
46import java.io.FileDescriptor;
47import java.io.FileInputStream;
48import java.io.FileOutputStream;
49import java.io.IOException;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.Collection;
53import java.util.Collections;
54import java.util.List;
55import java.util.Map;
56
Fred Quintana718d8a22009-04-29 17:53:20 -070057/**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070058 * Cache of registered services. This cache is lazily built by interrogating
59 * {@link PackageManager} on a per-user basis. It's updated as packages are
60 * added, removed and changed. Users are responsible for calling
61 * {@link #invalidateCache(int)} when a user is started, since
62 * {@link PackageManager} broadcasts aren't sent for stopped users.
63 * <p>
64 * The services are referred to by type V and are made available via the
65 * {@link #getServiceInfo} method.
66 *
Fred Quintana718d8a22009-04-29 17:53:20 -070067 * @hide
68 */
69public abstract class RegisteredServicesCache<V> {
70 private static final String TAG = "PackageManager";
71
72 public final Context mContext;
73 private final String mInterfaceName;
74 private final String mMetaDataName;
75 private final String mAttributesName;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080076 private final XmlSerializerAndParser<V> mSerializerAndParser;
Fred Quintana718d8a22009-04-29 17:53:20 -070077
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080078 private final Object mServicesLock = new Object();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070079
80 // @GuardedBy("mServicesLock")
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080081 private boolean mPersistentServicesFileDidNotExist;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070082 // @GuardedBy("mServicesLock")
83 private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>();
84
85 private static class UserServices<V> {
86 // @GuardedBy("mServicesLock")
87 public final Map<V, Integer> persistentServices = Maps.newHashMap();
88 // @GuardedBy("mServicesLock")
89 public Map<V, ServiceInfo<V>> services = null;
90 }
91
92 private UserServices<V> findOrCreateUserLocked(int userId) {
93 UserServices<V> services = mUserServices.get(userId);
94 if (services == null) {
95 services = new UserServices<V>();
96 mUserServices.put(userId, services);
97 }
98 return services;
99 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700100
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800101 /**
102 * This file contains the list of known services. We would like to maintain this forever
103 * so we store it as an XML file.
104 */
105 private final AtomicFile mPersistentServicesFile;
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700106
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800107 // the listener and handler are synchronized on "this" and must be updated together
108 private RegisteredServicesCacheListener<V> mListener;
109 private Handler mHandler;
Fred Quintana718d8a22009-04-29 17:53:20 -0700110
111 public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800112 String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
Fred Quintana718d8a22009-04-29 17:53:20 -0700113 mContext = context;
114 mInterfaceName = interfaceName;
115 mMetaDataName = metaDataName;
116 mAttributesName = attributeName;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800117 mSerializerAndParser = serializerAndParser;
118
119 File dataDir = Environment.getDataDirectory();
120 File systemDir = new File(dataDir, "system");
121 File syncDir = new File(systemDir, "registered_services");
122 mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
123
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700124 // Load persisted services from disk
125 readPersistentServicesLocked();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800126
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800127 IntentFilter intentFilter = new IntentFilter();
128 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
129 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
130 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
131 intentFilter.addDataScheme("package");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700132 mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
133
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800134 // Register for events related to sdcard installation.
135 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800136 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
137 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700138 mContext.registerReceiver(mExternalReceiver, sdFilter);
Fred Quintana718d8a22009-04-29 17:53:20 -0700139 }
140
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700141 private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
142 @Override
143 public void onReceive(Context context, Intent intent) {
144 final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
145 if (uid != -1) {
146 generateServicesMap(UserHandle.getUserId(uid));
147 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800148 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700149 };
150
151 private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
152 @Override
153 public void onReceive(Context context, Intent intent) {
154 // External apps can't coexist with multi-user, so scan owner
155 generateServicesMap(UserHandle.USER_OWNER);
156 }
157 };
158
159 public void invalidateCache(int userId) {
160 synchronized (mServicesLock) {
161 final UserServices<V> user = findOrCreateUserLocked(userId);
162 user.services = null;
163 }
164 }
165
166 public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
167 synchronized (mServicesLock) {
168 final UserServices<V> user = findOrCreateUserLocked(userId);
169 if (user.services != null) {
170 fout.println("RegisteredServicesCache: " + user.services.size() + " services");
171 for (ServiceInfo<?> info : user.services.values()) {
172 fout.println(" " + info);
173 }
174 } else {
175 fout.println("RegisteredServicesCache: services not loaded");
176 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700177 }
178 }
179
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800180 public RegisteredServicesCacheListener<V> getListener() {
Fred Quintana718d8a22009-04-29 17:53:20 -0700181 synchronized (this) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800182 return mListener;
Fred Quintana718d8a22009-04-29 17:53:20 -0700183 }
184 }
185
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800186 public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
187 if (handler == null) {
188 handler = new Handler(mContext.getMainLooper());
Fred Quintana718d8a22009-04-29 17:53:20 -0700189 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800190 synchronized (this) {
191 mHandler = handler;
192 mListener = listener;
193 }
194 }
195
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700196 private void notifyListener(final V type, final int userId, final boolean removed) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800197 if (Log.isLoggable(TAG, Log.VERBOSE)) {
198 Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
199 }
200 RegisteredServicesCacheListener<V> listener;
201 Handler handler;
202 synchronized (this) {
203 listener = mListener;
204 handler = mHandler;
205 }
206 if (listener == null) {
207 return;
208 }
209
210 final RegisteredServicesCacheListener<V> listener2 = listener;
211 handler.post(new Runnable() {
212 public void run() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700213 listener2.onServiceChanged(type, userId, removed);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800214 }
215 });
Fred Quintana718d8a22009-04-29 17:53:20 -0700216 }
217
218 /**
219 * Value type that describes a Service. The information within can be used
220 * to bind to the service.
221 */
222 public static class ServiceInfo<V> {
223 public final V type;
224 public final ComponentName componentName;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700225 public final int uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700226
Fred Quintana56285a62010-12-02 14:20:51 -0800227 /** @hide */
228 public ServiceInfo(V type, ComponentName componentName, int uid) {
Fred Quintana718d8a22009-04-29 17:53:20 -0700229 this.type = type;
230 this.componentName = componentName;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700231 this.uid = uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700232 }
233
Jeff Hamiltonc42c0dd2009-09-03 09:08:30 -0500234 @Override
Fred Quintana718d8a22009-04-29 17:53:20 -0700235 public String toString() {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800236 return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
Fred Quintana718d8a22009-04-29 17:53:20 -0700237 }
238 }
239
240 /**
241 * Accessor for the registered authenticators.
242 * @param type the account type of the authenticator
243 * @return the AuthenticatorInfo that matches the account type or null if none is present
244 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700245 public ServiceInfo<V> getServiceInfo(V type, int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800246 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700247 // Find user and lazily populate cache
248 final UserServices<V> user = findOrCreateUserLocked(userId);
249 if (user.services == null) {
250 generateServicesMap(userId);
251 }
252 return user.services.get(type);
Fred Quintana718d8a22009-04-29 17:53:20 -0700253 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700254 }
255
256 /**
257 * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
258 * registered authenticators.
259 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700260 public Collection<ServiceInfo<V>> getAllServices(int userId) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800261 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700262 // Find user and lazily populate cache
263 final UserServices<V> user = findOrCreateUserLocked(userId);
264 if (user.services == null) {
265 generateServicesMap(userId);
266 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700267 return Collections.unmodifiableCollection(
268 new ArrayList<ServiceInfo<V>>(user.services.values()));
Fred Quintana718d8a22009-04-29 17:53:20 -0700269 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700270 }
271
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800272 private boolean inSystemImage(int callerUid) {
273 String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
274 for (String name : packages) {
275 try {
276 PackageInfo packageInfo =
277 mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
278 if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
279 return true;
280 }
281 } catch (PackageManager.NameNotFoundException e) {
282 return false;
283 }
284 }
285 return false;
286 }
287
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700288 /**
289 * Populate {@link UserServices#services} by scanning installed packages for
290 * given {@link UserHandle}.
291 */
292 private void generateServicesMap(int userId) {
293 Slog.d(TAG, "generateServicesMap() for " + userId);
294
295 final PackageManager pm = mContext.getPackageManager();
296 final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
297 final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(
298 new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);
Fred Quintana718d8a22009-04-29 17:53:20 -0700299 for (ResolveInfo resolveInfo : resolveInfos) {
300 try {
301 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800302 if (info == null) {
303 Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
304 continue;
Fred Quintana718d8a22009-04-29 17:53:20 -0700305 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800306 serviceInfos.add(info);
Fred Quintana718d8a22009-04-29 17:53:20 -0700307 } catch (XmlPullParserException e) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800308 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
Fred Quintana718d8a22009-04-29 17:53:20 -0700309 } catch (IOException e) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800310 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
Fred Quintana718d8a22009-04-29 17:53:20 -0700311 }
312 }
313
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800314 synchronized (mServicesLock) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700315 final UserServices<V> user = findOrCreateUserLocked(userId);
316 final boolean firstScan = user.services == null;
317 if (firstScan) {
318 user.services = Maps.newHashMap();
319 } else {
320 user.services.clear();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800321 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700322
Alon Albert3fa51e32010-11-11 09:24:04 -0800323 StringBuilder changes = new StringBuilder();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800324 for (ServiceInfo<V> info : serviceInfos) {
325 // four cases:
326 // - doesn't exist yet
327 // - add, notify user that it was added
328 // - exists and the UID is the same
329 // - replace, don't notify user
330 // - exists, the UID is different, and the new one is not a system package
331 // - ignore
332 // - exists, the UID is different, and the new one is a system package
333 // - add, notify user that it was added
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700334 Integer previousUid = user.persistentServices.get(info.type);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800335 if (previousUid == null) {
Alon Albert3fa51e32010-11-11 09:24:04 -0800336 changes.append(" New service added: ").append(info).append("\n");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700337 user.services.put(info.type, info);
338 user.persistentServices.put(info.type, info.uid);
339 if (!(mPersistentServicesFileDidNotExist && firstScan)) {
340 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800341 }
342 } else if (previousUid == info.uid) {
343 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Alon Albert3fa51e32010-11-11 09:24:04 -0800344 changes.append(" Existing service (nop): ").append(info).append("\n");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800345 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700346 user.services.put(info.type, info);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800347 } else if (inSystemImage(info.uid)
348 || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
Alon Albert3fa51e32010-11-11 09:24:04 -0800349 if (inSystemImage(info.uid)) {
350 changes.append(" System service replacing existing: ").append(info)
351 .append("\n");
352 } else {
353 changes.append(" Existing service replacing a removed service: ")
354 .append(info).append("\n");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800355 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700356 user.services.put(info.type, info);
357 user.persistentServices.put(info.type, info.uid);
358 notifyListener(info.type, userId, false /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800359 } else {
360 // ignore
Alon Albert3fa51e32010-11-11 09:24:04 -0800361 changes.append(" Existing service with new uid ignored: ").append(info)
362 .append("\n");
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800363 }
364 }
365
366 ArrayList<V> toBeRemoved = Lists.newArrayList();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700367 for (V v1 : user.persistentServices.keySet()) {
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800368 if (!containsType(serviceInfos, v1)) {
369 toBeRemoved.add(v1);
370 }
371 }
372 for (V v1 : toBeRemoved) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700373 user.persistentServices.remove(v1);
Alon Albert3fa51e32010-11-11 09:24:04 -0800374 changes.append(" Service removed: ").append(v1).append("\n");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700375 notifyListener(v1, userId, true /* removed */);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800376 }
Alon Albert3fa51e32010-11-11 09:24:04 -0800377 if (changes.length() > 0) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700378 if (Log.isLoggable(TAG, Log.VERBOSE)) {
379 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
380 serviceInfos.size() + " services:\n" + changes);
381 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800382 writePersistentServicesLocked();
383 } else {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700384 if (Log.isLoggable(TAG, Log.VERBOSE)) {
385 Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
386 serviceInfos.size() + " services unchanged");
387 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800388 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800389 }
390 }
391
392 private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
393 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
394 if (serviceInfos.get(i).type.equals(type)) {
395 return true;
396 }
397 }
398
399 return false;
400 }
401
402 private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
403 for (int i = 0, N = serviceInfos.size(); i < N; i++) {
404 final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
405 if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
406 return true;
407 }
408 }
409
410 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -0700411 }
412
413 private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
414 throws XmlPullParserException, IOException {
415 android.content.pm.ServiceInfo si = service.serviceInfo;
416 ComponentName componentName = new ComponentName(si.packageName, si.name);
417
418 PackageManager pm = mContext.getPackageManager();
419
420 XmlResourceParser parser = null;
421 try {
422 parser = si.loadXmlMetaData(pm, mMetaDataName);
423 if (parser == null) {
424 throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
425 }
426
427 AttributeSet attrs = Xml.asAttributeSet(parser);
428
429 int type;
430 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
431 && type != XmlPullParser.START_TAG) {
432 }
433
434 String nodeName = parser.getName();
435 if (!mAttributesName.equals(nodeName)) {
436 throw new XmlPullParserException(
437 "Meta-data does not start with " + mAttributesName + " tag");
438 }
439
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800440 V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
441 si.packageName, attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700442 if (v == null) {
443 return null;
444 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700445 final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
446 final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
447 final int uid = applicationInfo.uid;
448 return new ServiceInfo<V>(v, componentName, uid);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800449 } catch (NameNotFoundException e) {
450 throw new XmlPullParserException(
451 "Unable to load resources for pacakge " + si.packageName);
Fred Quintana718d8a22009-04-29 17:53:20 -0700452 } finally {
453 if (parser != null) parser.close();
454 }
455 }
456
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800457 /**
458 * Read all sync status back in to the initial engine state.
459 */
460 private void readPersistentServicesLocked() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700461 mUserServices.clear();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800462 if (mSerializerAndParser == null) {
463 return;
464 }
465 FileInputStream fis = null;
466 try {
467 mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
468 if (mPersistentServicesFileDidNotExist) {
469 return;
470 }
471 fis = mPersistentServicesFile.openRead();
472 XmlPullParser parser = Xml.newPullParser();
473 parser.setInput(fis, null);
474 int eventType = parser.getEventType();
475 while (eventType != XmlPullParser.START_TAG) {
476 eventType = parser.next();
477 }
478 String tagName = parser.getName();
479 if ("services".equals(tagName)) {
480 eventType = parser.next();
481 do {
482 if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
483 tagName = parser.getName();
484 if ("service".equals(tagName)) {
485 V service = mSerializerAndParser.createFromXml(parser);
486 if (service == null) {
487 break;
488 }
489 String uidString = parser.getAttributeValue(null, "uid");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700490 final int uid = Integer.parseInt(uidString);
491 final int userId = UserHandle.getUserId(uid);
492 final UserServices<V> user = findOrCreateUserLocked(userId);
493 user.persistentServices.put(service, uid);
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800494 }
495 }
496 eventType = parser.next();
497 } while (eventType != XmlPullParser.END_DOCUMENT);
498 }
499 } catch (Exception e) {
500 Log.w(TAG, "Error reading persistent services, starting from scratch", e);
501 } finally {
502 if (fis != null) {
503 try {
504 fis.close();
505 } catch (java.io.IOException e1) {
506 }
507 }
508 }
509 }
510
511 /**
512 * Write all sync status to the sync status file.
513 */
514 private void writePersistentServicesLocked() {
515 if (mSerializerAndParser == null) {
516 return;
517 }
518 FileOutputStream fos = null;
519 try {
520 fos = mPersistentServicesFile.startWrite();
521 XmlSerializer out = new FastXmlSerializer();
522 out.setOutput(fos, "utf-8");
523 out.startDocument(null, true);
524 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
525 out.startTag(null, "services");
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700526 for (int i = 0; i < mUserServices.size(); i++) {
527 final UserServices<V> user = mUserServices.valueAt(i);
528 for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
529 out.startTag(null, "service");
530 out.attribute(null, "uid", Integer.toString(service.getValue()));
531 mSerializerAndParser.writeAsXml(service.getKey(), out);
532 out.endTag(null, "service");
533 }
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800534 }
535 out.endTag(null, "services");
536 out.endDocument();
537 mPersistentServicesFile.finishWrite(fos);
538 } catch (java.io.IOException e1) {
539 Log.w(TAG, "Error writing accounts", e1);
540 if (fos != null) {
541 mPersistentServicesFile.failWrite(fos);
542 }
543 }
544 }
545
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800546 public abstract V parseServiceAttributes(Resources res,
547 String packageName, AttributeSet attrs);
Fred Quintana718d8a22009-04-29 17:53:20 -0700548}