WebViewFactory.java revision 810c052d9b117217152c2a609ccec056a2a61d1e
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.webkit; 18 19import android.app.ActivityManagerInternal; 20import android.app.AppGlobals; 21import android.content.Context; 22import android.content.pm.PackageManager; 23import android.os.Build; 24import android.os.Process; 25import android.os.RemoteException; 26import android.os.ServiceManager; 27import android.os.StrictMode; 28import android.util.AndroidRuntimeException; 29import android.util.Log; 30import com.android.server.LocalServices; 31import dalvik.system.VMRuntime; 32 33import java.io.File; 34 35import com.android.internal.os.Zygote; 36 37/** 38 * Top level factory, used creating all the main WebView implementation classes. 39 * 40 * @hide 41 */ 42public final class WebViewFactory { 43 44 private static final String CHROMIUM_WEBVIEW_FACTORY = 45 "com.android.webview.chromium.WebViewChromiumFactoryProvider"; 46 47 private static final String NULL_WEBVIEW_FACTORY = 48 "com.android.webview.nullwebview.NullWebViewFactoryProvider"; 49 50 // TODO(torne): we need to use a system property instead of hardcoding the library paths to 51 // enable it to be changed when a webview update apk is installed. 52 private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_32 = 53 "/system/lib/libwebviewchromium.so"; 54 private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_64 = 55 "/system/lib64/libwebviewchromium.so"; 56 private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 = 57 "/data/misc/shared_relro/libwebviewchromium32.relro"; 58 private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 = 59 "/data/misc/shared_relro/libwebviewchromium64.relro"; 60 61 private static final String LOGTAG = "WebViewFactory"; 62 63 private static final boolean DEBUG = false; 64 65 // Cache the factory both for efficiency, and ensure any one process gets all webviews from the 66 // same provider. 67 private static WebViewFactoryProvider sProviderInstance; 68 private static final Object sProviderLock = new Object(); 69 private static boolean sAddressSpaceReserved = false; 70 71 public static String getWebViewPackageName() { 72 // TODO: Make this dynamic based on resource configuration. 73 return "com.android.webview"; 74 } 75 76 static WebViewFactoryProvider getProvider() { 77 synchronized (sProviderLock) { 78 // For now the main purpose of this function (and the factory abstraction) is to keep 79 // us honest and minimize usage of WebView internals when binding the proxy. 80 if (sProviderInstance != null) return sProviderInstance; 81 82 loadNativeLibrary(); 83 84 Class<WebViewFactoryProvider> providerClass; 85 try { 86 providerClass = getFactoryClass(); 87 } catch (ClassNotFoundException e) { 88 Log.e(LOGTAG, "error loading provider", e); 89 throw new AndroidRuntimeException(e); 90 } 91 92 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); 93 try { 94 sProviderInstance = providerClass.newInstance(); 95 if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance); 96 return sProviderInstance; 97 } catch (Exception e) { 98 Log.e(LOGTAG, "error instantiating provider", e); 99 throw new AndroidRuntimeException(e); 100 } finally { 101 StrictMode.setThreadPolicy(oldPolicy); 102 } 103 } 104 } 105 106 private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException { 107 try { 108 Context webViewContext = AppGlobals.getInitialApplication().createPackageContext( 109 getWebViewPackageName(), 110 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 111 ClassLoader clazzLoader = webViewContext.getClassLoader(); 112 return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true, 113 clazzLoader); 114 } catch (PackageManager.NameNotFoundException e) { 115 Log.e(LOGTAG, "Chromium WebView package does not exist"); 116 return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY); 117 } 118 } 119 120 /** 121 * Perform any WebView loading preparations that must happen in the zygote. 122 * Currently, this means allocating address space to load the real JNI library later. 123 */ 124 public static void prepareWebViewInZygote() { 125 try { 126 System.loadLibrary("webviewchromium_loader"); 127 sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32, 128 CHROMIUM_WEBVIEW_NATIVE_LIB_64); 129 if (sAddressSpaceReserved) { 130 if (DEBUG) Log.v(LOGTAG, "address space reserved"); 131 } else { 132 Log.e(LOGTAG, "reserving address space failed"); 133 } 134 } catch (Throwable t) { 135 // Log and discard errors at this stage as we must not crash the zygote. 136 Log.e(LOGTAG, "error preparing native loader", t); 137 } 138 } 139 140 /** 141 * Perform any WebView loading preparations that must happen at boot from the system server, 142 * after the package manager has started. 143 * This must be called in the system server. 144 * Currently, this means spawning the child processes which will create the relro files. 145 */ 146 public static void prepareWebViewInSystemServer() { 147 if (DEBUG) Log.v(LOGTAG, "creating relro files"); 148 if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) { 149 createRelroFile(true /* is64Bit */); 150 } 151 if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) { 152 createRelroFile(false /* is64Bit */); 153 } 154 } 155 156 private static void createRelroFile(final boolean is64Bit) { 157 String abi = is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]; 158 159 // crashHandler is invoked by the ActivityManagerService when the isolated process crashes. 160 Runnable crashHandler = new Runnable() { 161 @Override 162 public void run() { 163 try { 164 getUpdateService().notifyRelroCreationCompleted(is64Bit, false); 165 } catch (RemoteException e) { 166 Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage()); 167 } 168 } 169 }; 170 171 try { 172 String[] args = null; // TODO: plumb native library paths via args. 173 LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess( 174 RelroFileCreator.class.getName(), args, "WebViewLoader-" + abi, abi, 175 Process.SHARED_RELRO_UID, crashHandler); 176 } catch (Throwable t) { 177 // Log and discard errors as we must not crash the system server. 178 Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t); 179 crashHandler.run(); 180 } 181 } 182 183 private static class RelroFileCreator { 184 // Called in an unprivileged child process to create the relro file. 185 public static void main(String[] args) { 186 if (!sAddressSpaceReserved) { 187 Log.e(LOGTAG, "can't create relro file; address space not reserved"); 188 return; 189 } 190 boolean result = nativeCreateRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32, 191 CHROMIUM_WEBVIEW_NATIVE_LIB_64, 192 CHROMIUM_WEBVIEW_NATIVE_RELRO_32, 193 CHROMIUM_WEBVIEW_NATIVE_RELRO_64); 194 if (!result) { 195 Log.e(LOGTAG, "failed to create relro file"); 196 } else if (DEBUG) { 197 Log.v(LOGTAG, "created relro file"); 198 } 199 try { 200 getUpdateService().notifyRelroCreationCompleted(VMRuntime.getRuntime().is64Bit(), 201 result); 202 } catch (RemoteException e) { 203 Log.e(LOGTAG, "error notifying update service", e); 204 } 205 206 // Must explicitly exit or else this process will just sit around after we return. 207 System.exit(0); 208 } 209 } 210 211 private static void loadNativeLibrary() { 212 if (!sAddressSpaceReserved) { 213 Log.e(LOGTAG, "can't load with relro file; address space not reserved"); 214 return; 215 } 216 217 try { 218 getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit()); 219 } catch (RemoteException e) { 220 Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e); 221 return; 222 } 223 224 boolean result = nativeLoadWithRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32, 225 CHROMIUM_WEBVIEW_NATIVE_LIB_64, 226 CHROMIUM_WEBVIEW_NATIVE_RELRO_32, 227 CHROMIUM_WEBVIEW_NATIVE_RELRO_64); 228 if (!result) { 229 Log.w(LOGTAG, "failed to load with relro file, proceeding without"); 230 } else if (DEBUG) { 231 Log.v(LOGTAG, "loaded with relro file"); 232 } 233 } 234 235 private static IWebViewUpdateService getUpdateService() { 236 return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); 237 } 238 239 private static native boolean nativeReserveAddressSpace(String lib32, String lib64); 240 private static native boolean nativeCreateRelroFile(String lib32, String lib64, 241 String relro32, String relro64); 242 private static native boolean nativeLoadWithRelroFile(String lib32, String lib64, 243 String relro32, String relro64); 244} 245