blob: 8d73317b7b45d98177ed7376eb76df4035a9203c [file] [log] [blame]
nxpandroid64fd68c2015-09-23 16:45:15 +05301/*
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
17package com.android.nfc;
18
19import org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21
22import android.app.ActivityManager;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.ActivityInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.ResolveInfo;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.content.res.Resources;
32import android.content.res.XmlResourceParser;
33import android.os.UserHandle;
34import android.util.Log;
35
36import java.io.IOException;
37import java.util.ArrayList;
38import java.util.List;
39import java.util.concurrent.atomic.AtomicReference;
40
41/**
42 * A cache of intent filters registered to receive the TECH_DISCOVERED dispatch.
43 */
44public 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}