├── server
├── .gitignore
├── src
│ └── main
│ │ ├── jni
│ │ ├── Application.mk
│ │ ├── Android.mk
│ │ └── forker.c
│ │ └── java
│ │ └── com
│ │ └── bennyhuo
│ │ └── shell
│ │ └── server
│ │ ├── Main.kt
│ │ ├── Log.kt
│ │ └── ShellServer.kt
├── buildJni.sh
└── build.gradle
├── shell
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ └── layout
│ │ │ ├── activity_main.xml
│ │ │ └── activity_shell.xml
│ │ ├── java
│ │ └── com
│ │ │ └── bennyhuo
│ │ │ └── shell
│ │ │ └── api
│ │ │ ├── ShellListener.kt
│ │ │ ├── Log.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── ShellNoSessionActivity.kt
│ │ │ ├── ShellSessionActivity.kt
│ │ │ └── Shell.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── pc
├── server.dex
├── shellserver
├── launcher.sh
├── pusher.sh
└── eadb
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── README.md
├── .gitignore
├── gradle.properties
├── LICENSE
├── gradlew.bat
└── gradlew
/server/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/shell/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':shell', ':server'
2 |
--------------------------------------------------------------------------------
/pc/server.dex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/pc/server.dex
--------------------------------------------------------------------------------
/pc/shellserver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/pc/shellserver
--------------------------------------------------------------------------------
/server/src/main/jni/Application.mk:
--------------------------------------------------------------------------------
1 | APP_ABI := all
2 | APP_PLATFORM := android-18
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/shell/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Shell
3 |
4 |
--------------------------------------------------------------------------------
/pc/launcher.sh:
--------------------------------------------------------------------------------
1 | #!/system/bin/sh
2 | echo "Start Server."
3 | pkill -f shellserver
4 | rootDir=/data/local/tmp
5 | exec ${rootDir}/shellserver
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/shell/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/server/buildJni.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -r src/main/libs
4 | rm -r src/main/obj
5 | cd src/main/jni
6 | cd ~
7 | source .bash_profile
8 | cd -
9 | $1/ndk-build
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/shell/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bennyhuo/Shell/HEAD/shell/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/server/src/main/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:=$(call my-dir)
2 | include $(CLEAR_VARS)
3 |
4 | LOCAL_MODULE := shellserver
5 | LOCAL_SRC_FILES := forker.c
6 | LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lm -llog
7 | include $(BUILD_EXECUTABLE)
--------------------------------------------------------------------------------
/shell/src/main/java/com/bennyhuo/shell/api/ShellListener.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.api
2 |
3 | /**
4 | * Created by benny on 03/03/2018.
5 | */
6 | interface ShellListener {
7 | fun onResult(newLine: String)
8 |
9 | fun onClosed() = Unit
10 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 29 19:51:05 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/server/src/main/java/com/bennyhuo/shell/server/Main.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Main")
2 |
3 | package com.bennyhuo.shell.server
4 |
5 | import android.os.Looper
6 |
7 | fun main(args: Array) {
8 | Looper.prepareMainLooper()
9 | ShellServer("ShellServer").start()
10 | Looper.loop()
11 | }
12 |
--------------------------------------------------------------------------------
/shell/src/main/java/com/bennyhuo/shell/api/Log.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.api
2 |
3 | import android.util.Log
4 |
5 | /**
6 | * Created by benny on 05/03/2018.
7 | */
8 | inline fun T.debug(log: Any?){
9 | Log.d(T::class.java.simpleName, log.toString())
10 | }
11 |
12 | inline fun T.warn(log: Any?){
13 | Log.e(T::class.java.simpleName, log.toString())
14 | }
--------------------------------------------------------------------------------
/server/src/main/java/com/bennyhuo/shell/server/Log.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.server
2 |
3 | import android.util.Log
4 |
5 | /**
6 | * Created by benny on 05/03/2018.
7 | */
8 | val DEBUG = false
9 |
10 | inline fun T.debug(log: Any?) {
11 | if (DEBUG)
12 | Log.d(T::class.java.simpleName, log.toString())
13 | }
14 |
15 | inline fun T.warn(log: Any?) {
16 | Log.e(T::class.java.simpleName, log.toString())
17 | }
--------------------------------------------------------------------------------
/pc/pusher.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo $PATH
4 | #取脚本所在目录
5 | dir=$(cd `dirname $0`; pwd)
6 |
7 | $dir/eadb push $dir/server.dex /data/local/tmp
8 | $dir/eadb push $dir/launcher.sh /data/local/tmp
9 | $dir/eadb push $dir/shellserver /data/local/tmp
10 |
11 | # 不输出 后台执行
12 | # 注意,第一个 nohup ... &是为了不阻塞电脑;第二个 nohup 是为了不让 Android 在 USB 断开后杀掉这个进程。
13 | # 第二个 nohup 如果后面加 &,oppo R9S 服务会在执行完命令立即关掉,奇怪
14 | # >/dev/null 会卡住 Gradle,&>/dev/null Gradle 任务执行完后会退出(对于 Mac Terminal 这两个效果一样)
15 | nohup $dir/eadb shell "nohup /data/local/tmp/launcher.sh > /dev/null 2>&1" &>/dev/null &
16 | echo 'SUCCESSFUL'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Shell
2 | Android Shell run without root. Just like adb shell does.
3 |
4 | A server runs in the background with shell permissions while client app should connect and send shell cmd to it though a socket connection.
5 |
6 | The shell session closes until the socket connection disconnect.
7 |
8 | You can achieve varieties of goals with this, e.g. take a bugreport without pc.
9 |
10 | # Thanks
11 |
12 | This project is mainly inspired by [Fairy](https://github.com/Zane96/Fairy), most of the server code and scripts are reuse in this project. You should install a server started by adb shell from pc so that it has shell permissions.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/android
2 |
3 | # Java class files
4 | *.class
5 |
6 | # Generated files
7 | bin/
8 | gen/
9 | out/
10 |
11 | # Gradle files
12 | .gradle/
13 | build/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Proguard folder generated by Eclipse
19 | proguard/
20 |
21 | # Log Files
22 | *.log
23 |
24 | # Android Studio Navigation editor temp files
25 | .navigation/
26 |
27 | # Android Studio captures folder
28 | captures/
29 |
30 | # Intellij
31 | *.iml
32 | .idea
33 |
34 | # Keystore files
35 | *.jks
36 |
37 | ### Android Patch ###
38 | gen-external-apklibs
39 |
40 | *.o
41 | server/src/main/libs
42 | server/src/main/obj
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/shell/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/shell/src/main/java/com/bennyhuo/shell/api/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.api
2 |
3 | import android.os.Bundle
4 | import android.support.v7.app.AppCompatActivity
5 | import kotlinx.android.synthetic.main.activity_main.*
6 | import org.jetbrains.anko.sdk25.coroutines.onClick
7 | import org.jetbrains.anko.startActivity
8 |
9 | /**
10 | * Created by benny on 07/03/2018.
11 | */
12 | class MainActivity: AppCompatActivity() {
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 | setContentView(R.layout.activity_main)
16 |
17 | noSessionActivity.onClick {
18 | startActivity()
19 | }
20 |
21 | sessionActivity.onClick {
22 | startActivity()
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/shell/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Bennyhuo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/shell/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/shell/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion 26
8 | buildToolsVersion '26.0.3'
9 | defaultConfig {
10 | applicationId "com.bennyhuo.shell"
11 | minSdkVersion 21
12 | targetSdkVersion 26
13 | versionCode 1
14 | versionName "1.0"
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(include: ['*.jar'], dir: 'libs')
27 | implementation 'com.android.support:appcompat-v7:26.1.0'
28 | implementation 'com.android.support:support-v4:26.1.0'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
31 | exclude group: 'com.android.support', module: 'support-annotations'
32 | })
33 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
34 | compile "org.jetbrains.anko:anko:$anko_version"
35 | }
36 |
--------------------------------------------------------------------------------
/shell/src/main/java/com/bennyhuo/shell/api/ShellNoSessionActivity.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.api
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.os.Handler
6 | import android.support.v7.app.AppCompatActivity
7 | import android.text.SpannableString
8 | import android.text.style.ForegroundColorSpan
9 | import android.view.KeyEvent
10 | import kotlinx.android.synthetic.main.activity_shell.*
11 | import java.io.File
12 |
13 | /**
14 | * Created by benny on 03/03/2018.
15 | */
16 | class ShellNoSessionActivity : AppCompatActivity(){
17 | private val handler = Handler()
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.activity_shell)
22 | cmd.setText("bugreport")
23 | cmd.setOnKeyListener { v, keyCode, event ->
24 | if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) {
25 | val cmdText = "${cmd.text}\n"
26 | cmd.setText("bugreport")
27 | val spannableString = SpannableString("$ $cmdText")
28 | val foregroundColorSpan = ForegroundColorSpan(Color.RED)
29 | spannableString.setSpan(foregroundColorSpan, 0, spannableString.length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
30 | result.append(spannableString)
31 | Shell.run(cmdText, File(externalCacheDir, "bugreport00.txt"))
32 |
33 | true
34 | } else {
35 | false
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/shell/src/main/res/layout/activity_shell.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
23 |
24 |
30 |
31 |
32 |
37 |
38 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/server/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 | apply plugin: 'kotlin'
3 |
4 | dependencies {
5 | implementation fileTree(dir: 'libs', include: ['*.jar'])
6 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
7 | compileOnly 'com.google.android:android:4.1.1.4'
8 | compile 'com.google.code.gson:gson:2.8.0'
9 | compile 'com.squareup.okio:okio:1.13.0'
10 | }
11 |
12 | sourceCompatibility = "1.7"
13 | targetCompatibility = "1.7"
14 |
15 | jar {
16 | from {
17 | configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
18 | }
19 | }
20 |
21 | Properties properties = new Properties()
22 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
23 | def sdkLocation = properties.getProperty("sdk.dir")
24 | def ndkLocation = properties.getProperty("ndk.dir")
25 |
26 | task ndkBuild(type:Exec, dependsOn: build, group: "shellserver") {
27 | commandLine "sh", "buildJni.sh", ndkLocation
28 | copy {
29 | from "src/main/libs/armeabi-v7a/shellserver"
30 | into "../pc"
31 | }
32 | }
33 |
34 | task buildDex(type: Exec, dependsOn: ndkBuild, group: 'shellserver'){
35 | def dxDirs = new File(sdkLocation,"build-tools")
36 | .list()
37 | .grep { it.matches("\\d+\\.\\d+\\.\\d+") }
38 | .sort()
39 | .reverse()
40 | def dxDir = dxDirs[0]
41 | executable sdkLocation+"/build-tools/"+dxDir+"/dx"
42 | println executable
43 | args "--dex", "--output=../pc/server.dex", "build/libs/server.jar"
44 | }
45 |
46 | task startServer(type: Exec, dependsOn: buildDex, group: 'shellserver'){
47 | environment 'PATH', System.getenv('PATH') + ":" + sdkLocation+"/platform-tools/"
48 | executable "../pc/pusher.sh"
49 | }
50 |
--------------------------------------------------------------------------------
/shell/src/main/java/com/bennyhuo/shell/api/ShellSessionActivity.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.api
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.os.Handler
6 | import android.support.v7.app.AppCompatActivity
7 | import android.text.SpannableString
8 | import android.text.style.ForegroundColorSpan
9 | import android.view.KeyEvent
10 | import kotlinx.android.synthetic.main.activity_shell.*
11 |
12 | /**
13 | * Created by benny on 03/03/2018.
14 | */
15 | class ShellSessionActivity : AppCompatActivity(), ShellListener {
16 | private val handler = Handler()
17 |
18 | private var shell = Shell()
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.activity_shell)
22 |
23 | shell.addListener(this)
24 | shell.open()
25 | cmd.setOnKeyListener { v, keyCode, event ->
26 | if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) {
27 | val cmdText = "${cmd.text}\n"
28 | cmd.setText("")
29 | val spannableString = SpannableString("$ $cmdText")
30 | val foregroundColorSpan = ForegroundColorSpan(Color.RED)
31 | spannableString.setSpan(foregroundColorSpan, 0, spannableString.length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
32 | result.append(spannableString)
33 | shell.execute(cmdText)
34 | true
35 | } else {
36 | false
37 | }
38 | }
39 | }
40 |
41 | override fun onDestroy() {
42 | super.onDestroy()
43 | shell.removeListener(this)
44 | shell.close()
45 | }
46 |
47 | override fun onResult(newLine: String) {
48 | handler.post { result.append("$newLine\n") }
49 | }
50 | }
--------------------------------------------------------------------------------
/server/src/main/jni/forker.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Zane on 2017/9/30.
3 | //
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #define LOGD(...) (__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__))
11 | #define LOGW(...) (__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__))
12 | #define LOGE(...) (__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
13 |
14 | #define TAG "ShellServer"
15 |
16 | static int server() {
17 | pid_t pid = fork();
18 | if (pid < 0) {
19 | return -1;
20 | } else if (pid == 0) {
21 | LOGD("child fork");
22 | } else {
23 | LOGD("parent");
24 | return pid;
25 | }
26 |
27 | //Java文件的执行参数
28 | char *args[] = {"app_process",
29 | "-Djava.class.path=/data/local/tmp/server.dex",
30 | "/data/local/tmp",
31 | "--nice-name=shellserver",
32 | "com.bennyhuo.shell.server.Main",
33 | NULL};
34 |
35 | LOGD("exec");
36 | //吊起来就不会返回
37 | return execvp(args[0], args);
38 | }
39 |
40 | //守护服务进程
41 | static void signal_handler(int signal) {
42 | LOGD("handle %d %d", signal, getpid());
43 | if (signal == SIGCHLD) {
44 | int status;
45 | pid_t pid;
46 | for (;;) {
47 | pid = waitpid(-1, &status, 0);
48 | LOGD("pid %d", pid);
49 | if (pid == -1) {
50 | return;
51 | }
52 | }
53 | }
54 | }
55 |
56 | static int loadDaemon() {
57 | sigset_t new_set;
58 | sigemptyset(&new_set);
59 |
60 | //先fork一次
61 | int i = server();
62 | LOGD("num %d", i);
63 | if (i <= 0) {
64 | return -1;
65 | }
66 |
67 | for (;;) {
68 | //等待信号量,阻塞
69 | sigsuspend(&new_set);
70 | //pause();
71 | LOGD("fork again");
72 | if (server() <= 0) {
73 | break;
74 | }
75 | }
76 | return 0;
77 | }
78 |
79 | //在shell脚本中已经kill掉了旧进程
80 | //这里不再判断是否还存在旧进程
81 | int main(int argc, char **argv) {
82 | LOGD("%d %s", argc, argv[0]);
83 | //注册SIGCHLD的信号处理函数
84 | signal(SIGCHLD, signal_handler);
85 |
86 | //fork出第一个sys内陷的进程
87 | switch (fork()) {
88 | case -1:
89 | perror("cannot fork");
90 | return -1;
91 | case 0:
92 | LOGD("fork daemon");
93 | break;
94 | default:
95 | //sleep(10);
96 | //_exit(0);
97 | for(;;){}
98 | }
99 |
100 | return loadDaemon();
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/server/src/main/java/com/bennyhuo/shell/server/ShellServer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Zane.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of 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,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.bennyhuo.shell.server
17 |
18 | import java.io.IOException
19 | import java.net.ServerSocket
20 | import java.net.Socket
21 | import java.util.concurrent.Executors
22 |
23 | /**
24 | * Created by Zane on 2017/10/16.
25 | * Email: zanebot96@gmail.com
26 | */
27 |
28 | private const val SERVER_PORT = 62741
29 | private const val CMD_EXIT = "exit"
30 |
31 | class ShellServer(name: String) : Thread(name) {
32 | private val executor = Executors.newCachedThreadPool()
33 |
34 | override fun run() {
35 | try {
36 | val serverSocket = ServerSocket(SERVER_PORT)
37 |
38 | debug("Server Socket open success: ${serverSocket.inetAddress}")
39 | while (true) {
40 | val socket: Socket
41 | try {
42 | socket = serverSocket.accept()
43 | debug("Client Socket accept success from: ${socket.inetAddress}")
44 | process(socket)
45 | } catch (e: IOException) {
46 | warn("Socket error in serverSocket accept(): ${e.message}")
47 | if (serverSocket.isClosed) {
48 | break
49 | }
50 | }
51 | }
52 | } catch (e: IOException) {
53 | warn("Socket error when open: ${e.message}")
54 | return
55 | }
56 | }
57 |
58 | private fun process(socket: Socket) {
59 | val shellProcess = ProcessBuilder("sh").redirectErrorStream(true).start()
60 |
61 | executor.execute {
62 | try {
63 | socket.getInputStream().bufferedReader().forEachLine {
64 | debug("[ShellInput] $it")
65 | if (it == CMD_EXIT) {
66 | shellProcess.outputStream.close()
67 | } else {
68 | shellProcess.outputStream.write("$it\n".toByteArray())
69 | shellProcess.outputStream.flush()
70 | }
71 | }
72 | } catch (e: Exception) {
73 | e.printStackTrace()
74 | warn("Failed to read from client or write to shell process.")
75 | try {
76 | shellProcess.destroy()
77 | socket.close()
78 | } catch (e: Exception) {
79 | warn("Failed to close socket or destroy shell process.")
80 | e.printStackTrace()
81 | }
82 | }
83 | }
84 |
85 | executor.execute {
86 | try {
87 | shellProcess.inputStream.bufferedReader().forEachLine {
88 | debug("[ShellOutput] $it")
89 | socket.getOutputStream().write("$it\n".toByteArray())
90 | socket.getOutputStream().flush()
91 | }
92 | } catch (e: Exception) {
93 | e.printStackTrace()
94 | warn("Failed to read from shell process or write to client.")
95 | } finally {
96 | try {
97 | shellProcess.destroy()
98 | socket.close()
99 | } catch (e: Exception) {
100 | warn("Failed to close socket or destroy shell process.")
101 | e.printStackTrace()
102 | }
103 | }
104 | warn("Destroy shell process.")
105 | }
106 | }
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/pc/eadb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding=utf-8
3 | import os
4 | import subprocess
5 | import sys
6 | import re
7 |
8 | __author__ = 'benny'
9 |
10 | MANU_XIAOMI = "xiaomi"
11 | MANU_ALPS = "alps"
12 |
13 |
14 | def getdevices():
15 | lines = os.popen('adb devices').readlines()
16 | found = False
17 | ret = []
18 | for line in lines:
19 | line = line.strip()
20 | if found:
21 | if line:
22 | ret.append(line.split('\t')[0])
23 | elif 'List of devices attached' == line:
24 | found = True
25 |
26 | return ret
27 |
28 |
29 | def get_right_value(value):
30 | return value.split('=')[1]
31 |
32 |
33 | def find(collection, target):
34 | for ele in collection:
35 | if target in ele:
36 | return ele.strip()
37 |
38 |
39 | def execute(cmd):
40 | return os.popen(cmd).readlines()
41 |
42 |
43 | def checkInstall(args, device):
44 | if len(args) < 3:
45 | return
46 |
47 | if args[0] == 'install' and args[1].startswith("-") and "f" in args[1]:
48 | print "Try to retrieve the package name ..."
49 | lines = os.popen('aapt dump badging %s | grep package:\ name' % args[2]).readlines()
50 | # package: name='com.tencent.wecarsyncassistant' versionCode='10001' versionName='1.0.1' platformBuildVersionName='5.0.1-1624448'
51 | for line in lines:
52 | if line:
53 | # print line
54 | result = re.match(r"package:\s*name='(.*)'\s*versionCode='(\d*)'\s*versionName='(.*?)'.*", line, )
55 | if result:
56 | package = result.group(1)
57 | versionCode = result.group(2)
58 | versionName = result.group(3)
59 |
60 | # print package, versionName, versionCode
61 | print "Success. Package name is \"%s\". \n" % package
62 | print "Try to uninstall \"%s\" ..." % package
63 |
64 | if "k" in args[1]:
65 | print "The data and cache directories will be preserved."
66 | uninstall_cmd = 'adb -s %s shell pm uninstall -k %s' % (device, package)
67 | else:
68 | uninstall_cmd = 'adb -s %s uninstall %s' % (device, package)
69 | outputs = os.popen(uninstall_cmd).readlines()
70 | for output in outputs:
71 | print output
72 | else:
73 | print "Error occurred while retrieving package name."
74 |
75 | break
76 |
77 |
78 | def getModel(device):
79 | build_prop_str = 'adb -s %s shell cat /system/build.prop' % device
80 | result_manufacturer_str = find(execute(build_prop_str), "ro.product.manufacturer")
81 | manufacturer = get_right_value(result_manufacturer_str).lower()
82 | if manufacturer == MANU_ALPS:
83 | manufacturer = get_right_value(find(execute(build_prop_str), "ro.product.brand="))
84 | manufacturer = manufacturer.lower()
85 |
86 | # 获取型号
87 | if MANU_XIAOMI == manufacturer:
88 | ro_build_product = get_right_value(find(execute(build_prop_str), "ro.build.product="))
89 | if 'HM' in ro_build_product:
90 | model = "HONGMI"
91 | else:
92 | model = "XIAOMI"
93 | else:
94 | model = get_right_value(find(execute(build_prop_str), "ro.product.model="))
95 |
96 | return model
97 |
98 |
99 | def restartServer(args):
100 | if args[0] == 'restart':
101 | subprocess.call(["adb", "kill-server"])
102 | subprocess.call(["adb", "start-server"])
103 | return True
104 | return False
105 |
106 |
107 | def exe_cmd(args, device):
108 | cmds = ["adb", "-s", device]
109 | checkInstall(args, device)
110 | cmds.extend(args)
111 | subprocess.call(cmds)
112 |
113 |
114 | if len(sys.argv) == 1:
115 | subprocess.call(["adb", "devices"])
116 | elif not restartServer(sys.argv[1:]):
117 | devices = getdevices()
118 | chosenDevice = None
119 | if len(devices) > 1:
120 | for i in range(0, len(devices)):
121 | try:
122 | model = getModel(devices[i])
123 | except:
124 | model = devices[i]
125 | print ("[%d] %s" % (i, model))
126 |
127 | try:
128 | chosen = raw_input("Choose a device to execute the command: [default all]: ")
129 | if chosen:
130 | index = int(chosen)
131 | chosenDevice = devices[index]
132 | except:
133 | pass
134 |
135 | if not chosenDevice:
136 | for chosenDevice in devices:
137 | args = sys.argv[1:]
138 | if args[0] == 'install' and args[1].startswith("-") and "a" in args[1] and len(args) > 2:
139 | apks = os.listdir(args[2])
140 | for apk in apks:
141 | newArgs = args[:]
142 | newArgs[2] = apk
143 | if args[1] == '-a':
144 | newArgs.pop(1)
145 | else:
146 | newArgs[1] = args[1].replace('a', '')
147 |
148 | exe_cmd(newArgs, chosenDevice)
149 | else:
150 | exe_cmd(args, chosenDevice)
151 | else:
152 | args = sys.argv[1:]
153 | if args[0] == 'install' and args[1].startswith("-") and "a" in args[1] and len(args) > 2:
154 | apks = os.listdir(args[2])
155 | for apk in apks:
156 | newArgs = args[:]
157 | newArgs[2] = apk
158 | if args[1] == '-a':
159 | newArgs.pop(1)
160 | else:
161 | newArgs[1] = args[1].replace('a', '')
162 | exe_cmd(newArgs, chosenDevice)
163 | else:
164 | exe_cmd(args, chosenDevice)
165 |
--------------------------------------------------------------------------------
/shell/src/main/java/com/bennyhuo/shell/api/Shell.kt:
--------------------------------------------------------------------------------
1 | package com.bennyhuo.shell.api
2 |
3 | import java.io.Closeable
4 | import java.io.File
5 | import java.net.InetSocketAddress
6 | import java.net.Socket
7 | import java.util.concurrent.ExecutorService
8 | import java.util.concurrent.Executors
9 |
10 | /**
11 | * Created by benny on 03/03/2018.
12 | */
13 | private const val CMD_EXIT = "exit"
14 |
15 | class Shell : Closeable {
16 | private var executor: ExecutorService? = null
17 | private var shellSocket: Socket? = null
18 |
19 | private val shellListeners = ArrayList()
20 |
21 | @Volatile
22 | private var isOpen = false
23 |
24 | fun addListener(listener: ShellListener) {
25 | synchronized(this) {
26 | shellListeners.add(listener)
27 | }
28 | }
29 |
30 | fun removeListener(listener: ShellListener) {
31 | synchronized(this) {
32 | shellListeners.remove(listener)
33 | }
34 | }
35 |
36 | fun execute(cmd: String) {
37 | if (isOpen) {
38 | val socket = shellSocket!!
39 | executor?.execute {
40 | try {
41 | socket.getOutputStream().write("$cmd\n".toByteArray())
42 | socket.getOutputStream().flush()
43 | } catch (e: Exception) {
44 | e.printStackTrace()
45 | } finally {
46 | }
47 | }
48 | }
49 | }
50 |
51 | fun executeOnce(cmd: String) {
52 | isOpen = true
53 | val socket = Socket()
54 | shellSocket = socket
55 | val executor = Executors.newCachedThreadPool()
56 | this.executor = executor
57 | executor.execute {
58 | socket.connect(InetSocketAddress("127.0.0.1", 62741))
59 | try {
60 | socket.getOutputStream().write("$cmd\n".toByteArray())
61 | socket.getOutputStream().write("$CMD_EXIT\n".toByteArray())
62 | socket.getOutputStream().flush()
63 | } catch (e: Exception) {
64 | e.printStackTrace()
65 | }
66 | executor.execute {
67 | try {
68 | socket.getInputStream().bufferedReader().forEachLine { newLine ->
69 | val listeners = synchronized(this) { shellListeners.clone() as List }
70 | listeners.forEach {
71 | it.onResult(newLine)
72 | }
73 | }
74 | } catch (e: Exception) {
75 | e.printStackTrace()
76 | } finally {
77 | try {
78 | socket.close()
79 | executor.shutdownNow()
80 | this.executor = null
81 | } catch (e: Exception) {
82 | e.printStackTrace()
83 | } finally {
84 | onClosed()
85 | }
86 | }
87 | }
88 | }
89 | }
90 |
91 | fun open() {
92 | if (isOpen) return
93 | isOpen = true
94 | val socket = Socket()
95 | shellSocket = socket
96 | val executor = Executors.newCachedThreadPool()
97 | this.executor = executor
98 | executor.execute {
99 | socket.connect(InetSocketAddress("127.0.0.1", 62741))
100 | executor.execute {
101 | try {
102 | socket.getInputStream().bufferedReader().forEachLine { newLine ->
103 | val listeners = synchronized(this) { shellListeners.clone() as List }
104 | listeners.forEach {
105 | it.onResult(newLine)
106 | }
107 | }
108 | } catch (e: Exception) {
109 | e.printStackTrace()
110 | } finally {
111 | try {
112 | socket.close()
113 | executor.shutdownNow()
114 | this.executor = null
115 | } catch (e: Exception) {
116 | e.printStackTrace()
117 | } finally {
118 | onClosed()
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
125 | override fun close() {
126 | execute(CMD_EXIT)
127 | isOpen = false
128 | }
129 |
130 | private fun onClosed() {
131 | warn("ShellSocket onClosed.")
132 | val listeners = synchronized(this) { shellListeners.clone() as List }
133 | listeners.forEach {
134 | it.onClosed()
135 | }
136 | }
137 |
138 | companion object {
139 | fun run(cmd: String, block: (String) -> Unit) {
140 | val shell = Shell()
141 | shell.addListener(object : ShellListener {
142 |
143 | val resultBuilder = StringBuilder()
144 |
145 | override fun onResult(newLine: String) {
146 | resultBuilder.append(newLine)
147 | .append('\n')
148 | }
149 |
150 | override fun onClosed() {
151 | block(resultBuilder.toString())
152 | }
153 | })
154 | shell.executeOnce(cmd)
155 | }
156 |
157 | fun run(cmd: String, file: File) {
158 | val shell = Shell()
159 | shell.addListener(object : ShellListener {
160 |
161 | val writer = file.bufferedWriter()
162 |
163 | override fun onResult(newLine: String) {
164 | writer.append(newLine)
165 | .append('\n')
166 | }
167 |
168 | override fun onClosed() {
169 | writer.close()
170 | warn("ShellOutput File: ${file.length()}")
171 | }
172 | })
173 | shell.executeOnce(cmd)
174 | }
175 |
176 | }
177 | }
--------------------------------------------------------------------------------