1 /* 2 * Copyright (C) 2016 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.googlecode.android_scripting; 18 19 import com.googlecode.android_scripting.interpreter.InterpreterConstants; 20 import com.trilead.ssh2.StreamGobbler; 21 22 import java.io.File; 23 import java.io.FileDescriptor; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.OutputStream; 29 import java.util.ArrayList; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Map.Entry; 34 import java.util.concurrent.atomic.AtomicInteger; 35 36 public class Process { 37 38 private static final int DEFAULT_BUFFER_SIZE = 8192; 39 40 private final List<String> mArguments; 41 private final Map<String, String> mEnvironment; 42 43 private static final int PID_INIT_VALUE = -1; 44 45 private File mBinary; 46 private String mName; 47 private long mStartTime; 48 private long mEndTime; 49 50 protected final AtomicInteger mPid; 51 protected FileDescriptor mFd; 52 protected OutputStream mOut; 53 protected InputStream mIn; 54 protected File mLog; 55 Process()56 public Process() { 57 mArguments = new ArrayList<String>(); 58 mEnvironment = new HashMap<String, String>(); 59 mPid = new AtomicInteger(PID_INIT_VALUE); 60 } 61 addArgument(String argument)62 public void addArgument(String argument) { 63 mArguments.add(argument); 64 } 65 addAllArguments(List<String> arguments)66 public void addAllArguments(List<String> arguments) { 67 mArguments.addAll(arguments); 68 } 69 putAllEnvironmentVariables(Map<String, String> environment)70 public void putAllEnvironmentVariables(Map<String, String> environment) { 71 mEnvironment.putAll(environment); 72 } 73 putEnvironmentVariable(String key, String value)74 public void putEnvironmentVariable(String key, String value) { 75 mEnvironment.put(key, value); 76 } 77 setBinary(File binary)78 public void setBinary(File binary) { 79 if (!binary.exists()) { 80 throw new RuntimeException("Binary " + binary + " does not exist!"); 81 } 82 mBinary = binary; 83 } 84 getPid()85 public Integer getPid() { 86 return mPid.get(); 87 } 88 getFd()89 public FileDescriptor getFd() { 90 return mFd; 91 } 92 getOut()93 public OutputStream getOut() { 94 return mOut; 95 } 96 getErr()97 public OutputStream getErr() { 98 return getOut(); 99 } 100 getLogFile()101 public File getLogFile() { 102 return mLog; 103 } 104 getIn()105 public InputStream getIn() { 106 return mIn; 107 } 108 start(final Runnable shutdownHook)109 public void start(final Runnable shutdownHook) { 110 if (isAlive()) { 111 throw new RuntimeException("Attempted to start process that is already running."); 112 } 113 114 String binaryPath = mBinary.getAbsolutePath(); 115 Log.v("Executing " + binaryPath + " with arguments " + mArguments + " and with environment " 116 + mEnvironment.toString()); 117 118 int[] pid = new int[1]; 119 String[] argumentsArray = mArguments.toArray(new String[mArguments.size()]); 120 mLog = new File(String.format("%s/%s.log", InterpreterConstants.SDCARD_SL4A_ROOT, getName())); 121 122 mFd = 123 Exec.createSubprocess(binaryPath, argumentsArray, getEnvironmentArray(), 124 getWorkingDirectory(), pid); 125 mPid.set(pid[0]); 126 mOut = new FileOutputStream(mFd); 127 mIn = new StreamGobbler(new FileInputStream(mFd), mLog, DEFAULT_BUFFER_SIZE); 128 mStartTime = System.currentTimeMillis(); 129 130 new Thread(new Runnable() { 131 public void run() { 132 int result = Exec.waitFor(mPid.get()); 133 mEndTime = System.currentTimeMillis(); 134 int pid = mPid.getAndSet(PID_INIT_VALUE); 135 Log.v("Process " + pid + " exited with result code " + result + "."); 136 try { 137 mIn.close(); 138 } catch (IOException e) { 139 Log.e(e); 140 } 141 try { 142 mOut.close(); 143 } catch (IOException e) { 144 Log.e(e); 145 } 146 if (shutdownHook != null) { 147 shutdownHook.run(); 148 } 149 } 150 }).start(); 151 } 152 getEnvironmentArray()153 private String[] getEnvironmentArray() { 154 List<String> environmentVariables = new ArrayList<String>(); 155 for (Entry<String, String> entry : mEnvironment.entrySet()) { 156 environmentVariables.add(entry.getKey() + "=" + entry.getValue()); 157 } 158 String[] environment = environmentVariables.toArray(new String[environmentVariables.size()]); 159 return environment; 160 } 161 kill()162 public void kill() { 163 if (isAlive()) { 164 android.os.Process.killProcess(mPid.get()); 165 Log.v("Killed process " + mPid); 166 } 167 } 168 isAlive()169 public boolean isAlive() { 170 return (mFd != null && mFd.valid()) && mPid.get() != PID_INIT_VALUE; 171 } 172 getUptime()173 public String getUptime() { 174 long ms; 175 if (!isAlive()) { 176 ms = mEndTime - mStartTime; 177 } else { 178 ms = System.currentTimeMillis() - mStartTime; 179 } 180 StringBuilder buffer = new StringBuilder(); 181 int days = (int) (ms / (1000 * 60 * 60 * 24)); 182 int hours = (int) (ms % (1000 * 60 * 60 * 24)) / 3600000; 183 int minutes = (int) (ms % 3600000) / 60000; 184 int seconds = (int) (ms % 60000) / 1000; 185 if (days != 0) { 186 buffer.append(String.format("%02d:%02d:", days, hours)); 187 } else if (hours != 0) { 188 buffer.append(String.format("%02d:", hours)); 189 } 190 buffer.append(String.format("%02d:%02d", minutes, seconds)); 191 return buffer.toString(); 192 } 193 getName()194 public String getName() { 195 return mName; 196 } 197 setName(String name)198 public void setName(String name) { 199 mName = name; 200 } 201 getWorkingDirectory()202 public String getWorkingDirectory() { 203 return null; 204 } 205 } 206