/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.googlecode.android_scripting; import com.googlecode.android_scripting.interpreter.InterpreterConstants; import com.trilead.ssh2.StreamGobbler; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; public class Process { private static final int DEFAULT_BUFFER_SIZE = 8192; private final List<String> mArguments; private final Map<String, String> mEnvironment; private static final int PID_INIT_VALUE = -1; private File mBinary; private String mName; private long mStartTime; private long mEndTime; protected final AtomicInteger mPid; protected FileDescriptor mFd; protected OutputStream mOut; protected InputStream mIn; protected File mLog; public Process() { mArguments = new ArrayList<String>(); mEnvironment = new HashMap<String, String>(); mPid = new AtomicInteger(PID_INIT_VALUE); } public void addArgument(String argument) { mArguments.add(argument); } public void addAllArguments(List<String> arguments) { mArguments.addAll(arguments); } public void putAllEnvironmentVariables(Map<String, String> environment) { mEnvironment.putAll(environment); } public void putEnvironmentVariable(String key, String value) { mEnvironment.put(key, value); } public void setBinary(File binary) { if (!binary.exists()) { throw new RuntimeException("Binary " + binary + " does not exist!"); } mBinary = binary; } public Integer getPid() { return mPid.get(); } public FileDescriptor getFd() { return mFd; } public OutputStream getOut() { return mOut; } public OutputStream getErr() { return getOut(); } public File getLogFile() { return mLog; } public InputStream getIn() { return mIn; } public void start(final Runnable shutdownHook) { if (isAlive()) { throw new RuntimeException("Attempted to start process that is already running."); } String binaryPath = mBinary.getAbsolutePath(); Log.v("Executing " + binaryPath + " with arguments " + mArguments + " and with environment " + mEnvironment.toString()); int[] pid = new int[1]; String[] argumentsArray = mArguments.toArray(new String[mArguments.size()]); mLog = new File(String.format("%s/%s.log", InterpreterConstants.SDCARD_SL4A_ROOT, getName())); mFd = Exec.createSubprocess(binaryPath, argumentsArray, getEnvironmentArray(), getWorkingDirectory(), pid); mPid.set(pid[0]); mOut = new FileOutputStream(mFd); mIn = new StreamGobbler(new FileInputStream(mFd), mLog, DEFAULT_BUFFER_SIZE); mStartTime = System.currentTimeMillis(); new Thread(new Runnable() { public void run() { int result = Exec.waitFor(mPid.get()); mEndTime = System.currentTimeMillis(); int pid = mPid.getAndSet(PID_INIT_VALUE); Log.v("Process " + pid + " exited with result code " + result + "."); try { mIn.close(); } catch (IOException e) { Log.e(e); } try { mOut.close(); } catch (IOException e) { Log.e(e); } if (shutdownHook != null) { shutdownHook.run(); } } }).start(); } private String[] getEnvironmentArray() { List<String> environmentVariables = new ArrayList<String>(); for (Entry<String, String> entry : mEnvironment.entrySet()) { environmentVariables.add(entry.getKey() + "=" + entry.getValue()); } String[] environment = environmentVariables.toArray(new String[environmentVariables.size()]); return environment; } public void kill() { if (isAlive()) { android.os.Process.killProcess(mPid.get()); Log.v("Killed process " + mPid); } } public boolean isAlive() { return (mFd != null && mFd.valid()) && mPid.get() != PID_INIT_VALUE; } public String getUptime() { long ms; if (!isAlive()) { ms = mEndTime - mStartTime; } else { ms = System.currentTimeMillis() - mStartTime; } StringBuilder buffer = new StringBuilder(); int days = (int) (ms / (1000 * 60 * 60 * 24)); int hours = (int) (ms % (1000 * 60 * 60 * 24)) / 3600000; int minutes = (int) (ms % 3600000) / 60000; int seconds = (int) (ms % 60000) / 1000; if (days != 0) { buffer.append(String.format("%02d:%02d:", days, hours)); } else if (hours != 0) { buffer.append(String.format("%02d:", hours)); } buffer.append(String.format("%02d:%02d", minutes, seconds)); return buffer.toString(); } public String getName() { return mName; } public void setName(String name) { mName = name; } public String getWorkingDirectory() { return null; } }