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.app.ActivityManager; 23 import android.content.BroadcastReceiver; 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.os.UserHandle; 34 import android.util.Log; 35 36 import java.io.IOException; 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.concurrent.atomic.AtomicReference; 40 41 /** 42 * A cache of intent filters registered to receive the TECH_DISCOVERED dispatch. 43 */ 44 public class RegisteredComponentCache { 45 private static final String TAG = "RegisteredComponentCache"; 46 private static final boolean DEBUG = false; 47 48 final Context mContext; 49 final String mAction; 50 final String mMetaDataName; 51 final AtomicReference<BroadcastReceiver> mReceiver; 52 53 // synchronized on this 54 private ArrayList<ComponentInfo> mComponents; 55 56 public RegisteredComponentCache(Context context, String action, String metaDataName) { 57 mContext = context; 58 mAction = action; 59 mMetaDataName = metaDataName; 60 61 generateComponentsList(); 62 63 final BroadcastReceiver receiver = new BroadcastReceiver() { 64 @Override 65 public void onReceive(Context context1, Intent intent) { 66 generateComponentsList(); 67 } 68 }; 69 mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 70 IntentFilter intentFilter = new IntentFilter(); 71 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 72 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 73 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 74 intentFilter.addDataScheme("package"); 75 mContext.registerReceiverAsUser(receiver, UserHandle.ALL, intentFilter, null, null); 76 // Register for events related to sdcard installation. 77 IntentFilter sdFilter = new IntentFilter(); 78 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 79 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 80 mContext.registerReceiverAsUser(receiver, UserHandle.ALL, sdFilter, null, null); 81 // Generate a new list upon switching users as well 82 IntentFilter userFilter = new IntentFilter(); 83 userFilter.addAction(Intent.ACTION_USER_SWITCHED); 84 mContext.registerReceiverAsUser(receiver, UserHandle.ALL, userFilter, null, null); 85 } 86 87 public static class ComponentInfo { 88 public final ResolveInfo resolveInfo; 89 public final String[] techs; 90 91 ComponentInfo(ResolveInfo resolveInfo, String[] techs) { 92 this.resolveInfo = resolveInfo; 93 this.techs = techs; 94 } 95 96 @Override 97 public String toString() { 98 StringBuilder out = new StringBuilder("ComponentInfo: "); 99 out.append(resolveInfo); 100 out.append(", techs: "); 101 for (String tech : techs) { 102 out.append(tech); 103 out.append(", "); 104 } 105 return out.toString(); 106 } 107 } 108 109 /** 110 * @return a collection of {@link RegisteredComponentCache.ComponentInfo} objects for all 111 * registered authenticators. 112 */ 113 public ArrayList<ComponentInfo> getComponents() { 114 synchronized (this) { 115 // It's safe to return a reference here since mComponents is always replaced and 116 // never updated when it changes. 117 return mComponents; 118 } 119 } 120 121 /** 122 * Stops the monitoring of package additions, removals and changes. 123 */ 124 public void close() { 125 final BroadcastReceiver receiver = mReceiver.getAndSet(null); 126 if (receiver != null) { 127 mContext.unregisterReceiver(receiver); 128 } 129 } 130 131 @Override 132 protected void finalize() throws Throwable { 133 if (mReceiver.get() != null) { 134 Log.e(TAG, "RegisteredServicesCache finalized without being closed"); 135 } 136 close(); 137 super.finalize(); 138 } 139 140 void dump(ArrayList<ComponentInfo> components) { 141 for (ComponentInfo component : components) { 142 Log.i(TAG, component.toString()); 143 } 144 } 145 146 void generateComponentsList() { 147 PackageManager pm; 148 try { 149 UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); 150 pm = mContext.createPackageContextAsUser("android", 0, 151 currentUser).getPackageManager(); 152 } catch (NameNotFoundException e) { 153 Log.e(TAG, "Could not create user package context"); 154 return; 155 } 156 ArrayList<ComponentInfo> components = new ArrayList<ComponentInfo>(); 157 List<ResolveInfo> resolveInfos = pm.queryIntentActivitiesAsUser(new Intent(mAction), 158 PackageManager.GET_META_DATA, ActivityManager.getCurrentUser()); 159 for (ResolveInfo resolveInfo : resolveInfos) { 160 try { 161 parseComponentInfo(pm, resolveInfo, components); 162 } catch (XmlPullParserException e) { 163 Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); 164 } catch (IOException e) { 165 Log.w(TAG, "Unable to load component info " + resolveInfo.toString(), e); 166 } 167 } 168 169 if (DEBUG) { 170 dump(components); 171 } 172 173 synchronized (this) { 174 mComponents = components; 175 } 176 } 177 178 void parseComponentInfo(PackageManager pm, ResolveInfo info, 179 ArrayList<ComponentInfo> components) throws XmlPullParserException, IOException { 180 ActivityInfo ai = info.activityInfo; 181 182 XmlResourceParser parser = null; 183 try { 184 parser = ai.loadXmlMetaData(pm, mMetaDataName); 185 if (parser == null) { 186 throw new XmlPullParserException("No " + mMetaDataName + " meta-data"); 187 } 188 189 parseTechLists(pm.getResourcesForApplication(ai.applicationInfo), ai.packageName, 190 parser, info, components); 191 } catch (NameNotFoundException e) { 192 throw new XmlPullParserException("Unable to load resources for " + ai.packageName); 193 } finally { 194 if (parser != null) parser.close(); 195 } 196 } 197 198 void parseTechLists(Resources res, String packageName, XmlPullParser parser, 199 ResolveInfo resolveInfo, ArrayList<ComponentInfo> components) 200 throws XmlPullParserException, IOException { 201 int eventType = parser.getEventType(); 202 while (eventType != XmlPullParser.START_TAG) { 203 eventType = parser.next(); 204 } 205 206 ArrayList<String> items = new ArrayList<String>(); 207 String tagName; 208 eventType = parser.next(); 209 do { 210 tagName = parser.getName(); 211 if (eventType == XmlPullParser.START_TAG && "tech".equals(tagName)) { 212 items.add(parser.nextText()); 213 } else if (eventType == XmlPullParser.END_TAG && "tech-list".equals(tagName)) { 214 int size = items.size(); 215 if (size > 0) { 216 String[] techs = new String[size]; 217 techs = items.toArray(techs); 218 items.clear(); 219 components.add(new ComponentInfo(resolveInfo, techs)); 220 } 221 } 222 eventType = parser.next(); 223 } while (eventType != XmlPullParser.END_DOCUMENT); 224 } 225 } 226