1 /* 2 * Copyright (C) 2016 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 17 package com.android.internal.os; 18 19 import android.app.ApplicationLoaders; 20 import android.net.LocalSocket; 21 import android.net.LocalServerSocket; 22 import android.os.Build; 23 import android.system.ErrnoException; 24 import android.system.Os; 25 import android.system.OsConstants; 26 import android.text.TextUtils; 27 import android.util.Log; 28 import android.webkit.WebViewFactory; 29 import android.webkit.WebViewFactoryProvider; 30 import android.webkit.WebViewLibraryLoader; 31 32 import java.io.DataOutputStream; 33 import java.io.File; 34 import java.io.IOException; 35 import java.lang.reflect.Method; 36 37 /** 38 * Startup class for the WebView zygote process. 39 * 40 * See {@link ZygoteInit} for generic zygote startup documentation. 41 * 42 * @hide 43 */ 44 class WebViewZygoteInit { 45 public static final String TAG = "WebViewZygoteInit"; 46 47 private static ZygoteServer sServer; 48 49 private static class WebViewZygoteServer extends ZygoteServer { 50 @Override 51 protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList) 52 throws IOException { 53 return new WebViewZygoteConnection(socket, abiList); 54 } 55 } 56 57 private static class WebViewZygoteConnection extends ZygoteConnection { 58 WebViewZygoteConnection(LocalSocket socket, String abiList) throws IOException { 59 super(socket, abiList); 60 } 61 62 @Override 63 protected void preload() { 64 // Nothing to preload by default. 65 } 66 67 @Override 68 protected boolean isPreloadComplete() { 69 // Webview zygotes don't preload any classes or resources or defaults, all of their 70 // preloading is package specific. 71 return true; 72 } 73 74 @Override 75 protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName, 76 String cacheKey) { 77 Log.i(TAG, "Beginning package preload"); 78 // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that 79 // our children will reuse the same classloader instead of creating their own. 80 // This enables us to preload Java and native code in the webview zygote process and 81 // have the preloaded versions actually be used post-fork. 82 ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader( 83 packagePath, libsPath, cacheKey); 84 85 // Load the native library using WebViewLibraryLoader to share the RELRO data with other 86 // processes. 87 WebViewLibraryLoader.loadNativeLibrary(loader, libFileName); 88 89 // Add the APK to the Zygote's list of allowed files for children. 90 String[] packageList = TextUtils.split(packagePath, File.pathSeparator); 91 for (String packageEntry : packageList) { 92 Zygote.nativeAllowFileAcrossFork(packageEntry); 93 } 94 95 // Once we have the classloader, look up the WebViewFactoryProvider implementation and 96 // call preloadInZygote() on it to give it the opportunity to preload the native library 97 // and perform any other initialisation work that should be shared among the children. 98 boolean preloadSucceeded = false; 99 try { 100 Class<WebViewFactoryProvider> providerClass = 101 WebViewFactory.getWebViewProviderClass(loader); 102 Method preloadInZygote = providerClass.getMethod("preloadInZygote"); 103 preloadInZygote.setAccessible(true); 104 if (preloadInZygote.getReturnType() != Boolean.TYPE) { 105 Log.e(TAG, "Unexpected return type: preloadInZygote must return boolean"); 106 } else { 107 preloadSucceeded = (boolean) providerClass.getMethod("preloadInZygote") 108 .invoke(null); 109 if (!preloadSucceeded) { 110 Log.e(TAG, "preloadInZygote returned false"); 111 } 112 } 113 } catch (ReflectiveOperationException e) { 114 Log.e(TAG, "Exception while preloading package", e); 115 } 116 117 try { 118 DataOutputStream socketOut = getSocketOutputStream(); 119 socketOut.writeInt(preloadSucceeded ? 1 : 0); 120 } catch (IOException ioe) { 121 throw new IllegalStateException("Error writing to command socket", ioe); 122 } 123 124 Log.i(TAG, "Package preload done"); 125 } 126 } 127 128 public static void main(String argv[]) { 129 Log.i(TAG, "Starting WebViewZygoteInit"); 130 131 String socketName = null; 132 for (String arg : argv) { 133 Log.i(TAG, arg); 134 if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) { 135 socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length()); 136 } 137 } 138 if (socketName == null) { 139 throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified"); 140 } 141 142 try { 143 Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 144 } catch (ErrnoException ex) { 145 throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex); 146 } 147 148 sServer = new WebViewZygoteServer(); 149 150 final Runnable caller; 151 try { 152 sServer.registerServerSocketAtAbstractName(socketName); 153 154 // Add the abstract socket to the FD whitelist so that the native zygote code 155 // can properly detach it after forking. 156 Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName); 157 158 // The select loop returns early in the child process after a fork and 159 // loops forever in the zygote. 160 caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS)); 161 } catch (RuntimeException e) { 162 Log.e(TAG, "Fatal exception:", e); 163 throw e; 164 } finally { 165 sServer.closeServerSocket(); 166 } 167 168 // We're in the child process and have exited the select loop. Proceed to execute the 169 // command. 170 if (caller != null) { 171 caller.run(); 172 } 173 } 174 } 175