Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb37e715 authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

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.
parent e11a1b4d
Loading
Loading
Loading
Loading
+23 −17
Original line number Diff line number Diff line
@@ -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,17 +42,26 @@ 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);
        // 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 {
@@ -60,8 +69,5 @@ public class PluginActivity extends Activity {
            // just in case, finish the activity.
            finish();
        }
        } else {
            finish();
        }
    }
}
+120 −9
Original line number Diff line number Diff line
@@ -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<? extends WebkitPlugin> 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<PackageInfo> mPackageInfoCache;
    private ArrayList<PluginInfo> 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<PackageInfo>();
        mPluginInfoCache = new ArrayList<PluginInfo>();
    }

    public static synchronized PluginManager getInstance(Context context) {
@@ -108,35 +119,44 @@ public class PluginManager {
        ArrayList<String> directories = new ArrayList<String>();
        PackageManager pm = mContext.getPackageManager();
        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
            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);
    }
}
+0 −61
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+34 −21
Original line number Diff line number Diff line
@@ -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,33 +2229,26 @@ 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) {
        if (mWebView == null) {
            return null;
        }
    private ViewManager.ChildView createSurface(WebkitPlugin webkitPlugin,
            int x, int y, int width, int height) {
        
        String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName);
        if (pkgName == null) {
            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
        if (mWebView == null) {
            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 + ")");
        SurfaceDrawingModel embeddedSurface = webkitPlugin.getEmbeddedSurface();
        if(embeddedSurface == null) {
            Log.e(LOGTAG, "Attempted to create an embedded surface with a null drawing model");
            return null;
        }

        View pluginView = stub.getEmbeddedView(npp, mWebView.getContext());
        View pluginView = embeddedSurface.getSurface();

        ViewManager.ChildView view = mWebView.mViewManager.createView();
        view.mView = pluginView;
+37 −0
Original line number Diff line number Diff line
/*
 * 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);

}
Loading