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