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