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;
2420cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackbornimport android.content.pm.PackageManager.NameNotFoundException;
2520cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackbornimport android.content.res.Resources;
26718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.res.XmlResourceParser;
275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport android.os.Environment;
285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport android.os.Handler;
29718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.Log;
30718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.AttributeSet;
31718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.Xml;
32718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
33718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.Map;
34718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.Collection;
35718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.Collections;
365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.util.HashMap;
37718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.util.List;
385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.util.ArrayList;
395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.util.concurrent.atomic.AtomicReference;
405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.File;
415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.FileOutputStream;
42718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.io.FileDescriptor;
43718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.io.PrintWriter;
44718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport java.io.IOException;
455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.FileInputStream;
465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport com.android.internal.os.AtomicFile;
482269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornimport com.android.internal.util.FastXmlSerializer;
49718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
50718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport com.google.android.collect.Maps;
515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport com.google.android.collect.Lists;
525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
53718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport org.xmlpull.v1.XmlPullParserException;
54718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport org.xmlpull.v1.XmlPullParser;
555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport org.xmlpull.v1.XmlSerializer;
56718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
57718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana/**
58718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * A cache of registered services. This cache
59718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * is built by interrogating the {@link PackageManager} and is updated as packages are added,
60718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * removed and changed. The services are referred to by type V and
61718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * are made available via the {@link #getServiceInfo} method.
62718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * @hide
63718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana */
64718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanapublic abstract class RegisteredServicesCache<V> {
65718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String TAG = "PackageManager";
66718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
67718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public final Context mContext;
68718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mInterfaceName;
69718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mMetaDataName;
70718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mAttributesName;
715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final XmlSerializerAndParser<V> mSerializerAndParser;
725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final AtomicReference<BroadcastReceiver> mReceiver;
73718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final Object mServicesLock = new Object();
755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // synchronized on mServicesLock
765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private HashMap<V, Integer> mPersistentServices;
775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // synchronized on mServicesLock
785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private Map<V, ServiceInfo<V>> mServices;
795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // synchronized on mServicesLock
805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean mPersistentServicesFileDidNotExist;
813ecd5f437580e49d80beecd29489d5fb1f7a7db0Fred Quintana
825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * This file contains the list of known services. We would like to maintain this forever
845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * so we store it as an XML file.
855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final AtomicFile mPersistentServicesFile;
87718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // the listener and handler are synchronized on "this" and must be updated together
895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private RegisteredServicesCacheListener<V> mListener;
905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private Handler mHandler;
91718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
92718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
94718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mContext = context;
95718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mInterfaceName = interfaceName;
96718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mMetaDataName = metaDataName;
97718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mAttributesName = attributeName;
985ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mSerializerAndParser = serializerAndParser;
995ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1005ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File dataDir = Environment.getDataDirectory();
1015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File systemDir = new File(dataDir, "system");
1025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File syncDir = new File(systemDir, "registered_services");
1035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
1045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        generateServicesMap();
1065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final BroadcastReceiver receiver = new BroadcastReceiver() {
1085ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            @Override
1095ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            public void onReceive(Context context1, Intent intent) {
1105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                generateServicesMap();
1115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
1125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        };
1135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
1145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        IntentFilter intentFilter = new IntentFilter();
1155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addDataScheme("package");
1195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mContext.registerReceiver(receiver, intentFilter);
12008675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        // Register for events related to sdcard installation.
12108675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        IntentFilter sdFilter = new IntentFilter();
122b56ae20b22fd7283df32072a431ab6d4965f3c1bSuchi Amalapurapu        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
123b56ae20b22fd7283df32072a431ab6d4965f3c1bSuchi Amalapurapu        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
12408675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        mContext.registerReceiver(receiver, sdFilter);
125718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
126718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
127718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
1285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        Map<V, ServiceInfo<V>> services;
1295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
1305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            services = mServices;
1315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
132718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        fout.println("RegisteredServicesCache: " + services.size() + " services");
133718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        for (ServiceInfo info : services.values()) {
134718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            fout.println("  " + info);
135718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
136718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
137718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    public RegisteredServicesCacheListener<V> getListener() {
139718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        synchronized (this) {
1405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return mListener;
1415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
143718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
1455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (handler == null) {
1465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            handler = new Handler(mContext.getMainLooper());
1475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (this) {
1495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mHandler = handler;
1505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mListener = listener;
151718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
152718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
153718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void notifyListener(final V type, final boolean removed) {
1555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
1575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        RegisteredServicesCacheListener<V> listener;
1595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        Handler handler;
160718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        synchronized (this) {
1615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            listener = mListener;
1625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            handler = mHandler;
1635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1645ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (listener == null) {
1655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
166718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
1675ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1685ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final RegisteredServicesCacheListener<V> listener2 = listener;
1695ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        handler.post(new Runnable() {
1705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            public void run() {
1715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                listener2.onServiceChanged(type, removed);
1725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
1735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        });
174718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
175718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
176718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
177718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Value type that describes a Service. The information within can be used
178718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * to bind to the service.
179718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
180718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public static class ServiceInfo<V> {
181718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public final V type;
182718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public final ComponentName componentName;
183d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana        public final int uid;
184718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
18556285a60e83138bb4b4f2d3bdec91b2f3ca11aa2Fred Quintana        /** @hide */
18656285a60e83138bb4b4f2d3bdec91b2f3ca11aa2Fred Quintana        public ServiceInfo(V type, ComponentName componentName, int uid) {
187718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            this.type = type;
188718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            this.componentName = componentName;
189d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            this.uid = uid;
190718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
191718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
192c42c0dd1c4e2f7a4abaac1b2c9a6344448f9db7aJeff Hamilton        @Override
193718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public String toString() {
1945ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
195718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
196718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
197718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
198718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
199718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Accessor for the registered authenticators.
200718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @param type the account type of the authenticator
201718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @return the AuthenticatorInfo that matches the account type or null if none is present
202718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
2039788976b1465ce982b5ae7c741345edd0ecd9322Fred Quintana    public ServiceInfo<V> getServiceInfo(V type) {
2045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return mServices.get(type);
206718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
207718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
208718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
209718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
210718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
211718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * registered authenticators.
212718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
213718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public Collection<ServiceInfo<V>> getAllServices() {
2145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return Collections.unmodifiableCollection(mServices.values());
216718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
217718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
218718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
219718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
220718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Stops the monitoring of package additions, removals and changes.
221718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
222718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public void close() {
2235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final BroadcastReceiver receiver = mReceiver.getAndSet(null);
2245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (receiver != null) {
2255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mContext.unregisterReceiver(receiver);
2265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
227718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
228718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
229c42c0dd1c4e2f7a4abaac1b2c9a6344448f9db7aJeff Hamilton    @Override
230718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    protected void finalize() throws Throwable {
2315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mReceiver.get() != null) {
2325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.e(TAG, "RegisteredServicesCache finalized without being closed");
233718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
234718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        close();
235718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        super.finalize();
236718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
237718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean inSystemImage(int callerUid) {
2395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
2405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (String name : packages) {
2415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            try {
2425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                PackageInfo packageInfo =
2435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
2445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
2455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    return true;
2465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
2475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            } catch (PackageManager.NameNotFoundException e) {
2485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return false;
2495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
2515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
2525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
253718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    void generateServicesMap() {
2555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        PackageManager pm = mContext.getPackageManager();
2565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
2575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName),
2585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                PackageManager.GET_META_DATA);
259718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        for (ResolveInfo resolveInfo : resolveInfos) {
260718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            try {
261718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                ServiceInfo<V> info = parseServiceInfo(resolveInfo);
2625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (info == null) {
2635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
2645ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    continue;
265718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                }
2665ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                serviceInfos.add(info);
267718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            } catch (XmlPullParserException e) {
2685ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
269718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            } catch (IOException e) {
2705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
271718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
272718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
273718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (mPersistentServices == null) {
2765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                readPersistentServicesLocked();
2775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mServices = Maps.newHashMap();
2793fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert            StringBuilder changes = new StringBuilder();
2805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (ServiceInfo<V> info : serviceInfos) {
2815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // four cases:
2825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - doesn't exist yet
2835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - add, notify user that it was added
2845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists and the UID is the same
2855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - replace, don't notify user
2865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists, the UID is different, and the new one is not a system package
2875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - ignore
2885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists, the UID is different, and the new one is a system package
2895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - add, notify user that it was added
2905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Integer previousUid = mPersistentServices.get(info.type);
2915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (previousUid == null) {
2923fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                    changes.append("  New service added: ").append(info).append("\n");
2935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mServices.put(info.type, info);
2945ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mPersistentServices.put(info.type, info.uid);
2955ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (!mPersistentServicesFileDidNotExist) {
2965ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        notifyListener(info.type, false /* removed */);
2975ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
2985ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else if (previousUid == info.uid) {
2995ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3003fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                        changes.append("  Existing service (nop): ").append(info).append("\n");
3015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mServices.put(info.type, info);
3035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else if (inSystemImage(info.uid)
3045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
3053fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                    if (inSystemImage(info.uid)) {
3063fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                        changes.append("  System service replacing existing: ").append(info)
3073fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                                .append("\n");
3083fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                    } else {
3093fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                        changes.append("  Existing service replacing a removed service: ")
3103fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                                .append(info).append("\n");
3115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mServices.put(info.type, info);
3135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    mPersistentServices.put(info.type, info.uid);
3145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    notifyListener(info.type, false /* removed */);
3155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else {
3165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    // ignore
3173fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                    changes.append("  Existing service with new uid ignored: ").append(info)
3183fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                            .append("\n");
3195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            ArrayList<V> toBeRemoved = Lists.newArrayList();
3235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (V v1 : mPersistentServices.keySet()) {
3245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (!containsType(serviceInfos, v1)) {
3255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    toBeRemoved.add(v1);
3265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (V v1 : toBeRemoved) {
3295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mPersistentServices.remove(v1);
3303fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                changes.append("  Service removed: ").append(v1).append("\n");
3315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                notifyListener(v1, true /* removed */);
3325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3333fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert            if (changes.length() > 0) {
3343fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
3353fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                        serviceInfos.size() + " services:\n" + changes);
3365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                writePersistentServicesLocked();
3375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            } else {
3383fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
3393fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                        serviceInfos.size() + " services unchanged");
3405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFileDidNotExist = false;
3425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
3435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
3445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
3465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
3475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (serviceInfos.get(i).type.equals(type)) {
3485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return true;
3495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
3515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
3535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
3545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
3565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
3575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
3585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
3595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return true;
3605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
3625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
364718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
365718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
366718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
367718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            throws XmlPullParserException, IOException {
368718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        android.content.pm.ServiceInfo si = service.serviceInfo;
369718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        ComponentName componentName = new ComponentName(si.packageName, si.name);
370718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
371718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        PackageManager pm = mContext.getPackageManager();
372718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
373718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        XmlResourceParser parser = null;
374718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        try {
375718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            parser = si.loadXmlMetaData(pm, mMetaDataName);
376718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (parser == null) {
377718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
378718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
379718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
380718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            AttributeSet attrs = Xml.asAttributeSet(parser);
381718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
382718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            int type;
383718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
384718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                    && type != XmlPullParser.START_TAG) {
385718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
386718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
387718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            String nodeName = parser.getName();
388718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (!mAttributesName.equals(nodeName)) {
389718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                throw new XmlPullParserException(
390718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                        "Meta-data does not start with " + mAttributesName +  " tag");
391718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
392718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
39320cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
39420cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn                    si.packageName, attrs);
395718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (v == null) {
396718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                return null;
397718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
398d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
399d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
400d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final int uid = applicationInfo.uid;
401d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            return new ServiceInfo<V>(v, componentName, uid);
40220cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn        } catch (NameNotFoundException e) {
40320cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            throw new XmlPullParserException(
40420cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn                    "Unable to load resources for pacakge " + si.packageName);
405718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        } finally {
406718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (parser != null) parser.close();
407718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
408718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
409718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
4105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
4115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * Read all sync status back in to the initial engine state.
4125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
4135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void readPersistentServicesLocked() {
4145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mPersistentServices = Maps.newHashMap();
4155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mSerializerAndParser == null) {
4165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
4175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        FileInputStream fis = null;
4195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        try {
4205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
4215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (mPersistentServicesFileDidNotExist) {
4225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return;
4235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            fis = mPersistentServicesFile.openRead();
4255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            XmlPullParser parser = Xml.newPullParser();
4265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            parser.setInput(fis, null);
4275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            int eventType = parser.getEventType();
4285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            while (eventType != XmlPullParser.START_TAG) {
4295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                eventType = parser.next();
4305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            String tagName = parser.getName();
4325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if ("services".equals(tagName)) {
4335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                eventType = parser.next();
4345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                do {
4355ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
4365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        tagName = parser.getName();
4375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        if ("service".equals(tagName)) {
4385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            V service = mSerializerAndParser.createFromXml(parser);
4395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            if (service == null) {
4405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                                break;
4415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            }
4425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            String uidString = parser.getAttributeValue(null, "uid");
4435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            int uid = Integer.parseInt(uidString);
4445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            mPersistentServices.put(service, uid);
4455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        }
4465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
4475ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    eventType = parser.next();
4485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } while (eventType != XmlPullParser.END_DOCUMENT);
4495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } catch (Exception e) {
4515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.w(TAG, "Error reading persistent services, starting from scratch", e);
4525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } finally {
4535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (fis != null) {
4545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                try {
4555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    fis.close();
4565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } catch (java.io.IOException e1) {
4575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
4585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
4615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
4625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
4635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * Write all sync status to the sync status file.
4645ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
4655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void writePersistentServicesLocked() {
4665ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mSerializerAndParser == null) {
4675ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
4685ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4695ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        FileOutputStream fos = null;
4705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        try {
4715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            fos = mPersistentServicesFile.startWrite();
4725ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            XmlSerializer out = new FastXmlSerializer();
4735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.setOutput(fos, "utf-8");
4745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.startDocument(null, true);
4755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
4765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.startTag(null, "services");
4775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) {
4785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                out.startTag(null, "service");
4795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                out.attribute(null, "uid", Integer.toString(service.getValue()));
4805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mSerializerAndParser.writeAsXml(service.getKey(), out);
4815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                out.endTag(null, "service");
4825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.endTag(null, "services");
4845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.endDocument();
4855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFile.finishWrite(fos);
4865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } catch (java.io.IOException e1) {
4875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.w(TAG, "Error writing accounts", e1);
4885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (fos != null) {
4895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mPersistentServicesFile.failWrite(fos);
4905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4925ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
4935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
49420cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn    public abstract V parseServiceAttributes(Resources res,
49520cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            String packageName, AttributeSet attrs);
496718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana}
497