package org.kvj.bravo7.ipc; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.util.Log; public abstract class RemoteServicesCollector<T extends IInterface> { public class PluginConnection { RemoteServiceConnector<T> connector = null; } public interface PluginFilter { public boolean filter(String category, Bundle meta); } public static class APIPluginFilter implements PluginFilter { @Override public boolean filter(String category, Bundle meta) { if (meta.containsKey("api")) { // Have API try { // Conversion errors int api = meta.getInt("api"); if (api > Build.VERSION.SDK_INT) { // Not suitable return false; } } catch (Exception e) { e.printStackTrace(); } } return true; } } private static final String TAG = "RemotePlugins"; private Context ctx = null; private String action = null; private Map<String, List<PluginConnection>> plugins = new HashMap<String, List<PluginConnection>>(); private PackageBroadcastReceiver receiver = null; private List<PluginFilter> filters = new ArrayList<PluginFilter>(); class PackageBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { collectPlugins(intent.getAction(), intent.getDataString()); } } public RemoteServicesCollector(Context ctx, String action, PluginFilter... filters) { this.ctx = ctx; this.action = action; if (null != filters) { // Have filters for (PluginFilter filter : filters) { // Add filters addPluginFilter(filter); } } receiver = new PackageBroadcastReceiver(); IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); packageFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addCategory(Intent.CATEGORY_DEFAULT); packageFilter.addDataScheme("package"); ctx.registerReceiver(receiver, packageFilter); collectPlugins(Intent.ACTION_PACKAGE_ADDED, null); } private void collectPlugins(String packageAction, String changedPackage) { if (null != changedPackage) { List<PluginConnection> list = plugins.get(changedPackage); if (null != list) { // Have plugins - stop for (PluginConnection plugin : list) { // Call stop() plugin.connector.stop(); } list.clear(); } } if (!Intent.ACTION_PACKAGE_REMOVED.equals(packageAction)) { // Not removed // Discover PackageManager packageManager = ctx.getPackageManager(); Intent baseIntent = new Intent(action); if (null != changedPackage) { baseIntent.setPackage(changedPackage); } baseIntent.setFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); List<ResolveInfo> resolves = packageManager.queryIntentServices( baseIntent, PackageManager.GET_RESOLVED_FILTER | PackageManager.GET_META_DATA); if (null == resolves) { resolves = new ArrayList<ResolveInfo>(); } for (ResolveInfo info : resolves) { // Check every found item ServiceInfo sinfo = info.serviceInfo; IntentFilter filter = info.filter; if (null == sinfo || null == filter || 0 == filter.countCategories()) { // Invalid data continue; } List<PluginConnection> list = plugins.get(sinfo.packageName); if (null == list) { // List not created - new list = new ArrayList<PluginConnection>(); plugins.put(sinfo.packageName, list); } PluginConnection plugin = new PluginConnection(); boolean filteredOut = false; String category = filter.getCategory(0); synchronized (filters) { // Lock for modifications for (PluginFilter f : filters) { // Run filter if (null != sinfo.metaData && !f.filter(category, sinfo.metaData)) { // Filtered out filteredOut = true; break; } } } if (filteredOut) { // Skip plugin Log.i(TAG, "Plugin " + category + " filtered out"); } plugin.connector = new RemoteServiceConnector<T>(ctx, action, category) { @Override public T castAIDL(IBinder binder) { return RemoteServicesCollector.this.castAIDL(binder); } }; list.add(plugin); } } onChange(); } public void onChange() { } abstract public T castAIDL(IBinder binder); public void stop() { ctx.unregisterReceiver(receiver); } public List<T> getPlugins() { List<T> result = new ArrayList<T>(); synchronized (plugins) { for (List<PluginConnection> p : plugins.values()) { for (PluginConnection pc : p) { T remote = pc.connector.getRemote(); if (null != remote) { result.add(remote); } } } } return result; } public void addPluginFilter(PluginFilter filter) { synchronized (filters) { // Lock for modifications filters.add(filter); } } }