1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.nfc; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 22 import android.content.BroadcastReceiver; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.ActivityInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ResolveInfo; 30 import android.content.pm.PackageManager.NameNotFoundException; 31 import android.content.res.Resources; 32 import android.content.res.XmlResourceParser; 33 import android.util.Log; 34 35 import java.io.IOException; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.concurrent.atomic.AtomicReference; 39 40 /** 41 * A cache of intent filters registered to receive the TECH_DISCOVERED dispatch. 42 */ 43 public class RegisteredComponentCache { 44 private static final String TAG = "RegisteredComponentCache"; 45 46 final Context mContext; 47 final String mAction; 48 final String mMetaDataName; 49 final AtomicReference<BroadcastReceiver> mReceiver; 50 51 // synchronized on this 52 private ArrayList<ComponentInfo> mComponents; 53 RegisteredComponentCache(Context context, String action, String metaDataName)54 public RegisteredComponentCache(Context context, String action, String metaDataName) { 55 mContext = context; 56 mAction = action; 57 mMetaDataName = metaDataName; 58 59 generateComponentsList(); 60 61 final BroadcastReceiver receiver = new BroadcastReceiver() { 62 @Override 63 public void onReceive(Context context1, Intent intent) { 64 generateComponentsList(); 65 } 66 }; 67 mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 68 IntentFilter intentFilter = new IntentFilter(); 69 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 70 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 71 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 72 intentFilter.addDataScheme("package"); 73 mContext.registerReceiver(receiver, intentFilter); 74 // Register for events related to sdcard installation. 75 IntentFilter sdFilter = new IntentFilter(); 76 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 77 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 78 mContext.registerReceiver(receiver, sdFilter); 79 } 80 81 public static class ComponentInfo { 82 public final ResolveInfo resolveInfo; 83 public final String[] techs; 84 ComponentInfo(ResolveInfo resolveInfo, String[] techs)85 ComponentInfo(ResolveInfo resolveInfo, String[] techs) { 86 this.resolveInfo = resolveInfo; 87 this.techs = techs; 88 } 89 90 @Override toString()91 public String toString() { 92 StringBuilder out = new StringBuilder("ComponentInfo: "); 93 out.append(resolveInfo); 94 out.append(", techs: "); 95 for (String tech : techs) { 96 out.append(tech); 97 out.append(", "); 98 } 99 return out.toString(); 100 } 101 } 102 103 /** 104 * @return a collection of {@link RegisteredComponentCache.ComponentInfo} objects for all 105 * registered authenticators. 106 */ getComponents()107 public ArrayList<ComponentInfo> getComponents() { 108 synchronized (this) { 109 // It's safe to return a reference here since mComponents is always replaced and 110 // never updated when it changes. 111 return mComponents; 112 } 113 } 114 115 /** 116 * Stops the monitoring of package additions, removals and changes. 117 */ close()118 public void close() { 119 final BroadcastReceiver receiver = mReceiver.getAndSet(null); 120 if (receiver != null) { 121 mContext.unregisterReceiver(receiver); 122 } 123 } 124 125 @Override finalize()126 protected void finalize() throws Throwable { 127 if (mReceiver.get() != null) { 128 Log.e(TAG, "RegisteredServicesCache finalized without being closed"); 129 } 130 close(); 131 super.finalize(); 132 } 133 dump(ArrayList<ComponentInfo> components)134 void dump(ArrayList<ComponentInfo> components) { 135 for (ComponentInfo component : components) { 136 Log.i(TAG, component.toString()); 137 } 138 } 139 generateComponentsList()140 void generateComponentsList() { 141 PackageManager pm = mContext.getPackageManager(); 142 ArrayList<ComponentInfo> components = new ArrayList<ComponentInfo>(); 143 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(new Intent(mAction), 144 PackageManager.GET_META_DATA); 145 for (ResolveInfo resolveInfo : resolveInfos) { 146 try { 147 parseComponentInfo(resolveInfo, components); 148 } catch (XmlPullParserException e) { 149 Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); 150 } catch (IOException e) { 151 Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); 152 } 153 } 154 155 dump(components); 156 157 synchronized (this) { 158 mComponents = components; 159 } 160 } 161 parseComponentInfo(ResolveInfo info, ArrayList<ComponentInfo> components)162 void parseComponentInfo(ResolveInfo info, ArrayList<ComponentInfo> components) 163 throws XmlPullParserException, IOException { 164 ActivityInfo ai = info.activityInfo; 165 PackageManager pm = mContext.getPackageManager(); 166 167 XmlResourceParser parser = null; 168 try { 169 parser = ai.loadXmlMetaData(pm, mMetaDataName); 170 if (parser == null) { 171 throw new XmlPullParserException("No " + mMetaDataName + " meta-data"); 172 } 173 174 parseTechLists(pm.getResourcesForApplication(ai.applicationInfo), ai.packageName, 175 parser, info, components); 176 } catch (NameNotFoundException e) { 177 throw new XmlPullParserException("Unable to load resources for " + ai.packageName); 178 } finally { 179 if (parser != null) parser.close(); 180 } 181 } 182 parseTechLists(Resources res, String packageName, XmlPullParser parser, ResolveInfo resolveInfo, ArrayList<ComponentInfo> components)183 void parseTechLists(Resources res, String packageName, XmlPullParser parser, 184 ResolveInfo resolveInfo, ArrayList<ComponentInfo> components) 185 throws XmlPullParserException, IOException { 186 int eventType = parser.getEventType(); 187 while (eventType != XmlPullParser.START_TAG) { 188 eventType = parser.next(); 189 } 190 191 ArrayList<String> items = new ArrayList(); 192 String tagName; 193 eventType = parser.next(); 194 do { 195 tagName = parser.getName(); 196 if (eventType == XmlPullParser.START_TAG && "tech".equals(tagName)) { 197 items.add(parser.nextText()); 198 } else if (eventType == XmlPullParser.END_TAG && "tech-list".equals(tagName)) { 199 int size = items.size(); 200 if (size > 0) { 201 String[] techs = new String[size]; 202 techs = items.toArray(techs); 203 items.clear(); 204 components.add(new ComponentInfo(resolveInfo, techs)); 205 } 206 } 207 eventType = parser.next(); 208 } while (eventType != XmlPullParser.END_DOCUMENT); 209 } 210 } 211