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.BroadcastReceiver;
206ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport android.content.ComponentName;
216ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport android.content.Context;
22718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.Intent;
23718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.IntentFilter;
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;
296ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport android.os.UserHandle;
3039606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
31718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.AttributeSet;
326ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport android.util.Log;
336ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport android.util.Slog;
346ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport android.util.SparseArray;
35718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.Xml;
36718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
378b2c3a14603d163d7564e6f60286995079687690Jeff Sharkeyimport com.android.internal.annotations.GuardedBy;
382269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornimport com.android.internal.util.FastXmlSerializer;
395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport com.google.android.collect.Lists;
406ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport com.google.android.collect.Maps;
415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
42718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport org.xmlpull.v1.XmlPullParser;
436ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport org.xmlpull.v1.XmlPullParserException;
445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport org.xmlpull.v1.XmlSerializer;
45718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
466ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.io.File;
476ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.io.FileDescriptor;
486ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.io.FileInputStream;
496ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.io.FileOutputStream;
506ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.io.IOException;
516ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.io.PrintWriter;
526ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.util.ArrayList;
536ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.util.Collection;
546ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.util.Collections;
556ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.util.List;
566ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkeyimport java.util.Map;
576ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
58718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana/**
596ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * Cache of registered services. This cache is lazily built by interrogating
606ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * {@link PackageManager} on a per-user basis. It's updated as packages are
616ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * added, removed and changed. Users are responsible for calling
626ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * {@link #invalidateCache(int)} when a user is started, since
636ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * {@link PackageManager} broadcasts aren't sent for stopped users.
646ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * <p>
656ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * The services are referred to by type V and are made available via the
666ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey * {@link #getServiceInfo} method.
676ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey *
68718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * @hide
69718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana */
70718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanapublic abstract class RegisteredServicesCache<V> {
71718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String TAG = "PackageManager";
7240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn    private static final boolean DEBUG = false;
73718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
74718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public final Context mContext;
75718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mInterfaceName;
76718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mMetaDataName;
77718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private final String mAttributesName;
785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final XmlSerializerAndParser<V> mSerializerAndParser;
79718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final Object mServicesLock = new Object();
816ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
828b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey    @GuardedBy("mServicesLock")
835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean mPersistentServicesFileDidNotExist;
848b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey    @GuardedBy("mServicesLock")
85f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackborn    private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
866ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
876ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    private static class UserServices<V> {
888b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey        @GuardedBy("mServicesLock")
896ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        public final Map<V, Integer> persistentServices = Maps.newHashMap();
908b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey        @GuardedBy("mServicesLock")
916ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        public Map<V, ServiceInfo<V>> services = null;
926ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    }
936ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
946ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    private UserServices<V> findOrCreateUserLocked(int userId) {
956ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        UserServices<V> services = mUserServices.get(userId);
966ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        if (services == null) {
976ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            services = new UserServices<V>();
986ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            mUserServices.put(userId, services);
996ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        }
1006ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        return services;
1016ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    }
1023ecd5f437580e49d80beecd29489d5fb1f7a7db0Fred Quintana
1035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
1045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * This file contains the list of known services. We would like to maintain this forever
1055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * so we store it as an XML file.
1065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
1075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private final AtomicFile mPersistentServicesFile;
108718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1095ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    // the listener and handler are synchronized on "this" and must be updated together
1105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private RegisteredServicesCacheListener<V> mListener;
1115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private Handler mHandler;
112718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
113718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
1145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
115718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mContext = context;
116718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mInterfaceName = interfaceName;
117718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mMetaDataName = metaDataName;
118718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        mAttributesName = attributeName;
1195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mSerializerAndParser = serializerAndParser;
1205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File dataDir = Environment.getDataDirectory();
1225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File systemDir = new File(dataDir, "system");
1235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        File syncDir = new File(systemDir, "registered_services");
1245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
1255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1266ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        // Load persisted services from disk
1276ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        readPersistentServicesLocked();
1285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
1295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        IntentFilter intentFilter = new IntentFilter();
1305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        intentFilter.addDataScheme("package");
1346ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
1356ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
13608675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        // Register for events related to sdcard installation.
13708675a3376819a82aa5ab344bc3e7b1635c30b05Suchi Amalapurapu        IntentFilter sdFilter = new IntentFilter();
138b56ae20b22fd7283df32072a431ab6d4965f3c1bSuchi Amalapurapu        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
139b56ae20b22fd7283df32072a431ab6d4965f3c1bSuchi Amalapurapu        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1406ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        mContext.registerReceiver(mExternalReceiver, sdFilter);
141718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
142718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1436ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
1446ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        @Override
1456ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        public void onReceive(Context context, Intent intent) {
1466ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1476ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            if (uid != -1) {
1486ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                generateServicesMap(UserHandle.getUserId(uid));
1496ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            }
1506ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        }
1516ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    };
1526ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
1536ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
1546ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        @Override
1556ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        public void onReceive(Context context, Intent intent) {
1566ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            // External apps can't coexist with multi-user, so scan owner
1576ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            generateServicesMap(UserHandle.USER_OWNER);
1586ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        }
1596ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    };
1606ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
1616ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    public void invalidateCache(int userId) {
1625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
1636ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final UserServices<V> user = findOrCreateUserLocked(userId);
1646ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            user.services = null;
1655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1666ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    }
1676ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
1686ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
1696ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        synchronized (mServicesLock) {
1706ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final UserServices<V> user = findOrCreateUserLocked(userId);
1716ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            if (user.services != null) {
1726ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                fout.println("RegisteredServicesCache: " + user.services.size() + " services");
1736ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                for (ServiceInfo<?> info : user.services.values()) {
1746ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    fout.println("  " + info);
1756ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                }
1766ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            } else {
1776ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                fout.println("RegisteredServicesCache: services not loaded");
1786ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            }
179718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
180718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
181718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    public RegisteredServicesCacheListener<V> getListener() {
183718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        synchronized (this) {
1845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return mListener;
1855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
187718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
1895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (handler == null) {
1905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            handler = new Handler(mContext.getMainLooper());
1915ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1925ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (this) {
1935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mHandler = handler;
1945ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mListener = listener;
195718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
196718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
197718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
1986ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    private void notifyListener(final V type, final int userId, final boolean removed) {
19940e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn        if (DEBUG) {
2005ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
2015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
2025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        RegisteredServicesCacheListener<V> listener;
2035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        Handler handler;
204718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        synchronized (this) {
2055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            listener = mListener;
2065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            handler = mHandler;
2075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
2085ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (listener == null) {
2095ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
210718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
2115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
2125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        final RegisteredServicesCacheListener<V> listener2 = listener;
2135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        handler.post(new Runnable() {
2145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            public void run() {
2156ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                listener2.onServiceChanged(type, userId, removed);
2165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        });
218718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
219718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
220718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
221718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Value type that describes a Service. The information within can be used
222718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * to bind to the service.
223718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
224718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    public static class ServiceInfo<V> {
225718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public final V type;
226718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public final ComponentName componentName;
227d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana        public final int uid;
228718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
22956285a60e83138bb4b4f2d3bdec91b2f3ca11aa2Fred Quintana        /** @hide */
23056285a60e83138bb4b4f2d3bdec91b2f3ca11aa2Fred Quintana        public ServiceInfo(V type, ComponentName componentName, int uid) {
231718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            this.type = type;
232718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            this.componentName = componentName;
233d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            this.uid = uid;
234718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
235718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
236c42c0dd1c4e2f7a4abaac1b2c9a6344448f9db7aJeff Hamilton        @Override
237718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        public String toString() {
2385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
239718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
240718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
241718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
242718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
243718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * Accessor for the registered authenticators.
244718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @param type the account type of the authenticator
245718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @return the AuthenticatorInfo that matches the account type or null if none is present
246718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
2476ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    public ServiceInfo<V> getServiceInfo(V type, int userId) {
2485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2496ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            // Find user and lazily populate cache
2506ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final UserServices<V> user = findOrCreateUserLocked(userId);
2516ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            if (user.services == null) {
2526ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                generateServicesMap(userId);
2536ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            }
2546ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            return user.services.get(type);
255718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
256718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
257718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
258718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    /**
259718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
260718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     * registered authenticators.
261718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana     */
2626ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    public Collection<ServiceInfo<V>> getAllServices(int userId) {
2635ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
2646ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            // Find user and lazily populate cache
2656ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final UserServices<V> user = findOrCreateUserLocked(userId);
2666ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            if (user.services == null) {
2676ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                generateServicesMap(userId);
2686ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            }
269a706e2fd0059b1bb86c487722dbc9fc0fda9c980Jeff Sharkey            return Collections.unmodifiableCollection(
270a706e2fd0059b1bb86c487722dbc9fc0fda9c980Jeff Sharkey                    new ArrayList<ServiceInfo<V>>(user.services.values()));
271718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
272718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
273718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean inSystemImage(int callerUid) {
2755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
2765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (String name : packages) {
2775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            try {
2785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                PackageInfo packageInfo =
2795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
2805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
2815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    return true;
2825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
2835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            } catch (PackageManager.NameNotFoundException e) {
2845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return false;
2855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
2865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
2875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
2885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
289718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
2906ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    /**
2916ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey     * Populate {@link UserServices#services} by scanning installed packages for
2926ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey     * given {@link UserHandle}.
2936ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey     */
2946ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey    private void generateServicesMap(int userId) {
29540e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn        if (DEBUG) {
29640e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn            Slog.d(TAG, "generateServicesMap() for " + userId);
29740e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn        }
2986ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
2996ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        final PackageManager pm = mContext.getPackageManager();
3006ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
3016ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(
3026ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);
303718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        for (ResolveInfo resolveInfo : resolveInfos) {
304718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            try {
305718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                ServiceInfo<V> info = parseServiceInfo(resolveInfo);
3065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (info == null) {
3075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
3085ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    continue;
309718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                }
3105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                serviceInfos.add(info);
311718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            } catch (XmlPullParserException e) {
3125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
313718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            } catch (IOException e) {
3145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
315718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
316718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
317718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
3185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        synchronized (mServicesLock) {
3196ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final UserServices<V> user = findOrCreateUserLocked(userId);
3206ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            final boolean firstScan = user.services == null;
3216ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            if (firstScan) {
3226ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                user.services = Maps.newHashMap();
3236ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            } else {
3246ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                user.services.clear();
3255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3266ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey
3273fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert            StringBuilder changes = new StringBuilder();
32840e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn            boolean changed = false;
3295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (ServiceInfo<V> info : serviceInfos) {
3305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // four cases:
3315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - doesn't exist yet
3325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - add, notify user that it was added
3335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists and the UID is the same
3345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - replace, don't notify user
3355ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists, the UID is different, and the new one is not a system package
3365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - ignore
3375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                // - exists, the UID is different, and the new one is a system package
3385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                //   - add, notify user that it was added
3396ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                Integer previousUid = user.persistentServices.get(info.type);
3405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (previousUid == null) {
34140e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    if (DEBUG) {
34240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        changes.append("  New service added: ").append(info).append("\n");
34340e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    }
34440e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    changed = true;
3456ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    user.services.put(info.type, info);
3466ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    user.persistentServices.put(info.type, info.uid);
3476ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    if (!(mPersistentServicesFileDidNotExist && firstScan)) {
3486ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                        notifyListener(info.type, userId, false /* removed */);
3495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else if (previousUid == info.uid) {
35140e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    if (DEBUG) {
3523fa51e3430e3bf902ae4f2d72dfb956103b6bd2dAlon Albert                        changes.append("  Existing service (nop): ").append(info).append("\n");
3535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
3546ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    user.services.put(info.type, info);
3555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else if (inSystemImage(info.uid)
3565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
35740e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    if (DEBUG) {
35840e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        if (inSystemImage(info.uid)) {
35940e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                            changes.append("  System service replacing existing: ").append(info)
36040e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                                    .append("\n");
36140e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        } else {
36240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                            changes.append("  Existing service replacing a removed service: ")
36340e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                                    .append(info).append("\n");
36440e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        }
3655ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
36640e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    changed = true;
3676ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    user.services.put(info.type, info);
3686ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    user.persistentServices.put(info.type, info.uid);
3696ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    notifyListener(info.type, userId, false /* removed */);
3705ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } else {
3715ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    // ignore
37240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    if (DEBUG) {
37340e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        changes.append("  Existing service with new uid ignored: ").append(info)
37440e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                                .append("\n");
37540e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    }
3765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3775ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
3795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            ArrayList<V> toBeRemoved = Lists.newArrayList();
3806ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            for (V v1 : user.persistentServices.keySet()) {
3815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                if (!containsType(serviceInfos, v1)) {
3825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    toBeRemoved.add(v1);
3835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
3845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
3855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            for (V v1 : toBeRemoved) {
38640e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                if (DEBUG) {
38740e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                    changes.append("  Service removed: ").append(v1).append("\n");
38840e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                }
38940e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                changed = true;
3906ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                user.persistentServices.remove(v1);
3916ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                notifyListener(v1, userId, true /* removed */);
3925ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
39340e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn            if (DEBUG) {
39440e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                if (changes.length() > 0) {
3954428e17c5e05c0dad76da8f1c28ccba62a66cd91Dianne Hackborn                    Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
3964428e17c5e05c0dad76da8f1c28ccba62a66cd91Dianne Hackborn                            serviceInfos.size() + " services:\n" + changes);
39740e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                } else {
3984428e17c5e05c0dad76da8f1c28ccba62a66cd91Dianne Hackborn                    Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
3994428e17c5e05c0dad76da8f1c28ccba62a66cd91Dianne Hackborn                            serviceInfos.size() + " services unchanged");
4004428e17c5e05c0dad76da8f1c28ccba62a66cd91Dianne Hackborn                }
4015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
40240e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn            if (changed) {
40340e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                writePersistentServicesLocked();
40440e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn            }
4055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
4075ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
4085ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
4095ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
4105ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (serviceInfos.get(i).type.equals(type)) {
4115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return true;
4125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
4155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
4165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
4175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
4185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
4195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
4205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
4215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
4225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return true;
4235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
4265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        return false;
427718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
428718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
429718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
430718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            throws XmlPullParserException, IOException {
431718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        android.content.pm.ServiceInfo si = service.serviceInfo;
432718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        ComponentName componentName = new ComponentName(si.packageName, si.name);
433718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
434718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        PackageManager pm = mContext.getPackageManager();
435718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
436718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        XmlResourceParser parser = null;
437718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        try {
438718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            parser = si.loadXmlMetaData(pm, mMetaDataName);
439718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (parser == null) {
440718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
441718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
442718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
443718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            AttributeSet attrs = Xml.asAttributeSet(parser);
444718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
445718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            int type;
446718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
447718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                    && type != XmlPullParser.START_TAG) {
448718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
449718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
450718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            String nodeName = parser.getName();
451718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (!mAttributesName.equals(nodeName)) {
452718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                throw new XmlPullParserException(
453718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                        "Meta-data does not start with " + mAttributesName +  " tag");
454718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
455718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
45620cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
45720cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn                    si.packageName, attrs);
458718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (v == null) {
459718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                return null;
460718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
461d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
462d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
463d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            final int uid = applicationInfo.uid;
464d4a1d2e14297a3387fdb5761090961e714370492Fred Quintana            return new ServiceInfo<V>(v, componentName, uid);
46520cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn        } catch (NameNotFoundException e) {
46620cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            throw new XmlPullParserException(
46720cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn                    "Unable to load resources for pacakge " + si.packageName);
468718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        } finally {
469718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            if (parser != null) parser.close();
470718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
471718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
472718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
4735ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
4745ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * Read all sync status back in to the initial engine state.
4755ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
4765ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void readPersistentServicesLocked() {
4776ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey        mUserServices.clear();
4785ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mSerializerAndParser == null) {
4795ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
4805ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
4815ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        FileInputStream fis = null;
4825ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        try {
4835ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
4845ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (mPersistentServicesFileDidNotExist) {
4855ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                return;
4865ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4875ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            fis = mPersistentServicesFile.openRead();
4885ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            XmlPullParser parser = Xml.newPullParser();
4895ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            parser.setInput(fis, null);
4905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            int eventType = parser.getEventType();
491293ad6c6749044d97ea2a5f3fbd5c6ad58124584Jeff Sharkey            while (eventType != XmlPullParser.START_TAG
492293ad6c6749044d97ea2a5f3fbd5c6ad58124584Jeff Sharkey                    && eventType != XmlPullParser.END_DOCUMENT) {
4935ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                eventType = parser.next();
4945ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
4955ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            String tagName = parser.getName();
4965ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if ("services".equals(tagName)) {
4975ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                eventType = parser.next();
4985ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                do {
4995ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
5005ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        tagName = parser.getName();
5015ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        if ("service".equals(tagName)) {
5025ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            V service = mSerializerAndParser.createFromXml(parser);
5035ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            if (service == null) {
5045ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                                break;
5055ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            }
5065ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                            String uidString = parser.getAttributeValue(null, "uid");
5076ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                            final int uid = Integer.parseInt(uidString);
5086ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                            final int userId = UserHandle.getUserId(uid);
5096ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                            final UserServices<V> user = findOrCreateUserLocked(userId);
5106ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                            user.persistentServices.put(service, uid);
5115ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                        }
5125ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    }
5135ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    eventType = parser.next();
5145ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } while (eventType != XmlPullParser.END_DOCUMENT);
5155ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
5165ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } catch (Exception e) {
5175ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.w(TAG, "Error reading persistent services, starting from scratch", e);
5185ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } finally {
5195ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (fis != null) {
5205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                try {
5215ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                    fis.close();
5225ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                } catch (java.io.IOException e1) {
5235ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                }
5245ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
5255ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
5265ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
5275ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
5285ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    /**
5295ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     * Write all sync status to the sync status file.
5305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana     */
5315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private void writePersistentServicesLocked() {
5325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        if (mSerializerAndParser == null) {
5335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return;
5345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
5355ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        FileOutputStream fos = null;
5365ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        try {
5375ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            fos = mPersistentServicesFile.startWrite();
5385ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            XmlSerializer out = new FastXmlSerializer();
5395ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.setOutput(fos, "utf-8");
5405ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.startDocument(null, true);
5415ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
5425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.startTag(null, "services");
5436ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey            for (int i = 0; i < mUserServices.size(); i++) {
5446ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                final UserServices<V> user = mUserServices.valueAt(i);
5456ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
5466ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    out.startTag(null, "service");
5476ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    out.attribute(null, "uid", Integer.toString(service.getValue()));
5486ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    mSerializerAndParser.writeAsXml(service.getKey(), out);
5496ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                    out.endTag(null, "service");
5506ab72d74b86e5f4ec3c3909366fd46c225a66bd7Jeff Sharkey                }
5515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
5525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.endTag(null, "services");
5535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.endDocument();
5545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            mPersistentServicesFile.finishWrite(fos);
5555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        } catch (java.io.IOException e1) {
5565ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            Log.w(TAG, "Error writing accounts", e1);
5575ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            if (fos != null) {
5585ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                mPersistentServicesFile.failWrite(fos);
5595ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            }
5605ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
5615ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
5625ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
56320cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn    public abstract V parseServiceAttributes(Resources res,
56420cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            String packageName, AttributeSet attrs);
565718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana}
566