PluginManager.java revision c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bf
1658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba/*
2658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * Copyright (C) 2009 The Android Open Source Project
3658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba *
4658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * Licensed under the Apache License, Version 2.0 (the "License");
5658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * you may not use this file except in compliance with the License.
6658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * You may obtain a copy of the License at
7658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba *
8658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba *      http://www.apache.org/licenses/LICENSE-2.0
9658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba *
10658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * Unless required by applicable law or agreed to in writing, software
11658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * distributed under the License is distributed on an "AS IS" BASIS,
12658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * See the License for the specific language governing permissions and
14658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * limitations under the License.
15658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba */
16658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
17658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobapackage android.webkit;
18658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
19658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport java.util.ArrayList;
20658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport java.util.List;
21658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
22658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.annotation.SdkConstant;
23658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.annotation.SdkConstant.SdkConstantType;
24658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.Context;
25658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.Intent;
26658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.pm.PackageInfo;
27658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.pm.PackageManager;
28658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.pm.ResolveInfo;
29658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.pm.ServiceInfo;
30658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.pm.Signature;
31658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.content.pm.PackageManager.NameNotFoundException;
32658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobaimport android.util.Log;
33658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
34658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba/**
35658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * Class for managing the relationship between the {@link WebView} and installed
36658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * plugins in the system. You can find this class through
37658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * {@link PluginManager#getInstance}.
38658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba *
39658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba * @hide pending API solidification
40658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba */
41658ab7d787f64987d7c45aae08e5a12a073afe78Grace Klobapublic class PluginManager {
42658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
43658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    /**
44658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * Service Action: A plugin wishes to be loaded in the WebView must provide
45658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * {@link android.content.IntentFilter IntentFilter} that accepts this
46658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * action in their AndroidManifest.xml.
47658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * <p>
48658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * TODO: we may change this to a new PLUGIN_ACTION if this is going to be
49658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * public.
50658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     */
51658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    @SdkConstant(SdkConstantType.SERVICE_ACTION)
52658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
53658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
54658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    /**
55658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * A plugin wishes to be loaded in the WebView must provide this permission
56658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * in their AndroidManifest.xml.
57658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     */
58658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
59658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
60658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    private static final String LOGTAG = "webkit";
61658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
62658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    private static PluginManager mInstance = null;
63658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
64658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    private final Context mContext;
65658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
66c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger    private ArrayList<PackageInfo> mPackageInfoCache;
67c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
68658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    private PluginManager(Context context) {
69658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        mContext = context;
70c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        mPackageInfoCache = new ArrayList<PackageInfo>();
71658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    }
72658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
73658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    public static synchronized PluginManager getInstance(Context context) {
74658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        if (mInstance == null) {
75658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba            if (context == null) {
76658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                throw new IllegalStateException(
77658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                        "First call to PluginManager need a valid context.");
78658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba            }
79658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba            mInstance = new PluginManager(context);
80658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        }
81658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        return mInstance;
82658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    }
83658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
84658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    /**
85658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * Signal the WebCore thread to refresh its list of plugins. Use this if the
86658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * directory contents of one of the plugin directories has been modified and
87658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * needs its changes reflecting. May cause plugin load and/or unload.
88658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     *
89658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     * @param reloadOpenPages Set to true to reload all open pages.
90658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba     */
91658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    public void refreshPlugins(boolean reloadOpenPages) {
92658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        BrowserFrame.sJavaBridge.obtainMessage(
93658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                JWebCoreJavaBridge.REFRESH_PLUGINS, reloadOpenPages)
94658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                .sendToTarget();
95658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    }
96658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba
9737eb3c35152fadc2541ebe6af3e39b246ca87fb7Derek Sollenberger    String[] getPluginDirectories() {
98c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
99658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        ArrayList<String> directories = new ArrayList<String>();
100658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        PackageManager pm = mContext.getPackageManager();
101658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(
102658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                PLUGIN_ACTION), PackageManager.GET_SERVICES);
103c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
104c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        synchronized(mPackageInfoCache) {
105c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
106c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger            // clear the list of existing packageInfo objects
107c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger            mPackageInfoCache.clear();
108c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
109c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger            for (ResolveInfo info : plugins) {
110c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                ServiceInfo serviceInfo = info.serviceInfo;
111c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (serviceInfo == null) {
112c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    Log.w(LOGTAG, "Ignore bad plugin");
113c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
114c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
115c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                PackageInfo pkgInfo;
116c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                try {
117c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    pkgInfo = pm.getPackageInfo(serviceInfo.packageName,
118c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                                    PackageManager.GET_PERMISSIONS
119c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                                    | PackageManager.GET_SIGNATURES);
120c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                } catch (NameNotFoundException e) {
121c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName);
122c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
123c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
124c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (pkgInfo == null) {
125c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
126c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
127c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                String directory = pkgInfo.applicationInfo.dataDir + "/lib";
128c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (directories.contains(directory)) {
129c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
130c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
131c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                String permissions[] = pkgInfo.requestedPermissions;
132c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (permissions == null) {
133c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
134c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
135c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                boolean permissionOk = false;
136c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                for (String permit : permissions) {
137c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    if (PLUGIN_PERMISSION.equals(permit)) {
138c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                        permissionOk = true;
139c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                        break;
140c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    }
141c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
142c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (!permissionOk) {
143c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
144c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
145c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                Signature signatures[] = pkgInfo.signatures;
146c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (signatures == null) {
147c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
148c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
149c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                boolean signatureMatch = false;
150c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                for (Signature signature : signatures) {
151c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    // TODO: check signature against Google provided one
152c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    signatureMatch = true;
153658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                    break;
154658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba                }
155c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (!signatureMatch) {
156c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    continue;
157c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
158c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                mPackageInfoCache.add(pkgInfo);
159c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                directories.add(directory);
160658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba            }
161658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        }
16237eb3c35152fadc2541ebe6af3e39b246ca87fb7Derek Sollenberger
163658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba        return directories.toArray(new String[directories.size()]);
164658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba    }
165fd055b2d7cebd6693925a30adad6c66a697af407Grace Kloba
166c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger    String getPluginsAPKName(String pluginLib) {
167c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
168c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        // basic error checking on input params
169c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        if (pluginLib == null || pluginLib.length() == 0) {
170c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger            return null;
171c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        }
172c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
173c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        // must be synchronized to ensure the consistency of the cache
174c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        synchronized(mPackageInfoCache) {
175c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger            for (PackageInfo pkgInfo : mPackageInfoCache) {
176c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                if (pluginLib.startsWith(pkgInfo.applicationInfo.dataDir)) {
177c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                    return pkgInfo.packageName;
178c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger                }
179c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger            }
180c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        }
181c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
182c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        // if no apk was found then return null
183c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger        return null;
184c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger    }
185c0b8a96d28c55cb51e4f9e1f85c8d3ecf1ff13bfDerek Sollenberger
186fd055b2d7cebd6693925a30adad6c66a697af407Grace Kloba    String getPluginSharedDataDirectory() {
187fd055b2d7cebd6693925a30adad6c66a697af407Grace Kloba        return mContext.getDir("plugins", 0).getPath();
188fd055b2d7cebd6693925a30adad6c66a697af407Grace Kloba    }
189658ab7d787f64987d7c45aae08e5a12a073afe78Grace Kloba}
190