Re #1546: Initial version of generic console application loader.

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/android@4289 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Nanang Izzuddin 2012-10-29 07:22:44 +00:00
parent 3b27e8f073
commit 2ff094467a
21 changed files with 656 additions and 0 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.pjsip.apjloader.MainActivity</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,53 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.pjsip.apjloader"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.USE_SIP" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-feature android:name="android.hardware.sip.voip" android:required="true" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar" >
<activity
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation|screenLayout"
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,61 @@
# $id$
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Get PJ build settings
include ../../../build.mak
include $(PJDIR)/build/common.mak
# Path to SWIG
MY_SWIG := swig
#========================================================================
# Choose console application to load
#========================================================================
# pjlib test
MY_MODULE := $(PJDIR)/pjlib/build/output/pjlib-test-$(TARGET_NAME)/*.o
# pjlib-util test
#MY_MODULE := $(PJDIR)/pjlib-util/build/output/pjlib-util-test-$(TARGET_NAME)/*.o
# pjsip test
#MY_MODULE := $(PJDIR)/pjsip/build/output/pjsip-test-$(TARGET_NAME)/*.o
# pjnath test
#MY_MODULE := $(PJDIR)/pjnath/build/output/pjnath-test-$(TARGET_NAME)/*.o
# pjmedia test
# Note: jbuf test requires Jbtest.dat, this jbuf test must be disabled (for now).
#MY_MODULE := $(PJDIR)/pjmedia/build/output/pjmedia-test-$(TARGET_NAME)/*.o
# pjsystest app (not supported yet)
# Todo: this test requires some input and output files (log & WAV).
#MY_MODULE := $(PJDIR)/pjsip-apps/build/output/pjsystest-$(TARGET_NAME)/*.o
# pjsua app
# Note: must set USE_GUI to zero in config_site.h
#MY_MODULE := $(PJDIR)/pjsip-apps/build/output/pjsua-$(TARGET_NAME)/*.o
#========================================================================
# Constants
MY_JNI_WRAP := apjloader_wrap.c
MY_JNI_DIR := jni
# Android build settings
LOCAL_MODULE := libapjloader
LOCAL_CFLAGS := -Werror $(APP_CFLAGS)
LOCAL_LDFLAGS := $(APP_LDFLAGS)
LOCAL_LDLIBS := $(MY_MODULE) $(APP_LDLIBS)
LOCAL_SRC_FILES := $(MY_JNI_WRAP) apjloader_pipe.c
# Invoke SWIG
$(MY_JNI_DIR)/$(MY_JNI_WRAP):
@echo "Invoking SWIG..."
$(MY_SWIG) -o $(MY_JNI_DIR)/$(MY_JNI_WRAP) -package org.pjsip.apjloader -outdir src/org/pjsip/apjloader -java $(MY_JNI_DIR)/apjloader.i
.PHONY: $(MY_JNI_DIR)/$(MY_JNI_WRAP)
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,30 @@
%module apjloader
%{
int main(int argc, char **argv);
#ifdef __cplusplus
extern "C" {
#endif
int init_stdio_pipe();
void destroy_stdio_pipe();
int read_from_stdout(char *ch);
int write_to_stdin(const char *st);
#ifdef __cplusplus
}
#endif
%}
%include "various.i"
%apply char **STRING_ARRAY { char **argv };
int main(int argc, char **argv);
%include "typemaps.i"
%apply signed char *INOUT { char *ch };
int init_stdio_pipe();
void destroy_stdio_pipe();
int read_from_stdout(char *ch);
int write_to_stdin(const char *st);

View File

@ -0,0 +1,93 @@
/* $id$ */
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#define READ 0
#define WRITE 1
#define STATUS 2
static int stdout_pipe[3];
static int stdin_pipe[3];
void destroy_stdio_pipe();
int init_stdio_pipe()
{
int rc;
/* Set stdout no buffer */
setvbuf(stdout, NULL, _IONBF, 0);
rc = pipe(stdout_pipe);
if (rc != 0)
goto on_error;
stdout_pipe[STATUS] = 1;
rc = dup2(stdout_pipe[WRITE], 1);
if (rc < 0)
goto on_error;
rc = pipe(stdin_pipe);
if (rc != 0)
goto on_error;
stdin_pipe[STATUS] = 1;
rc = dup2(stdin_pipe[READ], 0);
if (rc < 0)
goto on_error;
return 0;
on_error:
rc = errno;
destroy_stdio_pipe();
return rc;
}
void destroy_stdio_pipe()
{
if (stdout_pipe[STATUS]) {
close(stdout_pipe[READ]);
close(stdout_pipe[WRITE]);
}
stdout_pipe[STATUS] = 0;
if (stdin_pipe[STATUS]) {
close(stdin_pipe[READ]);
close(stdin_pipe[WRITE]);
}
stdin_pipe[STATUS] = 0;
}
int read_from_stdout(char *ch)
{
int rc;
rc = read(stdout_pipe[READ], ch, 1);
if (rc == 0) {
/* EOF */
return -1;
} else if (rc == -1) {
/* ERROR */
return -2;
}
return 0;
}
int write_to_stdin(const char *st)
{
int rc;
char buf[100];
int len;
len = snprintf(buf, sizeof(buf), "%s\r\n", st);
rc = write(stdin_pipe[WRITE], buf, len);
return rc;
}

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
</lint>

View File

@ -0,0 +1,20 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,14 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-15

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,50 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/send_cmd_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send:" />
<EditText
android:id="@+id/input_cmd"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:text=""
android:hint="Type string to send">
</EditText>
<Button
android:id="@+id/quit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Quit" />
</LinearLayout>
<ScrollView
android:id="@+id/output_scroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:fillViewport="true" >
<TextView
android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="7dip"
android:typeface="monospace" />
</ScrollView>
</LinearLayout>

View File

@ -0,0 +1,5 @@
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light" />
</resources>

View File

@ -0,0 +1,5 @@
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" />
</resources>

View File

@ -0,0 +1,6 @@
<resources>
<string name="app_name">Console Loader</string>
<string name="title_activity_main">MainActivity</string>
</resources>

View File

@ -0,0 +1,5 @@
<resources>
<style name="AppTheme" parent="android:Theme.Light" />
</resources>

View File

@ -0,0 +1,254 @@
// $id$
package org.pjsip.apjloader;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.method.ScrollingMovementMethod;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
class CONST {
public static final String LIB_FILENAME = "apjloader";
public static final String TAG = "apjloader";
public static final String NEWLINE = "\r\n";
public static final Boolean AUTOKILL_ON_FINISHED = true;
public enum MSG_TYPE {
STR_INFO,
STR_ERROR,
QUIT
};
}
class LOG {
public static void INFO(Handler h, String str) {
Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_INFO.ordinal(), str);
msg.sendToTarget();
}
public static void ERROR(Handler h, String str) {
Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_ERROR.ordinal(), str);
msg.sendToTarget();
}
}
class LoaderThread extends Thread {
private Handler ui_handler;
public LoaderThread(Handler ui_handler_) {
ui_handler = ui_handler_;
}
public void run() {
// Console application's params. Note that, the first param will be
// ignored (it is supposed to contain the app path).
// Here we just put some pjsua app params, this sample params will be
// ignored by PJSIP test apps such as pjlib-test, pjsystest, etc.
String argv[] = {"",
"--clock-rate=8000",
"--auto-answer=200",
};
LOG.INFO(ui_handler, "Starting module.." + CONST.NEWLINE);
int rc = apjloader.main(argv.length, argv);
LOG.INFO(ui_handler, "Module finished with return code: " +
Integer.toString(rc) + CONST.NEWLINE);
apjloader.destroy_stdio_pipe();
if (CONST.AUTOKILL_ON_FINISHED) {
Message msg = Message.obtain(ui_handler, CONST.MSG_TYPE.QUIT.ordinal());
ui_handler.sendMessageDelayed(msg, 2000);
}
}
}
class OutputThread extends Thread {
private Handler ui_handler;
public OutputThread(Handler ui_handler_) {
ui_handler = ui_handler_;
}
public void run() {
final int BUFFERSIZE = 200;
StringBuilder sb = new StringBuilder(BUFFERSIZE);
while (true)
{
byte ch[] = new byte[1];
int rc = apjloader.read_from_stdout(ch);
if (rc == 0) {
if (ch[0]=='\r' || ch[0]== '\n') {
if (sb.length() > 0) {
LOG.INFO(ui_handler, sb.toString()+CONST.NEWLINE);
sb.delete(0, sb.length());
}
} else {
sb.append((char)ch[0]);
}
} else {
LOG.INFO(ui_handler, "Stdout pipe stopped, rc="+Integer.toString(rc)+CONST.NEWLINE);
break;
}
}
}
}
public class MainActivity extends Activity {
private TextView log_view;
private ScrollView log_scroll_view;
EditText input_cmd;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init_view();
print_log("Loading module.." + CONST.NEWLINE);
int rc = init_lib();
if (rc != 0)
print_log("Failed loading module: " + Integer.toString(rc) + CONST.NEWLINE);
}
private View.OnClickListener on_send_cmd_click = new View.OnClickListener() {
public void onClick(View v) {
String cmd = input_cmd.getText().toString().trim();
input_cmd.setText("");
send_cmd(cmd);
}
};
private View.OnClickListener on_quit_click = new View.OnClickListener() {
public void onClick(View v) {
/* Send quit command, only for pjsua-app */
send_cmd("q");
print_log("Quitting..");
Message msg = Message.obtain(ui_handler, CONST.MSG_TYPE.QUIT.ordinal());
ui_handler.sendMessageDelayed(msg, 2000);
}
};
private View.OnKeyListener on_input_cmd_key = new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER))
{
String cmd = input_cmd.getText().toString().trim();
input_cmd.setText("");
send_cmd(cmd);
return true;
}
return false;
}
};
private void print_log(String st) {
log_view.append(st);
//Log.i(CONST.TAG, st);
log_view.post(new Runnable() {
public void run() {
log_scroll_view.fullScroll(View.FOCUS_DOWN);
}
});
}
private Handler ui_handler = new Handler() {
@Override
public void handleMessage(Message m) {
if (m.what == CONST.MSG_TYPE.STR_INFO.ordinal() ||
m.what == CONST.MSG_TYPE.STR_ERROR.ordinal())
{
print_log((String)m.obj);
} else if (m.what == CONST.MSG_TYPE.QUIT.ordinal()) {
finish();
System.gc();
android.os.Process.killProcess(android.os.Process.myPid());
}
}
};
private void hide_soft_keyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(input_cmd.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
private void init_view() {
Button send_cmd_button = (Button)findViewById(R.id.send_cmd_button);
send_cmd_button.setOnClickListener(on_send_cmd_click);
Button quit_button = (Button)findViewById(R.id.quit_button);
quit_button.setOnClickListener(on_quit_click);
input_cmd = (EditText)findViewById(R.id.input_cmd);
input_cmd.setOnKeyListener(on_input_cmd_key);
log_view = (TextView)findViewById(R.id.output);
log_view.setMovementMethod(new ScrollingMovementMethod());
log_view.setText("");
log_scroll_view = (ScrollView)findViewById(R.id.output_scroller);
}
private int init_stdio_pipes() {
int rc = apjloader.init_stdio_pipe();
if (rc != 0)
return rc;
return 0;
}
private int init_lib() {
try {
System.loadLibrary(CONST.LIB_FILENAME);
} catch (UnsatisfiedLinkError e) {
print_log("UnsatisfiedLinkError: " + e.getMessage() + CONST.NEWLINE);
return -1;
}
// Wait for GDB to init
if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
print_log("InterruptedException: " + e.getMessage() + CONST.NEWLINE);
}
}
int rc = init_stdio_pipes();
print_log("Stdio pipes inited: " + Integer.toString(rc) + CONST.NEWLINE);
OutputThread ot = new OutputThread(ui_handler);
ot.start();
LoaderThread lt = new LoaderThread(ui_handler);
lt.start();
return 0;
}
private void send_cmd(String cmd) {
hide_soft_keyboard();
if (cmd == "")
return;
print_log("Sending command: " + cmd + CONST.NEWLINE);
apjloader.write_to_stdin(cmd);
}
}