blob: 8d73317b7b45d98177ed7376eb76df4035a9203c [file] [log] [blame]
/*
* 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.app.ActivityManager;
import android.content.BroadcastReceiver;
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.os.UserHandle;
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";
private static final boolean DEBUG = false;
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.registerReceiverAsUser(receiver, UserHandle.ALL, intentFilter, null, null);
// 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.registerReceiverAsUser(receiver, UserHandle.ALL, sdFilter, null, null);
// Generate a new list upon switching users as well
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiverAsUser(receiver, UserHandle.ALL, userFilter, null, null);
}
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;
try {
UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser());
pm = mContext.createPackageContextAsUser("android", 0,
currentUser).getPackageManager();
} catch (NameNotFoundException e) {
Log.e(TAG, "Could not create user package context");
return;
}
ArrayList<ComponentInfo> components = new ArrayList<ComponentInfo>();
List<ResolveInfo> resolveInfos = pm.queryIntentActivitiesAsUser(new Intent(mAction),
PackageManager.GET_META_DATA, ActivityManager.getCurrentUser());
for (ResolveInfo resolveInfo : resolveInfos) {
try {
parseComponentInfo(pm, 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);
}
}
if (DEBUG) {
dump(components);
}
synchronized (this) {
mComponents = components;
}
}
void parseComponentInfo(PackageManager pm, ResolveInfo info,
ArrayList<ComponentInfo> components) throws XmlPullParserException, IOException {
ActivityInfo ai = info.activityInfo;
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>();
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);
}
}