/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.nfc; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** * A cache of intent filters registered to receive the TECH_DISCOVERED dispatch. */ public class RegisteredComponentCache { private static final String TAG = "RegisteredComponentCache"; final Context mContext; final String mAction; final String mMetaDataName; final AtomicReference<BroadcastReceiver> mReceiver; // synchronized on this private ArrayList<ComponentInfo> mComponents; public RegisteredComponentCache(Context context, String action, String metaDataName) { mContext = context; mAction = action; mMetaDataName = metaDataName; generateComponentsList(); final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context1, Intent intent) { generateComponentsList(); } }; mReceiver = new AtomicReference<BroadcastReceiver>(receiver); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); mContext.registerReceiver(receiver, intentFilter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(receiver, sdFilter); } public static class ComponentInfo { public final ResolveInfo resolveInfo; public final String[] techs; ComponentInfo(ResolveInfo resolveInfo, String[] techs) { this.resolveInfo = resolveInfo; this.techs = techs; } @Override public String toString() { StringBuilder out = new StringBuilder("ComponentInfo: "); out.append(resolveInfo); out.append(", techs: "); for (String tech : techs) { out.append(tech); out.append(", "); } return out.toString(); } } /** * @return a collection of {@link RegisteredComponentCache.ComponentInfo} objects for all * registered authenticators. */ public ArrayList<ComponentInfo> getComponents() { synchronized (this) { // It's safe to return a reference here since mComponents is always replaced and // never updated when it changes. return mComponents; } } /** * Stops the monitoring of package additions, removals and changes. */ public void close() { final BroadcastReceiver receiver = mReceiver.getAndSet(null); if (receiver != null) { mContext.unregisterReceiver(receiver); } } @Override protected void finalize() throws Throwable { if (mReceiver.get() != null) { Log.e(TAG, "RegisteredServicesCache finalized without being closed"); } close(); super.finalize(); } void dump(ArrayList<ComponentInfo> components) { for (ComponentInfo component : components) { Log.i(TAG, component.toString()); } } void generateComponentsList() { PackageManager pm = mContext.getPackageManager(); ArrayList<ComponentInfo> components = new ArrayList<ComponentInfo>(); List<ResolveInfo> resolveInfos = pm.queryIntentActivities(new Intent(mAction), PackageManager.GET_META_DATA); for (ResolveInfo resolveInfo : resolveInfos) { try { parseComponentInfo(resolveInfo, components); } catch (XmlPullParserException e) { Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); } catch (IOException e) { Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); } } dump(components); synchronized (this) { mComponents = components; } } void parseComponentInfo(ResolveInfo info, ArrayList<ComponentInfo> components) throws XmlPullParserException, IOException { ActivityInfo ai = info.activityInfo; PackageManager pm = mContext.getPackageManager(); XmlResourceParser parser = null; try { parser = ai.loadXmlMetaData(pm, mMetaDataName); if (parser == null) { throw new XmlPullParserException("No " + mMetaDataName + " meta-data"); } parseTechLists(pm.getResourcesForApplication(ai.applicationInfo), ai.packageName, parser, info, components); } catch (NameNotFoundException e) { throw new XmlPullParserException("Unable to load resources for " + ai.packageName); } finally { if (parser != null) parser.close(); } } void parseTechLists(Resources res, String packageName, XmlPullParser parser, ResolveInfo resolveInfo, ArrayList<ComponentInfo> components) throws XmlPullParserException, IOException { int eventType = parser.getEventType(); while (eventType != XmlPullParser.START_TAG) { eventType = parser.next(); } ArrayList<String> items = new ArrayList(); String tagName; eventType = parser.next(); do { tagName = parser.getName(); if (eventType == XmlPullParser.START_TAG && "tech".equals(tagName)) { items.add(parser.nextText()); } else if (eventType == XmlPullParser.END_TAG && "tech-list".equals(tagName)) { int size = items.size(); if (size > 0) { String[] techs = new String[size]; techs = items.toArray(techs); items.clear(); components.add(new ComponentInfo(resolveInfo, techs)); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); } }