Skip to content

Commit

Permalink
[APP] Android RPC (apache#359)
Browse files Browse the repository at this point in the history
* [APP] Android RPC first version

* [APP] Android RPC build jni automatically

* [APP] Android OpenCL RPC tested on real devices

* [APP] optimize android app interface. add ndk compile tool

* add ndk compile tool

* [APP] fix android app thread crash; add android test script

* [APP] android app - show alert dialog and disconnect when error occurs

* fix ndk build script code lint

* fix ndk build default argument

* ndk script build remove shell=True. disable android app screen orientation
  • Loading branch information
yzhliu authored and tqchen committed Aug 25, 2017
1 parent 85bba12 commit 0ee68d7
Show file tree
Hide file tree
Showing 37 changed files with 1,460 additions and 287 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ jvmpkg:
mvn clean package -P$(JVM_PKG_PROFILE) -Dcxx="$(CXX)" \
-Dcflags="$(CFLAGS)" -Dldflags="$(LDFLAGS)" \
-Dcurrent_libdir="$(ROOTDIR)/lib" $(JVM_TEST_ARGS))
jvminstall:
(cd $(ROOTDIR)/jvm; \
mvn install -P$(JVM_PKG_PROFILE) -Dcxx="$(CXX)" \
-Dcflags="$(CFLAGS)" -Dldflags="$(LDFLAGS)" \
-Dcurrent_libdir="$(ROOTDIR)/lib" $(JVM_TEST_ARGS))

clean:
$(RM) -rf build lib bin *~ */*~ */*/*~ */*/*/*~ */*.o */*/*.o */*/*/*.o */*.d */*/*.d */*/*/*.d
Expand Down
9 changes: 9 additions & 0 deletions apps/android_rpc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
1 change: 1 addition & 0 deletions apps/android_rpc/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
46 changes: 46 additions & 0 deletions apps/android_rpc/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
apply plugin: 'com.android.application'

task buildJni(type: Exec, description: 'Build JNI libs') {
commandLine 'sh', 'src/main/jni/build.sh'
}

tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildJni
}

android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "ml.dmlc.tvm.tvmrpc"
minSdkVersion 17
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/libs']
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.0.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:26.0.1'
compile 'ml.dmlc.tvm:tvm4j-core:0.0.1-SNAPSHOT'
testCompile 'junit:junit:4.12'
}
24 changes: 24 additions & 0 deletions apps/android_rpc/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ml.dmlc.tvm.tvmrpc" >

<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

<uses-permission android:name="android.permission.INTERNET" />

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 ml.dmlc.tvm.tvmrpc;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;

public class MainActivity extends AppCompatActivity {
static final int MSG_RPC_ERROR = 0;
static final String MSG_RPC_ERROR_DATA_KEY = "msg_rpc_error_data_key";

private RPCProcessor tvmServerWorker;
@SuppressLint("HandlerLeak")
private final Handler rpcHandler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
Switch switchConnect = findViewById(R.id.switch_connect);
if (msg.what == MSG_RPC_ERROR && switchConnect.isChecked()) {
// switch off and show alert dialog.
switchConnect.setChecked(false);
String msgBody = msg.getData().getString(MSG_RPC_ERROR_DATA_KEY);
showDialog("Error", msgBody);
}
}
};

private void showDialog(String title, String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title);
builder.setMessage(msg);
builder.setCancelable(true);
builder.setNeutralButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.create().show();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

tvmServerWorker = new RPCProcessor(rpcHandler);
tvmServerWorker.setDaemon(true);
tvmServerWorker.start();

Switch switchConnect = findViewById(R.id.switch_connect);
switchConnect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
connectProxy();
} else {
disconnect();
}
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
tvmServerWorker.disconnect();
}

private void connectProxy() {
EditText edProxyAddress = findViewById(R.id.input_address);
EditText edProxyPort = findViewById(R.id.input_port);
EditText edAppKey = findViewById(R.id.input_key);

final String proxyHost = edProxyAddress.getText().toString();
final int proxyPort = Integer.parseInt(edProxyPort.getText().toString());
final String key = edAppKey.getText().toString();

tvmServerWorker.connect(proxyHost, proxyPort, key);
}

private void disconnect() {
tvmServerWorker.disconnect();
System.err.println("Disconnected.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 ml.dmlc.tvm.tvmrpc;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;

import java.net.Socket;

import ml.dmlc.tvm.rpc.ConnectProxyServerProcessor;
import ml.dmlc.tvm.rpc.SocketFileDescriptorGetter;

/**
* Connect to RPC proxy and deal with requests.
*/
class RPCProcessor extends Thread {
private String host;
private int port;
private String key;

private boolean running = false;
private ConnectProxyServerProcessor currProcessor;
private final Handler uiHandler;

static final SocketFileDescriptorGetter socketFdGetter
= new SocketFileDescriptorGetter() {
@Override
public int get(Socket socket) {
return ParcelFileDescriptor.fromSocket(socket).getFd();
}
};

RPCProcessor(Handler uiHandler) {
this.uiHandler = uiHandler;
}

@Override public void run() {
while (true) {
synchronized (this) {
currProcessor = null;
while (!running) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
currProcessor = new ConnectProxyServerProcessor(host, port, key, socketFdGetter);
}
try {
currProcessor.run();
} catch (Throwable e) {
disconnect();
// turn connect switch off.
Message message = new Message();
message.what = MainActivity.MSG_RPC_ERROR;
Bundle bundle = new Bundle();
bundle.putString(MainActivity.MSG_RPC_ERROR_DATA_KEY, e.getMessage());
message.setData(bundle);
uiHandler.sendMessage(message);
}
}
}

/**
* Disconnect from the proxy server.
*/
synchronized void disconnect() {
if (running) {
running = false;
if (currProcessor != null) {
currProcessor.terminate();
}
}
}

/**
* Start rpc processor and connect to the proxy server.
* @param host proxy server host.
* @param port proxy server port.
* @param key proxy server key.
*/
synchronized void connect(String host, int port, String key) {
this.host = host;
this.port = port;
this.key = key;
running = true;
notify();
}
}
42 changes: 42 additions & 0 deletions apps/android_rpc/app/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
LOCAL_PATH := $(call my-dir)
MY_PATH := $(LOCAL_PATH)

include $(CLEAR_VARS)

LOCAL_PATH := $(MY_PATH)
ROOT_PATH := $(MY_PATH)/../../../../../..

ifndef config
ifneq ("$(wildcard ./config.mk)","")
config ?= config.mk
else
config ?= make/config.mk
endif
endif

include $(config)

LOCAL_SRC_FILES := ml_dmlc_tvm_native_c_api.cc
LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib/ -llog

LOCAL_C_INCLUDES := $(ROOT_PATH)/include \
$(ROOT_PATH)/dlpack/include \
$(ROOT_PATH)/dmlc-core/include \
$(ROOT_PATH)/HalideIR/src \
$(ROOT_PATH)/topi/include

LOCAL_MODULE = tvm4j_runtime_packed

LOCAL_CPP_FEATURES += exceptions
LOCAL_LDLIBS += -latomic
LOCAL_ARM_MODE := arm

ifdef ADD_C_INCLUDES
LOCAL_C_INCLUDES += $(ADD_C_INCLUDES)
endif

ifdef ADD_LDLIBS
LOCAL_LDLIBS += $(ADD_LDLIBS)
endif

include $(BUILD_SHARED_LIBRARY)
16 changes: 16 additions & 0 deletions apps/android_rpc/app/src/main/jni/Application.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ifndef config
ifneq ("$(wildcard ./config.mk)","")
config ?= config.mk
else
config ?= make/config.mk
endif
endif

include $(config)

APP_STL := gnustl_static

APP_CPPFLAGS += -DDMLC_LOG_STACK_TRACE=0 -DTVM4J_ANDROID=1 -std=c++11 -Oz -frtti
ifeq ($(USE_OPENCL), 1)
APP_CPPFLAGS += -DTVM_OPENCL_RUNTIME=1
endif
9 changes: 9 additions & 0 deletions apps/android_rpc/app/src/main/jni/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
PATH="$PATH:/usr/local/bin"
CURR_DIR=$(cd `dirname $0`; pwd)
ROOT_DIR="$CURR_DIR/../../../../../.."
javah -o $CURR_DIR/ml_dmlc_tvm_native_c_api.h -cp "$ROOT_DIR/jvm/core/target/*" ml.dmlc.tvm.LibInfo || exit -1
cp -f $ROOT_DIR/jvm/native/src/main/native/ml_dmlc_tvm_native_c_api.cc $CURR_DIR/ || exit -1
cp -f $ROOT_DIR/jvm/native/src/main/native/jni_helper_func.h $CURR_DIR/ || exit -1
rm -rf $CURR_DIR/../libs
ndk-build --directory=$CURR_DIR
Loading

0 comments on commit 0ee68d7

Please sign in to comment.