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.
This commit is contained in:
Derek Sollenberger
2009-11-24 14:58:09 -05:00
parent e11a1b4d1a
commit cb37e71509
7 changed files with 287 additions and 108 deletions

View File

@@ -19,6 +19,8 @@ import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; 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. * 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 = /* package */ static final String INTENT_EXTRA_PACKAGE_NAME =
"android.webkit.plugin.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 = /* package */ static final String INTENT_EXTRA_NPP_INSTANCE =
"android.webkit.plugin.NPP_INSTANCE"; "android.webkit.plugin.NPP_INSTANCE";
@@ -42,25 +42,31 @@ public class PluginActivity extends Activity {
if (intent == null) { if (intent == null) {
// No intent means no class to lookup. // No intent means no class to lookup.
finish(); finish();
return;
} }
final String packageName = final String pkgName = intent.getStringExtra(INTENT_EXTRA_PACKAGE_NAME);
intent.getStringExtra(INTENT_EXTRA_PACKAGE_NAME);
final String className = intent.getStringExtra(INTENT_EXTRA_CLASS_NAME);
final int npp = intent.getIntExtra(INTENT_EXTRA_NPP_INSTANCE, -1); 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) { // XXX retrieve the existing object instead of creating a new one
View pluginView = stub.getFullScreenView(npp, this); WebkitPlugin plugin = PluginManager.getInstance(null).getPluginInstance(pkgName, npp);
if (pluginView != null) {
setContentView(pluginView); if (plugin == null) {
} else { //TODO log error
// No custom full-sreen view returned by the plugin, odd but finish();
// just in case, finish the activity. return;
finish(); }
} SurfaceDrawingModel fullScreenSurface = plugin.getFullScreenSurface();
if(fullScreenSurface == null) {
//TODO log error
finish();
return;
}
View pluginView = fullScreenSurface.getSurface();
if (pluginView != null) {
setContentView(pluginView);
} else { } else {
// No custom full-sreen view returned by the plugin, odd but
// just in case, finish the activity.
finish(); finish();
} }
} }

View File

@@ -31,6 +31,8 @@ import android.content.pm.Signature;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.util.Log; 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 * Class for managing the relationship between the {@link WebView} and installed
@@ -41,6 +43,12 @@ import android.util.Log;
*/ */
public class PluginManager { public class PluginManager {
private class PluginInfo {
public PackageInfo packageInfo;
public boolean isNative;
public Class<? extends WebkitPlugin> pluginClass;
}
/** /**
* Service Action: A plugin wishes to be loaded in the WebView must provide * Service Action: A plugin wishes to be loaded in the WebView must provide
* {@link android.content.IntentFilter IntentFilter} that accepts this * {@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 LOGTAG = "webkit";
private static final String PLUGIN_TYPE = "type";
private static final String TYPE_NATIVE = "native";
private static PluginManager mInstance = null; private static PluginManager mInstance = null;
private final Context mContext; private final Context mContext;
private ArrayList<PackageInfo> mPackageInfoCache; private ArrayList<PluginInfo> mPluginInfoCache;
// Only plugin matches one of the signatures in the list can be loaded // Only plugin matches one of the signatures in the list can be loaded
// inside the WebView process // inside the WebView process
@@ -76,7 +87,7 @@ public class PluginManager {
private PluginManager(Context context) { private PluginManager(Context context) {
mContext = context; mContext = context;
mPackageInfoCache = new ArrayList<PackageInfo>(); mPluginInfoCache = new ArrayList<PluginInfo>();
} }
public static synchronized PluginManager getInstance(Context context) { public static synchronized PluginManager getInstance(Context context) {
@@ -108,35 +119,44 @@ public class PluginManager {
ArrayList<String> directories = new ArrayList<String>(); ArrayList<String> directories = new ArrayList<String>();
PackageManager pm = mContext.getPackageManager(); PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> plugins = pm.queryIntentServices(new Intent( List<ResolveInfo> 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 // clear the list of existing packageInfo objects
mPackageInfoCache.clear(); mPluginInfoCache.clear();
for (ResolveInfo info : plugins) { for (ResolveInfo info : plugins) {
// retrieve the plugin's service information
ServiceInfo serviceInfo = info.serviceInfo; ServiceInfo serviceInfo = info.serviceInfo;
if (serviceInfo == null) { if (serviceInfo == null) {
Log.w(LOGTAG, "Ignore bad plugin"); Log.w(LOGTAG, "Ignore bad plugin");
continue; continue;
} }
// retrieve information from the plugin's manifest
PackageInfo pkgInfo; PackageInfo pkgInfo;
try { try {
pkgInfo = pm.getPackageInfo(serviceInfo.packageName, pkgInfo = pm.getPackageInfo(serviceInfo.packageName,
PackageManager.GET_PERMISSIONS PackageManager.GET_PERMISSIONS
| PackageManager.GET_SIGNATURES); | PackageManager.GET_SIGNATURES);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName); Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName);
continue; continue;
} }
if (pkgInfo == null) { if (pkgInfo == null) {
continue; continue;
} }
// check if their is a conflict in the lib directory names
String directory = pkgInfo.applicationInfo.dataDir + "/lib"; String directory = pkgInfo.applicationInfo.dataDir + "/lib";
if (directories.contains(directory)) { if (directories.contains(directory)) {
continue; continue;
} }
// check if the plugin has the required permissions
String permissions[] = pkgInfo.requestedPermissions; String permissions[] = pkgInfo.requestedPermissions;
if (permissions == null) { if (permissions == null) {
continue; continue;
@@ -151,6 +171,8 @@ public class PluginManager {
if (!permissionOk) { if (!permissionOk) {
continue; continue;
} }
// check to ensure the plugin is properly signed
Signature signatures[] = pkgInfo.signatures; Signature signatures[] = pkgInfo.signatures;
if (signatures == null) { if (signatures == null) {
continue; continue;
@@ -169,7 +191,51 @@ public class PluginManager {
continue; 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); directories.add(directory);
} }
} }
@@ -177,6 +243,7 @@ public class PluginManager {
return directories.toArray(new String[directories.size()]); return directories.toArray(new String[directories.size()]);
} }
/* package */
String getPluginsAPKName(String pluginLib) { String getPluginsAPKName(String pluginLib) {
// basic error checking on input params // basic error checking on input params
@@ -185,8 +252,9 @@ public class PluginManager {
} }
// must be synchronized to ensure the consistency of the cache // must be synchronized to ensure the consistency of the cache
synchronized(mPackageInfoCache) { synchronized(mPluginInfoCache) {
for (PackageInfo pkgInfo : mPackageInfoCache) { for (PluginInfo pluginInfo : mPluginInfoCache) {
PackageInfo pkgInfo = pluginInfo.packageInfo;
if (pluginLib.startsWith(pkgInfo.applicationInfo.dataDir)) { if (pluginLib.startsWith(pkgInfo.applicationInfo.dataDir)) {
return pkgInfo.packageName; return pkgInfo.packageName;
} }
@@ -200,4 +268,47 @@ public class PluginManager {
String getPluginSharedDataDirectory() { String getPluginSharedDataDirectory() {
return mContext.getDir("plugins", 0).getPath(); 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);
}
} }

View File

@@ -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);
}
}

View File

@@ -41,6 +41,8 @@ import android.view.KeyEvent;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.webkit.plugin.SurfaceDrawingModel;
import android.webkit.plugin.WebkitPlugin;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -2175,15 +2177,16 @@ final class WebViewCore {
return null; return null;
} }
String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); PluginManager pluginManager = PluginManager.getInstance(null);
String pkgName = pluginManager.getPluginsAPKName(libName);
if (pkgName == null) { if (pkgName == null) {
Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
return null; return null;
} }
Class<?> pluginClass = null;
try { try {
pluginClass = PluginUtil.getPluginClass(mWebView.getContext(), pkgName, clsName); return pluginManager.getPluginClass(pkgName, clsName);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@@ -2191,12 +2194,29 @@ final class WebViewCore {
") in the apk (" + pkgName + ")"); ") 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 // called by JNI. PluginWidget function to launch an activity and overlays
// the activity with the View provided by the plugin class. // 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) { if (mWebView == null) {
return; return;
} }
@@ -2209,40 +2229,33 @@ final class WebViewCore {
Intent intent = new Intent("android.intent.webkit.PLUGIN"); Intent intent = new Intent("android.intent.webkit.PLUGIN");
intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName); intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName);
intent.putExtra(PluginActivity.INTENT_EXTRA_CLASS_NAME, clsName);
intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp); intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp);
mWebView.getContext().startActivity(intent); mWebView.getContext().startActivity(intent);
} }
// called by JNI. PluginWidget functions for creating an embedded View for // called by JNI. PluginWidget functions for creating an embedded View for
// the surface drawing model. // the surface drawing model.
private ViewManager.ChildView createSurface(String libName, String clsName, private ViewManager.ChildView createSurface(WebkitPlugin webkitPlugin,
int npp, int x, int y, int width, int height) { int x, int y, int width, int height) {
if (mWebView == null) { if (mWebView == null) {
return null; return null;
} }
String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); SurfaceDrawingModel embeddedSurface = webkitPlugin.getEmbeddedSurface();
if (pkgName == null) { if(embeddedSurface == null) {
Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); Log.e(LOGTAG, "Attempted to create an embedded surface with a null drawing model");
return null; return null;
} }
PluginStub stub =PluginUtil.getPluginStub(mWebView.getContext(),pkgName, clsName); View pluginView = embeddedSurface.getSurface();
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());
ViewManager.ChildView view = mWebView.mViewManager.createView(); ViewManager.ChildView view = mWebView.mViewManager.createView();
view.mView = pluginView; view.mView = pluginView;
view.attachView(x, y, width, height); view.attachView(x, y, width, height);
return view; return view;
} }
private void updateSurface(ViewManager.ChildView childView, int x, int y, private void updateSurface(ViewManager.ChildView childView, int x, int y,
int width, int height) { int width, int height) {
childView.attachView(x, y, width, height); childView.attachView(x, y, width, height);

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}