package com.android.im.app;
+import com.android.im.plugin.ImConfigNames;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.Im;
+import android.util.Log;
+
+import java.util.Map;
public class DatabaseUtils {
+
+ private static final String TAG = ImApp.LOG_TAG;
+
private DatabaseUtils() {
}
Drawable avatar = new BitmapDrawable(b);
return avatar;
}
+
+ /**
+ * Update IM provider database for a plugin using newly loaded information.
+ * @param cr the resolver
+ * @param providerName the plugin provider name
+ * @param providerFullName the full name
+ * @param signUpUrl the plugin's service signup URL
+ * @param config the plugin's settings
+ * @return the provider ID of the plugin
+ */
+ public static long updateProviderDb(ContentResolver cr,
+ String providerName, String providerFullName, String signUpUrl,
+ Map<String, String> config) {
+ boolean versionChanged;
+
+ // query provider data
+ long providerId = Im.Provider.getProviderIdForName(cr, providerName);
+ if (providerId > 0) {
+ // already loaded, check if version changed
+ String pluginVersion = config.get(ImConfigNames.PLUGIN_VERSION);
+ if (!isPluginVersionChanged(cr, providerId, pluginVersion)) {
+ // no change, just return
+ return providerId;
+ }
+ // changed, update provider meta data
+ updateProviderRow(cr, providerId, providerFullName, signUpUrl);
+ // clear branding resource map cache
+ clearBrandingResourceMapCache(cr, providerId);
+
+ Log.d(TAG, "Plugin " + providerName + "(" + providerId +
+ ") has a version change. Database updated.");
+ } else {
+ // new plugin, not loaded before, insert the provider data
+ providerId = insertProviderRow(cr, providerName, providerFullName, signUpUrl);
+
+ Log.d(TAG, "Plugin " + providerName + "(" + providerId +
+ ") is new. Provider added to IM db.");
+ }
+
+ // plugin provider has been inserted/updated, we need to update settings
+ saveProviderSettings(cr, providerId, config);
+
+ return providerId;
+ }
+
+ /**
+ * Clear the branding resource map cache.
+ */
+ private static int clearBrandingResourceMapCache(ContentResolver cr, long providerId) {
+ StringBuilder where = new StringBuilder();
+ where.append(Im.BrandingResourceMapCache.PROVIDER_ID);
+ where.append('=');
+ where.append(providerId);
+ return cr.delete(Im.BrandingResourceMapCache.CONTENT_URI, where.toString(), null);
+ }
+
+ /**
+ * Insert the plugin settings into the database.
+ */
+ private static int saveProviderSettings(ContentResolver cr, long providerId,
+ Map<String, String> config) {
+ ContentValues[] settingValues = new ContentValues[config.size()];
+ int index = 0;
+ for (Map.Entry<String, String> entry : config.entrySet()) {
+ ContentValues settingValue = new ContentValues();
+ settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
+ settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
+ settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
+ settingValues[index++] = settingValue;
+ }
+ return cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
+ }
+
+ /**
+ * Insert a new plugin provider to the provider table.
+ */
+ private static long insertProviderRow(ContentResolver cr, String providerName,
+ String providerFullName, String signUpUrl) {
+ ContentValues values = new ContentValues(3);
+ values.put(Im.Provider.NAME, providerName);
+ values.put(Im.Provider.FULLNAME, providerFullName);
+ values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+ values.put(Im.Provider.SIGNUP_URL, signUpUrl);
+ Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
+ return ContentUris.parseId(result);
+ }
+
+ /**
+ * Update the data of a plugin provider.
+ */
+ private static int updateProviderRow(ContentResolver cr, long providerId,
+ String providerFullName, String signUpUrl) {
+ // Update the full name, signup url and category each time when the plugin change
+ // instead of specific version change because this is called only once.
+ // It's ok to update them even the values are not changed.
+ // Note that we don't update the provider name because it's used as
+ // identifier at some place and the plugin should never change it.
+ ContentValues values = new ContentValues(3);
+ values.put(Im.Provider.FULLNAME, providerFullName);
+ values.put(Im.Provider.SIGNUP_URL, signUpUrl);
+ values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+ Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
+ return cr.update(uri, values, null, null);
+ }
+
+ /**
+ * Compare the saved version of a plugin provider with the newly loaded version.
+ */
+ private static boolean isPluginVersionChanged(ContentResolver cr, long providerId,
+ String newVersion) {
+ String oldVersion = Im.ProviderSettings.getStringValue(cr, providerId,
+ ImConfigNames.PLUGIN_VERSION);
+ if (oldVersion == null) {
+ return true;
+ }
+ return !oldVersion.equals(newVersion);
+ }
}
*/
package com.android.im.app;
+import com.android.im.plugin.ImConfigNames;
import com.android.im.plugin.ImPluginConstants;
import android.app.Service;
private ArrayList<String> mProviderNames;
private HashMap<String, String> mPackageNames;
- private HashMap<String, String> mClassNames;
- private HashMap<String, String> mSrcPaths;
private HashMap<String, Map<Integer, Integer>> mBrandingResources;
@Override
public IBinder onBind(Intent intent) {
- // temporary provider ID<->Name mappings
+ // temporary mappings
HashMap<String, Long> providerNameToId = new HashMap<String, Long>();
HashMap<Long, String> providerIdToName = new HashMap<Long, String>();
+ HashMap<String, Class> classes = new HashMap<String, Class>();
- loadThirdPartyPlugins(providerNameToId, providerIdToName);
- loadBrandingResources(providerNameToId, providerIdToName);
+ loadThirdPartyPlugins(providerNameToId, providerIdToName, classes);
+ loadBrandingResources(providerNameToId, providerIdToName, classes);
return mBinder;
}
- private void loadThirdPartyPlugins(HashMap<String, Long> providerNameToId,
- HashMap<Long, String> providerIdToName) {
+ private void loadThirdPartyPlugins(
+ HashMap<String, Long> providerNameToId, HashMap<Long, String> providerIdToName,
+ HashMap<String, Class> classes) {
mProviderNames = new ArrayList<String>();
mPackageNames = new HashMap<String, String>();
- mClassNames = new HashMap<String, String>();
- mSrcPaths = new HashMap<String, String>();
PackageManager pm = getPackageManager();
List<ResolveInfo> plugins = pm.queryIntentServices(
Bundle metaData = serviceInfo.metaData;
if (metaData != null) {
providerName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_NAME);
- providerFullName = metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
+ providerFullName =
+ metaData.getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME);
signUpUrl = metaData.getString(ImPluginConstants.METADATA_SIGN_UP_URL);
}
if (TextUtils.isEmpty(providerName) || TextUtils.isEmpty(providerFullName)) {
mProviderNames.add(providerName);
mPackageNames.put(providerName, serviceInfo.packageName);
- mClassNames.put(providerName, serviceInfo.name);
- mSrcPaths.put(providerName, serviceInfo.applicationInfo.sourceDir);
- long providerId = updateProviderDb(providerName, providerFullName, signUpUrl);
- providerNameToId.put(providerName, providerId);
- providerIdToName.put(providerId, providerName);
- }
- }
-
- private long updateProviderDb(
- String providerName, String providerFullName, String signUpUrl) {
- long providerId;
- ContentResolver cr = getContentResolver();
- String where = Im.Provider.NAME + "=?";
- String[] selectionArgs = new String[]{providerName};
- Cursor c = cr.query(Im.Provider.CONTENT_URI, null, where, selectionArgs, null);
+ String className = serviceInfo.name;
+ String srcPath = serviceInfo.applicationInfo.sourceDir;
+ Class pluginClass = loadClass(className, srcPath);
+ if (pluginClass == null) {
+ Log.e(TAG, "Can not load package for plugin " + providerName);
+ continue;
+ }
+ classes.put(providerName, pluginClass);
- try {
- if (c.moveToFirst()) {
- providerId = c.getLong(c.getColumnIndexOrThrow(Im.Provider._ID));
- String origFullName = c.getString(
- c.getColumnIndexOrThrow(Im.Provider.FULLNAME));
- String origCategory = c.getString(
- c.getColumnIndexOrThrow(Im.Provider.CATEGORY));
- String origSignupUrl = c.getString(
- c.getColumnIndexOrThrow(Im.Provider.SIGNUP_URL));
- ContentValues values = new ContentValues();
- if (origFullName == null || !origFullName.equals(providerFullName)) {
- values.put(Im.Provider.FULLNAME, providerFullName);
- }
- if (origCategory == null) {
- values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
- }
- if (origSignupUrl == null || !origSignupUrl.equals(signUpUrl)) {
- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
- }
- if (values.size() > 0) {
- Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
- cr.update(uri, values, null, null);
- }
- } else {
- ContentValues values = new ContentValues(3);
- values.put(Im.Provider.NAME, providerName);
- values.put(Im.Provider.FULLNAME, providerFullName);
- values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
-
- Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
- providerId = ContentUris.parseId(result);
+ Map<String, String> config = loadProviderConfigFromPlugin(pluginClass);
+ if (config == null) {
+ Log.e(TAG, "Can not load config for plugin " + providerName);
+ continue;
}
- } finally {
- c.close();
- }
+ config.put(ImConfigNames.PLUGIN_PATH, srcPath);
+ config.put(ImConfigNames.PLUGIN_CLASS, className);
- return providerId;
+ long providerId = DatabaseUtils.updateProviderDb(getContentResolver(),
+ providerName, providerFullName, signUpUrl, config);
+ providerNameToId.put(providerName, providerId);
+ providerIdToName.put(providerId, providerName);
+ }
}
- private void loadBrandingResources(HashMap<String, Long> providerNameToId,
- HashMap<Long, String> providerIdToName) {
+ private void loadBrandingResources(
+ HashMap<String, Long> providerNameToId, HashMap<Long, String> providerIdToName,
+ HashMap<String, Class> classes) {
mBrandingResources = new HashMap<String, Map<Integer, Integer>>();
// first try load from cache
for (String provider : mProviderNames) {
long providerId = providerNameToId.get(provider);
if (!mBrandingResources.containsKey(provider)) {
- Map<Integer, Integer> resMap = loadBrandingResource(mClassNames.get(provider),
- mSrcPaths.get(provider));
+ Map<Integer, Integer> resMap = loadBrandingResource(classes.get(provider));
if (resMap != null) {
mBrandingResources.put(provider, resMap);
for (int appResId : resMap.keySet()) {
/**
* Load branding resources from one plugin.
*/
- private Map<Integer, Integer> loadBrandingResource(String className, String srcPath) {
- Map retVal = null;
-
- if (LOCAL_DEBUG) log("loadBrandingResource: className=" + className +
- ", srcPath=" + srcPath);
-
- PathClassLoader classLoader = new PathClassLoader(srcPath,
- getCustomClassLoader());
-
+ private Map<Integer, Integer> loadBrandingResource(Class cls) {
try {
- Class cls = classLoader.loadClass(className);
Method m = cls.getMethod("getResourceMap");
-
// TODO: this would still cause a VM verifier exception to be thrown if.
// the landing page Android.mk and AndroidManifest.xml don't include use-library for
// "com.android.im.plugin". This is even with getCustomClassLoader() as the parent
// class loader.
- retVal = (Map)m.invoke(cls.newInstance(), new Object[]{});
+ return (Map)m.invoke(cls.newInstance(), new Object[]{});
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "Failed load the plugin resource map", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed load the plugin resource map", e);
} catch (InstantiationException e) {
} catch (InvocationTargetException e) {
Log.e(TAG, "Failed load the plugin resource map", e);
}
+ return null;
+ }
- return retVal;
+ /**
+ * Load plugin config.
+ */
+ private Map<String, String> loadProviderConfigFromPlugin(Class cls) {
+ try {
+ Method m = cls.getMethod("onBind", Intent.class);
+ com.android.im.plugin.IImPlugin plugin =
+ (com.android.im.plugin.IImPlugin)m.invoke(cls.newInstance(), new Object[]{null});
+ return plugin.getProviderConfig();
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Could not create plugin instance", e);
+ } catch (InstantiationException e) {
+ Log.e(TAG, "Could not create plugin instance", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Could not load config from the plugin", e);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Could not load config from the plugin", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Could not load config from the plugin", e);
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "Could not load config from the plugin", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not load config from the plugin", e);
+ }
+ return null;
}
- private ClassLoader getCustomClassLoader() {
- /*
- // TODO: should not hard code the path!
- ClassLoader retVal = new PathClassLoader("/System/framework/com.android.im.plugin.jar",
- getClassLoader());
- if (LOCAL_DEBUG) log("getCustomClassLoader: " + retVal);
- return retVal;
- */
- return getClassLoader();
+ private Class loadClass(String className, String srcPath) {
+ PathClassLoader loader = new PathClassLoader(srcPath, getClassLoader());
+ try {
+ return loader.loadClass(className);
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Could not find plugin class", e);
+ }
+ return null;
}
private void log(String msg) {
import android.util.Log;
import android.widget.Toast;
+import com.android.im.app.DatabaseUtils;
import com.android.im.IConnectionCreationListener;
import com.android.im.IImConnection;
import com.android.im.IRemoteImService;
config.put(ImConfigNames.PLUGIN_PATH, pluginInfo.mSrcPath);
config.put(ImConfigNames.PLUGIN_CLASS, pluginInfo.mClassName);
- long providerId = updateProviderDb(providerName, providerFullName, signUpUrl,
- config);
+ long providerId = DatabaseUtils.updateProviderDb(getContentResolver(),
+ providerName, providerFullName, signUpUrl, config);
mPlugins.put(providerId, pluginInfo);
}
}
return null;
}
- private long updateProviderDb(String providerName, String providerFullName,
- String signUpUrl, Map<String, String> config) {
- long providerId = -1;
- ContentResolver cr = getContentResolver();
- String where = Im.Provider.NAME + "=?";
- String[] selectionArgs = new String[]{providerName};
- Cursor c = cr.query(Im.Provider.CONTENT_URI,
- null /* projection */,
- where,
- selectionArgs,
- null /* sort order */);
- String pluginVersion = config.get(ImConfigNames.PLUGIN_VERSION);
- boolean versionChanged;
- try {
- if (c.moveToFirst()) {
- providerId = c.getLong(c.getColumnIndexOrThrow(Im.Provider._ID));
- versionChanged = isPluginVersionChanged(cr, providerId, pluginVersion);
- if (versionChanged) {
- // Update the full name, signup url and category each time when the plugin change
- // instead of specific version change because this is called only once.
- // It's ok to update them even the values are not changed.
- // Note that we don't update the provider name because it's used as
- // identifier at some place and the plugin should never change it.
- ContentValues values = new ContentValues(3);
- values.put(Im.Provider.FULLNAME, providerFullName);
- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
- values.put(Im.Provider.CATEGORY, "com.android.im.IMPS_CATEGORY");
- Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
- cr.update(uri, values, null, null);
- }
- } else {
- ContentValues values = new ContentValues(3);
- values.put(Im.Provider.NAME, providerName);
- values.put(Im.Provider.FULLNAME, providerFullName);
- values.put(Im.Provider.CATEGORY, "com.android.im.IMPS_CATEGORY");
- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
-
- Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
- providerId = ContentUris.parseId(result);
- versionChanged = true;
- }
- } finally {
- c.close();
- }
-
- if (versionChanged) {
- ContentValues[] settingValues = new ContentValues[config.size()];
-
- int index = 0;
- for (Map.Entry<String, String> entry : config.entrySet()) {
- ContentValues settingValue = new ContentValues();
- settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
- settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
- settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
- settingValues[index++] = settingValue;
- }
- cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
- }
-
- return providerId;
- }
-
- private boolean isPluginVersionChanged(ContentResolver cr, long providerId,
- String newVersion) {
- String oldVersion = Im.ProviderSettings.getStringValue(cr, providerId,
- ImConfigNames.PLUGIN_VERSION);
- if (oldVersion == null) {
- return true;
- }
- return !oldVersion.equals(newVersion);
- }
-
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);