From cb37e71509da43e0d8d809591b09e8f5a582b5cd Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Tue, 24 Nov 2009 14:58:09 -0500 Subject: [PATCH] Refactoring plugins to use new java interfaces. This change adds 3 new interfaces for plugins to the framework. This change also includes extensive cleanup as we consolidate internal plugin functions into the pluginManager. Also using the new interfaces we no longer need to pass additional parameters in quite a few methods. --- core/java/android/webkit/PluginActivity.java | 40 +++--- core/java/android/webkit/PluginManager.java | 129 ++++++++++++++++-- core/java/android/webkit/PluginUtil.java | 61 --------- core/java/android/webkit/WebViewCore.java | 55 +++++--- .../android/webkit/plugin/NativePlugin.java | 37 +++++ .../webkit/plugin/SurfaceDrawingModel.java | 37 +++++ .../android/webkit/plugin/WebkitPlugin.java | 36 +++++ 7 files changed, 287 insertions(+), 108 deletions(-) delete mode 100644 core/java/android/webkit/PluginUtil.java create mode 100644 core/java/android/webkit/plugin/NativePlugin.java create mode 100644 core/java/android/webkit/plugin/SurfaceDrawingModel.java create mode 100644 core/java/android/webkit/plugin/WebkitPlugin.java diff --git a/core/java/android/webkit/PluginActivity.java b/core/java/android/webkit/PluginActivity.java index cda7b59dfc06a..22d6ccb7eba29 100644 --- a/core/java/android/webkit/PluginActivity.java +++ b/core/java/android/webkit/PluginActivity.java @@ -19,6 +19,8 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; +import android.webkit.plugin.SurfaceDrawingModel; +import android.webkit.plugin.WebkitPlugin; /** * This activity is invoked when a plugin elects to go into full screen mode. @@ -28,8 +30,6 @@ public class PluginActivity extends Activity { /* package */ static final String INTENT_EXTRA_PACKAGE_NAME = "android.webkit.plugin.PACKAGE_NAME"; - /* package */ static final String INTENT_EXTRA_CLASS_NAME = - "android.webkit.plugin.CLASS_NAME"; /* package */ static final String INTENT_EXTRA_NPP_INSTANCE = "android.webkit.plugin.NPP_INSTANCE"; @@ -42,25 +42,31 @@ public class PluginActivity extends Activity { if (intent == null) { // No intent means no class to lookup. finish(); + return; } - final String packageName = - intent.getStringExtra(INTENT_EXTRA_PACKAGE_NAME); - final String className = intent.getStringExtra(INTENT_EXTRA_CLASS_NAME); + final String pkgName = intent.getStringExtra(INTENT_EXTRA_PACKAGE_NAME); final int npp = intent.getIntExtra(INTENT_EXTRA_NPP_INSTANCE, -1); - // Retrieve the PluginStub implemented in packageName.className - PluginStub stub = - PluginUtil.getPluginStub(this, packageName, className); - if (stub != null) { - View pluginView = stub.getFullScreenView(npp, this); - if (pluginView != null) { - setContentView(pluginView); - } else { - // No custom full-sreen view returned by the plugin, odd but - // just in case, finish the activity. - finish(); - } + // XXX retrieve the existing object instead of creating a new one + WebkitPlugin plugin = PluginManager.getInstance(null).getPluginInstance(pkgName, npp); + + if (plugin == null) { + //TODO log error + finish(); + return; + } + SurfaceDrawingModel fullScreenSurface = plugin.getFullScreenSurface(); + if(fullScreenSurface == null) { + //TODO log error + finish(); + return; + } + View pluginView = fullScreenSurface.getSurface(); + if (pluginView != null) { + setContentView(pluginView); } else { + // No custom full-sreen view returned by the plugin, odd but + // just in case, finish the activity. finish(); } } diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java index 4588f46e57714..88429c50442f3 100644 --- a/core/java/android/webkit/PluginManager.java +++ b/core/java/android/webkit/PluginManager.java @@ -31,6 +31,8 @@ import android.content.pm.Signature; import android.content.pm.PackageManager.NameNotFoundException; import android.os.SystemProperties; import android.util.Log; +import android.webkit.plugin.NativePlugin; +import android.webkit.plugin.WebkitPlugin; /** * Class for managing the relationship between the {@link WebView} and installed @@ -41,6 +43,12 @@ import android.util.Log; */ public class PluginManager { + private class PluginInfo { + public PackageInfo packageInfo; + public boolean isNative; + public Class pluginClass; + } + /** * Service Action: A plugin wishes to be loaded in the WebView must provide * {@link android.content.IntentFilter IntentFilter} that accepts this @@ -60,11 +68,14 @@ public class PluginManager { private static final String LOGTAG = "webkit"; + private static final String PLUGIN_TYPE = "type"; + private static final String TYPE_NATIVE = "native"; + private static PluginManager mInstance = null; private final Context mContext; - private ArrayList mPackageInfoCache; + private ArrayList mPluginInfoCache; // Only plugin matches one of the signatures in the list can be loaded // inside the WebView process @@ -76,7 +87,7 @@ public class PluginManager { private PluginManager(Context context) { mContext = context; - mPackageInfoCache = new ArrayList(); + mPluginInfoCache = new ArrayList(); } public static synchronized PluginManager getInstance(Context context) { @@ -108,35 +119,44 @@ public class PluginManager { ArrayList directories = new ArrayList(); PackageManager pm = mContext.getPackageManager(); List plugins = pm.queryIntentServices(new Intent( - PLUGIN_ACTION), PackageManager.GET_SERVICES); + PLUGIN_ACTION), PackageManager.GET_SERVICES + | PackageManager.GET_META_DATA); - synchronized(mPackageInfoCache) { + synchronized(mPluginInfoCache) { // clear the list of existing packageInfo objects - mPackageInfoCache.clear(); + mPluginInfoCache.clear(); for (ResolveInfo info : plugins) { + + // retrieve the plugin's service information ServiceInfo serviceInfo = info.serviceInfo; if (serviceInfo == null) { Log.w(LOGTAG, "Ignore bad plugin"); continue; } + + // retrieve information from the plugin's manifest PackageInfo pkgInfo; try { pkgInfo = pm.getPackageInfo(serviceInfo.packageName, PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES); } catch (NameNotFoundException e) { - Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName); + Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); continue; } if (pkgInfo == null) { continue; } + + // check if their is a conflict in the lib directory names String directory = pkgInfo.applicationInfo.dataDir + "/lib"; if (directories.contains(directory)) { continue; } + + // check if the plugin has the required permissions String permissions[] = pkgInfo.requestedPermissions; if (permissions == null) { continue; @@ -151,6 +171,8 @@ public class PluginManager { if (!permissionOk) { continue; } + + // check to ensure the plugin is properly signed Signature signatures[] = pkgInfo.signatures; if (signatures == null) { continue; @@ -169,7 +191,51 @@ public class PluginManager { continue; } } - mPackageInfoCache.add(pkgInfo); + + PluginInfo pluginInfo = new PluginInfo(); + pluginInfo.packageInfo = pkgInfo; + + // determine the type of plugin from the manifest + if (serviceInfo.metaData == null) { + Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined"); + continue; + } + + String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE); + if (TYPE_NATIVE.equals(pluginType)) { + pluginInfo.isNative = true; + } else { + Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType); + continue; + } + + try { + Class cls = getPluginClass(serviceInfo.packageName, serviceInfo.name); + + boolean classFound = false; + for(Class implemented : cls.getInterfaces()) { + if (pluginInfo.isNative && implemented.equals(NativePlugin.class)) { + pluginInfo.pluginClass = cls.asSubclass(WebkitPlugin.class); + classFound = true; + break; + } + } + + if (!classFound) { + Log.e(LOGTAG, "The plugin's class'" + serviceInfo.name + "' does not extend the appropriate interface."); + continue; + } + + } catch (NameNotFoundException e) { + Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); + continue; + } catch (ClassNotFoundException e) { + Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name); + continue; + } + + // if all checks have passed then make the plugin available + mPluginInfoCache.add(pluginInfo); directories.add(directory); } } @@ -177,6 +243,7 @@ public class PluginManager { return directories.toArray(new String[directories.size()]); } + /* package */ String getPluginsAPKName(String pluginLib) { // basic error checking on input params @@ -185,8 +252,9 @@ public class PluginManager { } // must be synchronized to ensure the consistency of the cache - synchronized(mPackageInfoCache) { - for (PackageInfo pkgInfo : mPackageInfoCache) { + synchronized(mPluginInfoCache) { + for (PluginInfo pluginInfo : mPluginInfoCache) { + PackageInfo pkgInfo = pluginInfo.packageInfo; if (pluginLib.startsWith(pkgInfo.applicationInfo.dataDir)) { return pkgInfo.packageName; } @@ -200,4 +268,47 @@ public class PluginManager { String getPluginSharedDataDirectory() { return mContext.getDir("plugins", 0).getPath(); } + + /* package */ + WebkitPlugin getPluginInstance(String pkgName, int npp) { + + // must be synchronized to ensure the consistency of the cache + synchronized(mPluginInfoCache) { + + // lookup plugin based on pkgName and instantiate if possible. + for (PluginInfo pluginInfo : mPluginInfoCache) { + + if (pluginInfo.packageInfo.packageName.equals(pkgName)) { + + try { + WebkitPlugin webkitPlugin = pluginInfo.pluginClass.newInstance(); + + if (pluginInfo.isNative) { + NativePlugin nativePlugin = (NativePlugin) webkitPlugin; + nativePlugin.initializePlugin(npp, mContext); + } + + return webkitPlugin; + } catch (Exception e) { + // Any number of things could have happened. Log the exception and + // return null. Careful not to use Log.e(LOGTAG, "String", e) + // because that reports the exception to the checkin service. + Log.e(LOGTAG, Log.getStackTraceString(e)); + } + break; + } + } + } + return null; + } + + /* package */ + Class getPluginClass(String packageName, String className) + throws NameNotFoundException, ClassNotFoundException { + Context pluginContext = mContext.createPackageContext(packageName, + Context.CONTEXT_INCLUDE_CODE | + Context.CONTEXT_IGNORE_SECURITY); + ClassLoader pluginCL = pluginContext.getClassLoader(); + return pluginCL.loadClass(className); + } } diff --git a/core/java/android/webkit/PluginUtil.java b/core/java/android/webkit/PluginUtil.java deleted file mode 100644 index 33ccf9d205fc9..0000000000000 --- a/core/java/android/webkit/PluginUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 android.webkit; - -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Log; - -class PluginUtil { - - private static final String LOGTAG = "PluginUtil"; - - /** - * - * @param packageName the name of the apk where the class can be found - * @param className the fully qualified name of a subclass of PluginStub - */ - /* package */ - static PluginStub getPluginStub(Context context, String packageName, - String className) { - try { - Class stubClass = getPluginClass(context, packageName, className); - Object stubObject = stubClass.newInstance(); - - if (stubObject instanceof PluginStub) { - return (PluginStub) stubObject; - } else { - Log.e(LOGTAG, "The plugin class is not of type PluginStub"); - } - } catch (Exception e) { - // Any number of things could have happened. Log the exception and - // return null. Careful not to use Log.e(LOGTAG, "String", e) - // because that reports the exception to the checkin service. - Log.e(LOGTAG, Log.getStackTraceString(e)); - } - return null; - } - - /* package */ - static Class getPluginClass(Context context, String packageName, - String className) throws NameNotFoundException, ClassNotFoundException { - Context pluginContext = context.createPackageContext(packageName, - Context.CONTEXT_INCLUDE_CODE | - Context.CONTEXT_IGNORE_SECURITY); - ClassLoader pluginCL = pluginContext.getClassLoader(); - return pluginCL.loadClass(className); - } -} diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 769f0b1a6d3f8..be36fe0fb97cf 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -41,6 +41,8 @@ import android.view.KeyEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; +import android.webkit.plugin.SurfaceDrawingModel; +import android.webkit.plugin.WebkitPlugin; import java.util.ArrayList; import java.util.Collection; @@ -2175,15 +2177,16 @@ final class WebViewCore { return null; } - String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); + PluginManager pluginManager = PluginManager.getInstance(null); + + String pkgName = pluginManager.getPluginsAPKName(libName); if (pkgName == null) { Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); return null; } - Class pluginClass = null; try { - pluginClass = PluginUtil.getPluginClass(mWebView.getContext(), pkgName, clsName); + return pluginManager.getPluginClass(pkgName, clsName); } catch (NameNotFoundException e) { Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); } catch (ClassNotFoundException e) { @@ -2191,12 +2194,29 @@ final class WebViewCore { ") in the apk (" + pkgName + ")"); } - return pluginClass; + return null; } - + + private WebkitPlugin createPluginJavaInstance(String libName, int npp) { + + if (mWebView == null) { + return null; + } + + PluginManager pluginManager = PluginManager.getInstance(null); + + String pkgName = pluginManager.getPluginsAPKName(libName); + if (pkgName == null) { + Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); + return null; + } + + return pluginManager.getPluginInstance(pkgName, npp); + } + // called by JNI. PluginWidget function to launch an activity and overlays // the activity with the View provided by the plugin class. - private void startFullScreenPluginActivity(String libName, String clsName, int npp) { + private void startFullScreenPluginActivity(String libName, int npp) { if (mWebView == null) { return; } @@ -2209,40 +2229,33 @@ final class WebViewCore { Intent intent = new Intent("android.intent.webkit.PLUGIN"); intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName); - intent.putExtra(PluginActivity.INTENT_EXTRA_CLASS_NAME, clsName); intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp); mWebView.getContext().startActivity(intent); } // called by JNI. PluginWidget functions for creating an embedded View for // the surface drawing model. - private ViewManager.ChildView createSurface(String libName, String clsName, - int npp, int x, int y, int width, int height) { + private ViewManager.ChildView createSurface(WebkitPlugin webkitPlugin, + int x, int y, int width, int height) { + if (mWebView == null) { return null; } - String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); - if (pkgName == null) { - Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); + SurfaceDrawingModel embeddedSurface = webkitPlugin.getEmbeddedSurface(); + if(embeddedSurface == null) { + Log.e(LOGTAG, "Attempted to create an embedded surface with a null drawing model"); return null; } - PluginStub stub =PluginUtil.getPluginStub(mWebView.getContext(),pkgName, clsName); - if (stub == null) { - Log.e(LOGTAG, "Unable to find plugin class (" + clsName + - ") in the apk (" + pkgName + ")"); - return null; - } - - View pluginView = stub.getEmbeddedView(npp, mWebView.getContext()); + View pluginView = embeddedSurface.getSurface(); ViewManager.ChildView view = mWebView.mViewManager.createView(); view.mView = pluginView; view.attachView(x, y, width, height); return view; } - + private void updateSurface(ViewManager.ChildView childView, int x, int y, int width, int height) { childView.attachView(x, y, width, height); diff --git a/core/java/android/webkit/plugin/NativePlugin.java b/core/java/android/webkit/plugin/NativePlugin.java new file mode 100644 index 0000000000000..5019c35e74ef8 --- /dev/null +++ b/core/java/android/webkit/plugin/NativePlugin.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package android.webkit.plugin; + +import android.content.Context; + +/** + * + * @hide pending API solidification + */ +public interface NativePlugin extends WebkitPlugin { + + void initializePlugin(int npp, Context context); + +} diff --git a/core/java/android/webkit/plugin/SurfaceDrawingModel.java b/core/java/android/webkit/plugin/SurfaceDrawingModel.java new file mode 100644 index 0000000000000..93ba46667f274 --- /dev/null +++ b/core/java/android/webkit/plugin/SurfaceDrawingModel.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package android.webkit.plugin; + +import android.view.View; + +/** + * + * @hide pending API solidification + */ +public interface SurfaceDrawingModel { + + public View getSurface(); + +} diff --git a/core/java/android/webkit/plugin/WebkitPlugin.java b/core/java/android/webkit/plugin/WebkitPlugin.java new file mode 100644 index 0000000000000..3d13c1cbe0861 --- /dev/null +++ b/core/java/android/webkit/plugin/WebkitPlugin.java @@ -0,0 +1,36 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package android.webkit.plugin; + +/** + * + * @hide pending API solidification + */ +public interface WebkitPlugin { + + SurfaceDrawingModel getEmbeddedSurface(); + SurfaceDrawingModel getFullScreenSurface(); + +}