AI 144089: Make "Term" build against the public Android API.
This was done by making a private copy of android.term.Exec.
BUG=1750582
Automated import of CL 144089
diff --git a/apps/Term/Android.mk b/apps/Term/Android.mk
index 843aec5..9ff6c0d 100644
--- a/apps/Term/Android.mk
+++ b/apps/Term/Android.mk
@@ -1,3 +1,26 @@
+#
+# Copyright (C) 2008 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.
+#
+
+# This makefile shows how to build a shared library and an activity that
+# bundles the shared library and calls it using JNI.
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -7,4 +30,11 @@
LOCAL_PACKAGE_NAME := Term
+LOCAL_JNI_SHARED_LIBRARIES := libterm
+
include $(BUILD_PACKAGE)
+
+# ============================================================
+
+# Also build all of the sub-targets under this one: the shared library.
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/apps/Term/jni/Android.mk b/apps/Term/jni/Android.mk
new file mode 100644
index 0000000..2fe4a75
--- /dev/null
+++ b/apps/Term/jni/Android.mk
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2008 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.
+#
+
+# This makefile supplies the rules for building a library of JNI code for
+# use by our example of how to bundle a shared library with an APK.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := eng
+
+# This is the target being built.
+LOCAL_MODULE:= libterm
+
+
+# All of the source files that we will compile.
+LOCAL_SRC_FILES:= \
+ termExec.cpp
+
+# All of the shared libraries we link against.
+LOCAL_SHARED_LIBRARIES := \
+ libutils
+
+# No static libraries.
+LOCAL_STATIC_LIBRARIES :=
+
+# Also need the JNI headers.
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE)
+
+# No special compiler flags.
+LOCAL_CFLAGS +=
+
+# Don't prelink this library. For more efficient code, you may want
+# to add this library to the prelink map and set this to true. However,
+# it's difficult to do this for applications that are not supplied as
+# part of a system image.
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/Term/jni/termExec.cpp b/apps/Term/jni/termExec.cpp
new file mode 100644
index 0000000..968bb90
--- /dev/null
+++ b/apps/Term/jni/termExec.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#define LOG_TAG "Exec"
+
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+static jclass class_fileDescriptor;
+static jfieldID field_fileDescriptor_descriptor;
+static jmethodID method_fileDescriptor_init;
+
+
+class String8 {
+public:
+ String8() {
+ mString = 0;
+ }
+
+ ~String8() {
+ if (mString) {
+ free(mString);
+ }
+ }
+
+ void set(const char16_t* o, size_t numChars) {
+ mString = (char*) malloc(numChars + 1);
+ for (size_t i = 0; i < numChars; i++) {
+ mString[i] = (char) o[i];
+ }
+ mString[numChars] = '\0';
+ }
+
+ const char* string() {
+ return mString;
+ }
+private:
+ char* mString;
+};
+
+static int create_subprocess(const char *cmd, const char *arg0, const char *arg1,
+ int* pProcessId)
+{
+ char *devname;
+ int ptm;
+ pid_t pid;
+
+ ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
+ if(ptm < 0){
+ LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
+ return -1;
+ }
+ fcntl(ptm, F_SETFD, FD_CLOEXEC);
+
+ if(grantpt(ptm) || unlockpt(ptm) ||
+ ((devname = (char*) ptsname(ptm)) == 0)){
+ LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
+ return -1;
+ }
+
+ pid = fork();
+ if(pid < 0) {
+ LOGE("- fork failed: %s -\n", strerror(errno));
+ return -1;
+ }
+
+ if(pid == 0){
+ close(ptm);
+
+ int pts;
+
+ setsid();
+
+ pts = open(devname, O_RDWR);
+ if(pts < 0) exit(-1);
+
+ dup2(pts, 0);
+ dup2(pts, 1);
+ dup2(pts, 2);
+
+ execl(cmd, cmd, arg0, arg1, NULL);
+ exit(-1);
+ } else {
+ *pProcessId = (int) pid;
+ return ptm;
+ }
+}
+
+
+static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
+ jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray)
+{
+ const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
+ String8 cmd_8;
+ if (str) {
+ cmd_8.set(str, env->GetStringLength(cmd));
+ env->ReleaseStringCritical(cmd, str);
+ }
+
+ str = arg0 ? env->GetStringCritical(arg0, 0) : 0;
+ const char* arg0Str = 0;
+ String8 arg0_8;
+ if (str) {
+ arg0_8.set(str, env->GetStringLength(arg0));
+ env->ReleaseStringCritical(arg0, str);
+ arg0Str = arg0_8.string();
+ }
+
+ str = arg1 ? env->GetStringCritical(arg1, 0) : 0;
+ const char* arg1Str = 0;
+ String8 arg1_8;
+ if (str) {
+ arg1_8.set(str, env->GetStringLength(arg1));
+ env->ReleaseStringCritical(arg1, str);
+ arg1Str = arg1_8.string();
+ }
+
+ int procId;
+ int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId);
+
+ if (processIdArray) {
+ int procIdLen = env->GetArrayLength(processIdArray);
+ if (procIdLen > 0) {
+ jboolean isCopy;
+
+ int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
+ if (pProcId) {
+ *pProcId = procId;
+ env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
+ }
+ }
+ }
+
+ jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init);
+
+ if (!result) {
+ LOGE("Couldn't create a FileDescriptor.");
+ }
+ else {
+ env->SetIntField(result, field_fileDescriptor_descriptor, ptm);
+ }
+
+ return result;
+}
+
+
+static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz,
+ jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel)
+{
+ int fd;
+ struct winsize sz;
+
+ fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
+
+ if (env->ExceptionOccurred() != NULL) {
+ return;
+ }
+
+ sz.ws_row = row;
+ sz.ws_col = col;
+ sz.ws_xpixel = xpixel;
+ sz.ws_ypixel = ypixel;
+
+ ioctl(fd, TIOCSWINSZ, &sz);
+}
+
+static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz,
+ jint procId) {
+ int status;
+ waitpid(procId, &status, 0);
+ int result = 0;
+ if (WIFEXITED(status)) {
+ result = WEXITSTATUS(status);
+ }
+ return result;
+}
+
+static int register_FileDescriptor(JNIEnv *env)
+{
+ class_fileDescriptor = env->FindClass("java/io/FileDescriptor");
+
+ if (class_fileDescriptor == NULL) {
+ LOGE("Can't find java/io/FileDescriptor");
+ return -1;
+ }
+
+ field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I");
+
+ if (field_fileDescriptor_descriptor == NULL) {
+ LOGE("Can't find FileDescriptor.descriptor");
+ return -1;
+ }
+
+ method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V");
+ if (method_fileDescriptor_init == NULL) {
+ LOGE("Can't find FileDescriptor.init");
+ return -1;
+ }
+ return 0;
+}
+
+
+static const char *classPathName = "com/android/term/Exec";
+
+static JNINativeMethod method_table[] = {
+ { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;",
+ (void*) android_os_Exec_createSubProcess },
+ { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V",
+ (void*) android_os_Exec_setPtyWindowSize},
+ { "waitFor", "(I)I",
+ (void*) android_os_Exec_waitFor}
+};
+
+/*
+ * Register several native methods for one class.
+ */
+static int registerNativeMethods(JNIEnv* env, const char* className,
+ JNINativeMethod* gMethods, int numMethods)
+{
+ jclass clazz;
+
+ clazz = env->FindClass(className);
+ if (clazz == NULL) {
+ LOGE("Native registration unable to find class '%s'", className);
+ return JNI_FALSE;
+ }
+ if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
+ LOGE("RegisterNatives failed for '%s'", className);
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+/*
+ * Register native methods for all classes we know about.
+ *
+ * returns JNI_TRUE on success.
+ */
+static int registerNatives(JNIEnv* env)
+{
+ if (!registerNativeMethods(env, classPathName, method_table,
+ sizeof(method_table) / sizeof(method_table[0]))) {
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+
+// ----------------------------------------------------------------------------
+
+/*
+ * This is called by the VM when the shared library is first loaded.
+ */
+
+typedef union {
+ JNIEnv* env;
+ void* venv;
+} UnionJNIEnvToVoid;
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ UnionJNIEnvToVoid uenv;
+ uenv.venv = NULL;
+ jint result = -1;
+ JNIEnv* env = NULL;
+
+ LOGI("JNI_OnLoad");
+
+ if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
+ LOGE("ERROR: GetEnv failed");
+ goto bail;
+ }
+ env = uenv.env;
+
+ if ((result = register_FileDescriptor(env)) < 0) {
+ LOGE("ERROR: registerFileDescriptor failed");
+ goto bail;
+ }
+
+ if (registerNatives(env) != JNI_TRUE) {
+ LOGE("ERROR: registerNatives failed");
+ goto bail;
+ }
+
+ result = JNI_VERSION_1_4;
+
+bail:
+ return result;
+}
diff --git a/apps/Term/src/com/android/term/Exec.java b/apps/Term/src/com/android/term/Exec.java
new file mode 100644
index 0000000..f84e7c1
--- /dev/null
+++ b/apps/Term/src/com/android/term/Exec.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 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.android.term;
+
+import java.io.FileDescriptor;
+
+/**
+ * @hide
+ * Tools for executing commands. Not for public consumption.
+ */
+
+public class Exec
+{
+ static {
+ System.loadLibrary("term");
+ }
+
+ /**
+ * @param cmd The command to execute
+ * @param arg0 The first argument to the command, may be null
+ * @param arg1 the second argument to the command, may be null
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static FileDescriptor createSubprocess(
+ String cmd, String arg0, String arg1) {
+ return createSubprocess(cmd, arg0, arg1, null);
+ }
+
+ /**
+ * @param cmd The command to execute
+ * @param arg0 The first argument to the command, may be null
+ * @param arg1 the second argument to the command, may be null
+ * @param processId A one-element array to which the process ID of the
+ * started process will be written.
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static native FileDescriptor createSubprocess(
+ String cmd, String arg0, String arg1, int[] processId);
+
+ public static native void setPtyWindowSize(FileDescriptor fd,
+ int row, int col, int xpixel, int ypixel);
+ /**
+ * Causes the calling thread to wait for the process associated with the
+ * receiver to finish executing.
+ *
+ * @return The exit value of the Process being waited on
+ *
+ */
+ public static native int waitFor(int processId);
+}
+
diff --git a/apps/Term/src/com/android/term/Term.java b/apps/Term/src/com/android/term/Term.java
index 1f43843..ae83a89 100644
--- a/apps/Term/src/com/android/term/Term.java
+++ b/apps/Term/src/com/android/term/Term.java
@@ -16,6 +16,12 @@
package com.android.term;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -35,10 +41,8 @@
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Exec;
import android.os.Handler;
import android.os.Message;
-import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
@@ -54,13 +58,6 @@
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
/**
* A terminal emulator activity.
@@ -98,10 +95,7 @@
/**
* The pseudo-teletype (pty) file descriptor that we use to communicate with
- * another process, typically a shell. Currently we just use this to get the
- * mTermIn / mTermOut file descriptors, but when we implement resizing of
- * the terminal we will need it to issue the ioctl to inform the other
- * process that we've changed the terminal size.
+ * another process, typically a shell.
*/
private FileDescriptor mTermFd;