/**
* Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
* This file is part of CSipSimple.
*
* CSipSimple is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* If you own a pjsip commercial license you can also redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as an android library.
*
* CSipSimple is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CSipSimple. If not, see <http://www.gnu.org/licenses/>.
*/
package com.csipsimple.utils;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import com.csipsimple.api.SipManager;
import com.csipsimple.pjsip.NativeLibManager;
import com.csipsimple.pjsip.PjSipService;
public class ExtraPlugins {
private static final String THIS_FILE = "ExtraPlugins";
/**
* Class that stores and help to build infos from a remote plugin application
* that holds a native library extension
*/
public static class DynCodecInfos {
/**
* Path of the native library .so file corresponding
*/
public String libraryPath;
/**
* The function to call inside the native library to init this plugin
* The params depends of the kind of plugin it is
*/
public String factoryInitFunction;
/**
* The function to call inside the native library to deinit this plugin
* The params depends of the kind of plugin it is
*/
public String factoryDeinitFunction;
/**
* Build codec infos based on a component name of a manifest
* It will retrieve {@link SipManager#META_LIB_NAME} and {@link SipManager#META_LIB_INIT_FACTORY} from the meta datas of the component
* @param ctxt The current application context
* @param cmp The component name of the remote plugin application
* @throws NameNotFoundException If the remote component is not found
*/
public DynCodecInfos(Context ctxt, ComponentName cmp) throws NameNotFoundException {
PackageManager pm = ctxt.getPackageManager();
ActivityInfo infos = pm.getReceiverInfo(cmp, PackageManager.GET_META_DATA);
factoryInitFunction = infos.metaData.getString(SipManager.META_LIB_INIT_FACTORY);
factoryDeinitFunction = infos.metaData.getString(SipManager.META_LIB_DEINIT_FACTORY);
String libName = infos.metaData.getString(SipManager.META_LIB_NAME);
PackageInfo pInfos = pm.getPackageInfo(cmp.getPackageName(), PackageManager.GET_SHARED_LIBRARY_FILES);
// TODO : for now only api-9 compatible
File libFile = NativeLibManager.getLibFileFromPackage(pInfos.applicationInfo, libName, true);
if(libFile != null) {
libraryPath = libFile.getAbsolutePath();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("File : ");
sb.append(libraryPath);
sb.append("/");
sb.append(factoryInitFunction);
return super.toString();
}
}
private static Map<String, Map<String, DynCodecInfos>> CACHED_RESOLUTION = new HashMap<String, Map<String, DynCodecInfos>>();
/**
* Reset all dynamic plugins infos
*/
public static void clearDynPlugins() {
CACHED_RESOLUTION.clear();
CACHED_ACTIVITY_RESOLUTION.clear();
PjSipService.resetCodecs();
}
/**
* Retrieve all dynamic codec plugins available in the platform
* It will resolve for a given action available sip plugins
* @param ctxt Context of the application
* @param action Action of the plugin to be resolved
* For example {@link SipManager#ACTION_GET_EXTRA_CODECS}, {@link SipManager.ACTION_GET_VIDEO_PLUGIN}
* @return a map containing plugins infos and registrered component name as key
*/
public static Map<String, DynCodecInfos> getDynCodecPlugins(Context ctxt, String action){
if(!CACHED_RESOLUTION.containsKey(action)) {
HashMap<String, DynCodecInfos> plugins = new HashMap<String, DynCodecInfos>();
PackageManager packageManager = ctxt.getPackageManager();
Intent it = new Intent(action);
List<ResolveInfo> availables = packageManager.queryBroadcastReceivers(it, 0);
for(ResolveInfo resInfo : availables) {
ActivityInfo actInfos = resInfo.activityInfo;
if( packageManager.checkPermission(SipManager.PERMISSION_CONFIGURE_SIP, actInfos.packageName) == PackageManager.PERMISSION_GRANTED) {
ComponentName cmp = new ComponentName(actInfos.packageName, actInfos.name);
DynCodecInfos dynInfos;
try {
dynInfos = new DynCodecInfos(ctxt, cmp);
plugins.put(cmp.flattenToString(), dynInfos);
} catch (NameNotFoundException e) {
Log.e(THIS_FILE, "Error while retrieving infos from dyn codec ", e);
}
}
}
CACHED_RESOLUTION.put(action, plugins);
}
return CACHED_RESOLUTION.get(action);
}
private static Map<String, Map<String, DynActivityPlugin>> CACHED_ACTIVITY_RESOLUTION = new HashMap<String, Map<String, DynActivityPlugin>>();
/**
* Part for incall plugin
*/
public static class DynActivityPlugin {
private final ComponentName cmp;
private final String name;
private final String action;
private final Bundle metaDatas;
public DynActivityPlugin(String name, String action, ComponentName componentName, Bundle meta) {
this.cmp = componentName;
this.action = action;
this.name = name;
this.metaDatas = meta;
}
public Intent getIntent() {
Intent it = new Intent(action);
it.addCategory(Intent.CATEGORY_EMBED);
it.setComponent(cmp);
return it;
}
public String getMetaDataString(String key, String defaultValue) {
if(metaDatas == null) {
return defaultValue;
}
String res = metaDatas.getString(key);
if(res == null) {
return defaultValue;
}
return res;
}
public Integer getMetaDataInt(String key, int defaultValue) {
if(metaDatas == null) {
return defaultValue;
}
return metaDatas.getInt(key, defaultValue);
}
public CharSequence getName() {
return name;
}
}
public static Map<String, DynActivityPlugin> getDynActivityPlugins(Context ctxt, String action){
if(!CACHED_ACTIVITY_RESOLUTION.containsKey(action)) {
HashMap<String, DynActivityPlugin> plugins = new HashMap<String, DynActivityPlugin>();
PackageManager packageManager = ctxt.getPackageManager();
Intent it = new Intent(action);
List<ResolveInfo> availables = packageManager.queryIntentActivities(it, 0);
for(ResolveInfo resInfo : availables) {
ActivityInfo actInfos = resInfo.activityInfo;
if( packageManager.checkPermission(SipManager.PERMISSION_USE_SIP, actInfos.packageName) == PackageManager.PERMISSION_GRANTED) {
ComponentName cmp = new ComponentName(actInfos.packageName, actInfos.name);
DynActivityPlugin dynInfos;
dynInfos = new DynActivityPlugin(actInfos.loadLabel(packageManager).toString(), action,
cmp, actInfos.metaData);
plugins.put(cmp.flattenToString(), dynInfos);
}
}
CACHED_ACTIVITY_RESOLUTION.put(action, plugins);
}
return CACHED_ACTIVITY_RESOLUTION.get(action);
}
}