RegisteredServicesCache.java revision 2269d1572e5fcfb725ea55f5764d8c3280d69f6d
1718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana/*
2718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * Copyright (C) 2009 The Android Open Source Project
3718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *
4718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * Licensed under the Apache License, Version 2.0 (the "License");
5718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * you may not use this file except in compliance with the License.
6718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * You may obtain a copy of the License at
7718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *
8718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *      http://www.apache.org/licenses/LICENSE-2.0
9718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *
10718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * Unless required by applicable law or agreed to in writing, software
11718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * distributed under the License is distributed on an "AS IS" BASIS,
12718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * See the License for the specific language governing permissions and
14718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * limitations under the License.
15718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana */
16718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
17718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanapackage android.content.pm;
18718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
19718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.Context;
20718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.BroadcastReceiver;
21718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.Intent;
22718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.IntentFilter;
23718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.ComponentName;
24718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.res.XmlResourceParser;
255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport android.os.Environment;
265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport android.os.Handler;
27718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.Log;
28718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.AttributeSet;
29718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.Xml;
30718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
31718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.Map;
32718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.Collection;
33718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.Collections;
345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.util.HashMap;
35718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.List;
365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.util.ArrayList;
375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.util.concurrent.atomic.AtomicReference;
385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.File;
395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.FileOutputStream;
40718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.io.FileDescriptor;
41718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.io.PrintWriter;
42718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.io.IOException;
435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.FileInputStream;
445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport com.android.internal.os.AtomicFile;
462269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornimport com.android.internal.util.FastXmlSerializer;
47718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
48718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport com.google.android.collect.Maps;
495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport com.google.android.collect.Lists;
505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
51718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport org.xmlpull.v1.XmlPullParserException;
52718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport org.xmlpull.v1.XmlPullParser;
535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport org.xmlpull.v1.XmlSerializer;
54718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
55718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana/**
56718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * A cache of registered services. This cache
57718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * is built by interrogating the {@link PackageManager} and is updated as packages are added,
58718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * removed and changed. The services are referred to by type V and
59718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * are made available via the {@link #getServiceInfo} method.
60718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * @hide
61718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana */
62718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanapublic abstract class RegisteredServicesCache<V> {
63718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String TAG = "PackageManager";
64718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
65718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public final Context mContext;
66718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mInterfaceName;
67718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mMetaDataName;
68718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mAttributesName;
695ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final XmlSerializerAndParser<V> mSerializerAndParser;
705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final AtomicReference<BroadcastReceiver> mReceiver;
71718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final Object mServicesLock = new Object();
735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // synchronized on mServicesLock
745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private HashMap<V, Integer> mPersistentServices;
755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // synchronized on mServicesLock
765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private Map<V, ServiceInfo<V>> mServices;
775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // synchronized on mServicesLock
785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean mPersistentServicesFileDidNotExist;
793ecd5f437580e49d80beecd29489d5fb1f7a7db0Fred Quintana
805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * This file contains the list of known services. We would like to maintain this forever
825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * so we store it as an XML file.
835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final AtomicFile mPersistentServicesFile;
85718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // the listener and handler are synchronized on "this" and must be updated together
875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private RegisteredServicesCacheListener<V> mListener;
885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private Handler mHandler;
89718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
90718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
92718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mContext = context;
93718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mInterfaceName = interfaceName;
94718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mMetaDataName = metaDataName;
95718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mAttributesName = attributeName;
965ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mSerializerAndParser = serializerAndParser;
975ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
985ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File dataDir = Environment.getDataDirectory();
995ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File systemDir = new File(dataDir, "system");
1005ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File syncDir = new File(systemDir, "registered_services");
1015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
1025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        generateServicesMap();
1045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final BroadcastReceiver receiver = new BroadcastReceiver() {
1065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            @Override
1075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            public void onReceive(Context context1, Intent intent) {
1085ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                generateServicesMap();
1095ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
1105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        };
1115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
1125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        IntentFilter intentFilter = new IntentFilter();
1135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addDataScheme("package");
1175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mContext.registerReceiver(receiver, intentFilter);
11808675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        // Register for events related to sdcard installation.
11908675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        IntentFilter sdFilter = new IntentFilter();
120b56ae20b22fd7283df32072a431ab6d4965f3c1bSuchi Amalapurapu        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
121b56ae20b22fd7283df32072a431ab6d4965f3c1bSuchi Amalapurapu        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
12208675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        mContext.registerReceiver(receiver, sdFilter);
123718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
124718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
125718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
1265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        Map<V, ServiceInfo<V>> services;
1275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
1285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            services = mServices;
1295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
130718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        fout.println("RegisteredServicesCache: " + services.size() + " services");
131718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        for (ServiceInfo info : services.values()) {
132718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            fout.println("  " + info);
133718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
134718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
135718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    public RegisteredServicesCacheListener<V> getListener() {
137718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        synchronized (this) {
1385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return mListener;
1395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
141718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
1435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (handler == null) {
1445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            handler = new Handler(mContext.getMainLooper());
1455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (this) {
1475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mHandler = handler;
1485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mListener = listener;
149718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
150718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
151718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void notifyListener(final V type, final boolean removed) {
1535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
1555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        RegisteredServicesCacheListener<V> listener;
1575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        Handler handler;
158718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        synchronized (this) {
1595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            listener = mListener;
1605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            handler = mHandler;
1615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (listener == null) {
1635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
164718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
1655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1665ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final RegisteredServicesCacheListener<V> listener2 = listener;
1675ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        handler.post(new Runnable() {
1685ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            public void run() {
1695ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                listener2.onServiceChanged(type, removed);
1705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
1715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        });
172718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
173718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
174718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
175718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Value type that describes a Service. The information within can be used
176718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * to bind to the service.
177718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
178718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public static class ServiceInfo<V> {
179718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public final V type;
180718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public final ComponentName componentName;
181d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana        public final int uid;
182718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
183d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana        private ServiceInfo(V type, ComponentName componentName, int uid) {
184718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            this.type = type;
185718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            this.componentName = componentName;
186d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            this.uid = uid;
187718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
188718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
189c42c0dd1c4e2f7a4abaac1b2c9a6344448f9db7aJeff Hamilton        @Override
190718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public String toString() {
1915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
192718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
193718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
194718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
195718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
196718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Accessor for the registered authenticators.
197718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @param type the account type of the authenticator
198718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @return the AuthenticatorInfo that matches the account type or null if none is present
199718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
2009788976b1465ce982b5ae7c741345edd0ecd9322Fred Quintana    public ServiceInfo<V> getServiceInfo(V type) {
2015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return mServices.get(type);
203718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
204718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
205718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
206718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
207718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
208718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * registered authenticators.
209718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
210718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public Collection<ServiceInfo<V>> getAllServices() {
2115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return Collections.unmodifiableCollection(mServices.values());
213718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
214718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
215718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
216718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
217718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Stops the monitoring of package additions, removals and changes.
218718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
219718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public void close() {
2205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final BroadcastReceiver receiver = mReceiver.getAndSet(null);
2215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (receiver != null) {
2225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mContext.unregisterReceiver(receiver);
2235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
224718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
225718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
226c42c0dd1c4e2f7a4abaac1b2c9a6344448f9db7aJeff Hamilton    @Override
227718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    protected void finalize() throws Throwable {
2285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mReceiver.get() != null) {
2295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.e(TAG, "RegisteredServicesCache finalized without being closed");
230718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
231718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        close();
232718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        super.finalize();
233718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
234718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2355ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean inSystemImage(int callerUid) {
2365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
2375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (String name : packages) {
2385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            try {
2395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                PackageInfo packageInfo =
2405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
2415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
2425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    return true;
2435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
2445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            } catch (PackageManager.NameNotFoundException e) {
2455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return false;
2465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
2485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
2495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
250718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    void generateServicesMap() {
2525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        PackageManager pm = mContext.getPackageManager();
2535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
2545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName),
2555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                PackageManager.GET_META_DATA);
256718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        for (ResolveInfo resolveInfo : resolveInfos) {
257718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            try {
258718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                ServiceInfo<V> info = parseServiceInfo(resolveInfo);
2595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (info == null) {
2605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
2615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    continue;
262718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                }
2635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                serviceInfos.add(info);
264718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            } catch (XmlPullParserException e) {
2655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
266718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            } catch (IOException e) {
2675ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
268718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
269718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
270718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (Log.isLoggable(TAG, Log.VERBOSE)) {
2735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.d(TAG, "generateServicesMap: " + mInterfaceName);
2745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (mPersistentServices == null) {
2765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                readPersistentServicesLocked();
2775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mServices = Maps.newHashMap();
2795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            boolean changed = false;
2805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (Log.isLoggable(TAG, Log.VERBOSE)) {
2815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.d(TAG, "found " + serviceInfos.size() + " services");
2825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (ServiceInfo<V> info : serviceInfos) {
2845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // four cases:
2855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - doesn't exist yet
2865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - add, notify user that it was added
2875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists and the UID is the same
2885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - replace, don't notify user
2895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists, the UID is different, and the new one is not a system package
2905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - ignore
2915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists, the UID is different, and the new one is a system package
2925ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - add, notify user that it was added
2935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Integer previousUid = mPersistentServices.get(info.type);
2945ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (previousUid == null) {
2955ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
2965ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        Log.d(TAG, "encountered new type: " + info);
2975ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
2985ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    changed = true;
2995ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mServices.put(info.type, info);
3005ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mPersistentServices.put(info.type, info.uid);
3015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (!mPersistentServicesFileDidNotExist) {
3025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        notifyListener(info.type, false /* removed */);
3035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else if (previousUid == info.uid) {
3055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        Log.d(TAG, "encountered existing type with the same uid: " + info);
3075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3085ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mServices.put(info.type, info);
3095ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else if (inSystemImage(info.uid)
3105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
3115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        if (inSystemImage(info.uid)) {
3135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            Log.d(TAG, "encountered existing type with a new uid but from"
3145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                                    + " the system: " + info);
3155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        } else {
3165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            Log.d(TAG, "encountered existing type with a new uid but existing was"
3175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                                    + " removed: " + info);
3185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        }
3195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    changed = true;
3215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mServices.put(info.type, info);
3225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mPersistentServices.put(info.type, info.uid);
3235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    notifyListener(info.type, false /* removed */);
3245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else {
3255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    // ignore
3265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        Log.d(TAG, "encountered existing type with a new uid, ignoring: " + info);
3285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            ArrayList<V> toBeRemoved = Lists.newArrayList();
3335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (V v1 : mPersistentServices.keySet()) {
3345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (!containsType(serviceInfos, v1)) {
3355ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    toBeRemoved.add(v1);
3365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (V v1 : toBeRemoved) {
3395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mPersistentServices.remove(v1);
3405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                changed = true;
3415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                notifyListener(v1, true /* removed */);
3425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (changed) {
3445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    Log.d(TAG, "writing updated list of persistent services");
3465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                writePersistentServicesLocked();
3485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            } else {
3495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    Log.d(TAG, "persistent services did not change, so not writing anything");
3515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFileDidNotExist = false;
3545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
3555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
3565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
3585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
3595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (serviceInfos.get(i).type.equals(type)) {
3605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return true;
3615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
3635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3645ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
3655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
3665ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3675ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
3685ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
3695ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
3705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
3715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return true;
3725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
3745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
376718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
377718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
378718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
379718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            throws XmlPullParserException, IOException {
380718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        android.content.pm.ServiceInfo si = service.serviceInfo;
381718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        ComponentName componentName = new ComponentName(si.packageName, si.name);
382718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
383718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        PackageManager pm = mContext.getPackageManager();
384718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
385718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        XmlResourceParser parser = null;
386718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        try {
387718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            parser = si.loadXmlMetaData(pm, mMetaDataName);
388718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (parser == null) {
389718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
390718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
391718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
392718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            AttributeSet attrs = Xml.asAttributeSet(parser);
393718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
394718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            int type;
395718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
396718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                    && type != XmlPullParser.START_TAG) {
397718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
398718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
399718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            String nodeName = parser.getName();
400718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (!mAttributesName.equals(nodeName)) {
401718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                throw new XmlPullParserException(
402718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                        "Meta-data does not start with " + mAttributesName +  " tag");
403718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
404718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
4059788976b1465ce982b5ae7c741345edd0ecd9322Fred Quintana            V v = parseServiceAttributes(si.packageName, attrs);
406718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (v == null) {
407718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                return null;
408718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
409d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
410d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
411d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final int uid = applicationInfo.uid;
412d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            return new ServiceInfo<V>(v, componentName, uid);
413718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        } finally {
414718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (parser != null) parser.close();
415718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
416718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
417718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
4185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
4195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * Read all sync status back in to the initial engine state.
4205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
4215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void readPersistentServicesLocked() {
4225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mPersistentServices = Maps.newHashMap();
4235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mSerializerAndParser == null) {
4245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
4255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        FileInputStream fis = null;
4275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        try {
4285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
4295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (mPersistentServicesFileDidNotExist) {
4305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return;
4315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            fis = mPersistentServicesFile.openRead();
4335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            XmlPullParser parser = Xml.newPullParser();
4345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            parser.setInput(fis, null);
4355ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            int eventType = parser.getEventType();
4365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            while (eventType != XmlPullParser.START_TAG) {
4375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                eventType = parser.next();
4385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            String tagName = parser.getName();
4405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if ("services".equals(tagName)) {
4415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                eventType = parser.next();
4425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                do {
4435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
4445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        tagName = parser.getName();
4455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        if ("service".equals(tagName)) {
4465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            V service = mSerializerAndParser.createFromXml(parser);
4475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            if (service == null) {
4485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                                break;
4495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            }
4505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            String uidString = parser.getAttributeValue(null, "uid");
4515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            int uid = Integer.parseInt(uidString);
4525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            mPersistentServices.put(service, uid);
4535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        }
4545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
4555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    eventType = parser.next();
4565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } while (eventType != XmlPullParser.END_DOCUMENT);
4575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } catch (Exception e) {
4595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.w(TAG, "Error reading persistent services, starting from scratch", e);
4605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } finally {
4615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (fis != null) {
4625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                try {
4635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    fis.close();
4645ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } catch (java.io.IOException e1) {
4655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
4665ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4675ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4685ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
4695ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
4705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
4715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * Write all sync status to the sync status file.
4725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
4735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void writePersistentServicesLocked() {
4745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mSerializerAndParser == null) {
4755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
4765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        FileOutputStream fos = null;
4785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        try {
4795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            fos = mPersistentServicesFile.startWrite();
4805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            XmlSerializer out = new FastXmlSerializer();
4815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.setOutput(fos, "utf-8");
4825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.startDocument(null, true);
4835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
4845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.startTag(null, "services");
4855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) {
4865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                out.startTag(null, "service");
4875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                out.attribute(null, "uid", Integer.toString(service.getValue()));
4885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mSerializerAndParser.writeAsXml(service.getKey(), out);
4895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                out.endTag(null, "service");
4905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.endTag(null, "services");
4925ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.endDocument();
4935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFile.finishWrite(fos);
4945ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } catch (java.io.IOException e1) {
4955ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.w(TAG, "Error writing accounts", e1);
4965ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (fos != null) {
4975ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mPersistentServicesFile.failWrite(fos);
4985ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4995ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
5005ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
5015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
5029788976b1465ce982b5ae7c741345edd0ecd9322Fred Quintana    public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
503718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana}
504