Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 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.os.Process; |
Fyodor Kupolov | 3235e0c | 2016-11-17 11:44:49 -0800 | [diff] [blame] | 20 | import android.os.Trace; |
Andreas Gampe | 220aba2 | 2017-03-13 14:36:09 -0700 | [diff] [blame] | 21 | import android.system.ErrnoException; |
| 22 | import android.system.Os; |
| 23 | import android.system.OsConstants; |
| 24 | import android.system.StructCapUserData; |
| 25 | import android.system.StructCapUserHeader; |
Fyodor Kupolov | 6e3461b | 2017-08-10 17:00:43 -0700 | [diff] [blame] | 26 | import android.util.TimingsTraceLog; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 27 | import android.util.Slog; |
Narayan Kamath | 37ad4b0 | 2015-01-19 16:05:24 +0000 | [diff] [blame] | 28 | import dalvik.system.VMRuntime; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 29 | import java.io.DataOutputStream; |
| 30 | import java.io.FileDescriptor; |
| 31 | import java.io.FileOutputStream; |
| 32 | import java.io.IOException; |
| 33 | |
| 34 | import libcore.io.IoUtils; |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 35 | |
| 36 | /** |
| 37 | * Startup class for the wrapper process. |
| 38 | * @hide |
| 39 | */ |
| 40 | public class WrapperInit { |
| 41 | private final static String TAG = "AndroidRuntime"; |
| 42 | |
| 43 | /** |
| 44 | * Class not instantiable. |
| 45 | */ |
| 46 | private WrapperInit() { |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * The main function called when starting a runtime application through a |
| 51 | * wrapper process instead of by forking Zygote. |
| 52 | * |
| 53 | * The first argument specifies the file descriptor for a pipe that should receive |
Elliott Hughes | e1dfcb7 | 2011-07-08 11:08:07 -0700 | [diff] [blame] | 54 | * the pid of this process, or 0 if none. |
| 55 | * |
| 56 | * The second argument is the target SDK version for the app. |
| 57 | * |
| 58 | * The remaining arguments are passed to the runtime. |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 59 | * |
| 60 | * @param args The command-line arguments. |
| 61 | */ |
| 62 | public static void main(String[] args) { |
Narayan Kamath | bf99d06 | 2017-07-05 14:45:38 +0100 | [diff] [blame] | 63 | // Parse our mandatory arguments. |
| 64 | int fdNum = Integer.parseInt(args[0], 10); |
| 65 | int targetSdkVersion = Integer.parseInt(args[1], 10); |
Elliott Hughes | e1dfcb7 | 2011-07-08 11:08:07 -0700 | [diff] [blame] | 66 | |
Narayan Kamath | bf99d06 | 2017-07-05 14:45:38 +0100 | [diff] [blame] | 67 | // Tell the Zygote what our actual PID is (since it only knows about the |
| 68 | // wrapper that it directly forked). |
| 69 | if (fdNum != 0) { |
| 70 | try { |
| 71 | FileDescriptor fd = new FileDescriptor(); |
| 72 | fd.setInt$(fdNum); |
| 73 | DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); |
| 74 | os.writeInt(Process.myPid()); |
| 75 | os.close(); |
| 76 | IoUtils.closeQuietly(fd); |
| 77 | } catch (IOException ex) { |
| 78 | Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 79 | } |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 80 | } |
Narayan Kamath | bf99d06 | 2017-07-05 14:45:38 +0100 | [diff] [blame] | 81 | |
| 82 | // Mimic system Zygote preloading. |
Fyodor Kupolov | 6e3461b | 2017-08-10 17:00:43 -0700 | [diff] [blame] | 83 | ZygoteInit.preload(new TimingsTraceLog("WrapperInitTiming", |
Narayan Kamath | bf99d06 | 2017-07-05 14:45:38 +0100 | [diff] [blame] | 84 | Trace.TRACE_TAG_DALVIK)); |
| 85 | |
| 86 | // Launch the application. |
| 87 | String[] runtimeArgs = new String[args.length - 2]; |
| 88 | System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); |
| 89 | Runnable r = wrapperInit(targetSdkVersion, runtimeArgs); |
| 90 | |
| 91 | r.run(); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Executes a runtime application with a wrapper command. |
| 96 | * This method never returns. |
| 97 | * |
| 98 | * @param invokeWith The wrapper command. |
| 99 | * @param niceName The nice name for the application, or null if none. |
Elliott Hughes | e1dfcb7 | 2011-07-08 11:08:07 -0700 | [diff] [blame] | 100 | * @param targetSdkVersion The target SDK version for the app. |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 101 | * @param pipeFd The pipe to which the application's pid should be written, or null if none. |
Narayan Kamath | 973b466 | 2014-03-31 13:41:26 +0100 | [diff] [blame] | 102 | * @param args Arguments for {@link RuntimeInit#main}. |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 103 | */ |
| 104 | public static void execApplication(String invokeWith, String niceName, |
Narayan Kamath | 37ad4b0 | 2015-01-19 16:05:24 +0000 | [diff] [blame] | 105 | int targetSdkVersion, String instructionSet, FileDescriptor pipeFd, |
| 106 | String[] args) { |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 107 | StringBuilder command = new StringBuilder(invokeWith); |
Narayan Kamath | 37ad4b0 | 2015-01-19 16:05:24 +0000 | [diff] [blame] | 108 | |
| 109 | final String appProcess; |
| 110 | if (VMRuntime.is64BitInstructionSet(instructionSet)) { |
| 111 | appProcess = "/system/bin/app_process64"; |
| 112 | } else { |
| 113 | appProcess = "/system/bin/app_process32"; |
| 114 | } |
| 115 | command.append(' '); |
| 116 | command.append(appProcess); |
| 117 | |
David Srbecky | 28a8937 | 2018-03-21 12:58:00 +0000 | [diff] [blame] | 118 | // Generate bare minimum of debug information to be able to backtrace through JITed code. |
| 119 | // We assume that if the invoke wrapper is used, backtraces are desirable: |
| 120 | // * The wrap.sh script can only be used by debuggable apps, which would enable this flag |
| 121 | // without the script anyway (the fork-zygote path). So this makes the two consistent. |
| 122 | // * The wrap.* property can only be used on userdebug builds and is likely to be used by |
| 123 | // developers (e.g. enable debug-malloc), in which case backtraces are also useful. |
| 124 | command.append(" -Xcompiler-option --generate-mini-debug-info"); |
| 125 | |
Narayan Kamath | 37ad4b0 | 2015-01-19 16:05:24 +0000 | [diff] [blame] | 126 | command.append(" /system/bin --application"); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 127 | if (niceName != null) { |
| 128 | command.append(" '--nice-name=").append(niceName).append("'"); |
| 129 | } |
| 130 | command.append(" com.android.internal.os.WrapperInit "); |
| 131 | command.append(pipeFd != null ? pipeFd.getInt$() : 0); |
Elliott Hughes | e1dfcb7 | 2011-07-08 11:08:07 -0700 | [diff] [blame] | 132 | command.append(' '); |
| 133 | command.append(targetSdkVersion); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 134 | Zygote.appendQuotedShellArgs(command, args); |
Andreas Gampe | 220aba2 | 2017-03-13 14:36:09 -0700 | [diff] [blame] | 135 | preserveCapabilities(); |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 136 | Zygote.execShell(command.toString()); |
| 137 | } |
Andreas Gampe | 76d4fc8 | 2017-02-07 19:44:37 -0800 | [diff] [blame] | 138 | |
| 139 | /** |
| 140 | * The main function called when an application is started through a |
| 141 | * wrapper process. |
| 142 | * |
| 143 | * When the wrapper starts, the runtime starts {@link RuntimeInit#main} |
| 144 | * which calls {@link main} which then calls this method. |
| 145 | * So we don't need to call commonInit() here. |
| 146 | * |
| 147 | * @param targetSdkVersion target SDK version |
| 148 | * @param argv arg strings |
| 149 | */ |
Narayan Kamath | bf99d06 | 2017-07-05 14:45:38 +0100 | [diff] [blame] | 150 | private static Runnable wrapperInit(int targetSdkVersion, String[] argv) { |
Andreas Gampe | 76d4fc8 | 2017-02-07 19:44:37 -0800 | [diff] [blame] | 151 | if (RuntimeInit.DEBUG) { |
| 152 | Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper"); |
| 153 | } |
| 154 | |
tony.ys_liu | 3473817 | 2016-12-14 18:50:29 +0800 | [diff] [blame] | 155 | // Check whether the first argument is a "-cp" in argv, and assume the next argument is the |
| 156 | // classpath. If found, create a PathClassLoader and use it for applicationInit. |
| 157 | ClassLoader classLoader = null; |
| 158 | if (argv != null && argv.length > 2 && argv[0].equals("-cp")) { |
| 159 | classLoader = ZygoteInit.createPathClassLoader(argv[1], targetSdkVersion); |
| 160 | |
| 161 | // Install this classloader as the context classloader, too. |
| 162 | Thread.currentThread().setContextClassLoader(classLoader); |
| 163 | |
| 164 | // Remove the classpath from the arguments. |
| 165 | String removedArgs[] = new String[argv.length - 2]; |
| 166 | System.arraycopy(argv, 2, removedArgs, 0, argv.length - 2); |
| 167 | argv = removedArgs; |
| 168 | } |
| 169 | |
Christopher Ferris | 2980de4 | 2017-06-20 16:13:40 -0700 | [diff] [blame] | 170 | // Perform the same initialization that would happen after the Zygote forks. |
| 171 | Zygote.nativePreApplicationInit(); |
Narayan Kamath | bf99d06 | 2017-07-05 14:45:38 +0100 | [diff] [blame] | 172 | return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); |
Andreas Gampe | 76d4fc8 | 2017-02-07 19:44:37 -0800 | [diff] [blame] | 173 | } |
Andreas Gampe | 220aba2 | 2017-03-13 14:36:09 -0700 | [diff] [blame] | 174 | |
| 175 | /** |
| 176 | * Copy current capabilities to ambient capabilities. This is required for apps using |
| 177 | * capabilities, as execv will re-evaluate the capability set, and the set of sh is |
| 178 | * empty. Ambient capabilities have to be set to inherit them effectively. |
| 179 | * |
| 180 | * Note: This is BEST EFFORT ONLY. In case capabilities can't be raised, this function |
| 181 | * will silently return. In THIS CASE ONLY, as this is a development feature, it |
| 182 | * is better to return and try to run anyways, instead of blocking the wrapped app. |
| 183 | * This is acceptable here as failure will leave the wrapped app with strictly less |
| 184 | * capabilities, which may make it crash, but not exceed its allowances. |
| 185 | */ |
| 186 | private static void preserveCapabilities() { |
| 187 | StructCapUserHeader header = new StructCapUserHeader( |
| 188 | OsConstants._LINUX_CAPABILITY_VERSION_3, 0); |
| 189 | StructCapUserData[] data; |
| 190 | try { |
| 191 | data = Os.capget(header); |
| 192 | } catch (ErrnoException e) { |
| 193 | Slog.e(RuntimeInit.TAG, "RuntimeInit: Failed capget", e); |
| 194 | return; |
| 195 | } |
| 196 | |
| 197 | if (data[0].permitted != data[0].inheritable || |
| 198 | data[1].permitted != data[1].inheritable) { |
| 199 | data[0] = new StructCapUserData(data[0].effective, data[0].permitted, |
| 200 | data[0].permitted); |
| 201 | data[1] = new StructCapUserData(data[1].effective, data[1].permitted, |
| 202 | data[1].permitted); |
| 203 | try { |
| 204 | Os.capset(header, data); |
| 205 | } catch (ErrnoException e) { |
| 206 | Slog.e(RuntimeInit.TAG, "RuntimeInit: Failed capset", e); |
| 207 | return; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | for (int i = 0; i < 64; i++) { |
| 212 | int dataIndex = OsConstants.CAP_TO_INDEX(i); |
| 213 | int capMask = OsConstants.CAP_TO_MASK(i); |
| 214 | if ((data[dataIndex].inheritable & capMask) != 0) { |
| 215 | try { |
| 216 | Os.prctl(OsConstants.PR_CAP_AMBIENT, OsConstants.PR_CAP_AMBIENT_RAISE, i, 0, |
| 217 | 0); |
| 218 | } catch (ErrnoException ex) { |
| 219 | // Only log here. Try to run the wrapped application even without this |
| 220 | // ambient capability. It may crash after fork, but at least we'll try. |
| 221 | Slog.e(RuntimeInit.TAG, "RuntimeInit: Failed to raise ambient capability " |
| 222 | + i, ex); |
| 223 | } |
| 224 | } |
| 225 | } |
| 226 | } |
Jeff Brown | ebed7d6 | 2011-05-16 17:08:42 -0700 | [diff] [blame] | 227 | } |