Skip to content
This repository has been archived by the owner on May 27, 2024. It is now read-only.

Commit

Permalink
add robust /dump/hierarchy, remove whatsinput
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed May 29, 2019
1 parent bc60656 commit 79f0529
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 192 deletions.
3 changes: 2 additions & 1 deletion DEVELOP.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ export GOPROXY=https://goproxy.io
export GO111MODULE=on

# 将assets目录下的文件打包成go代码
go get -v github.com/shurcooL/vfsgen # 不执行这个好像也没关系
go generate

# build for android binary
GOOS=linux GOARCH=arm go build -tags vfs
```
```
71 changes: 23 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,29 @@ $ curl $DEVICE_URL/info
}
```

## 获取Hierarchy
这个接口目前比较高级,当跟uiautomator通信失败的时候,它会在后台启动uiautomator这个服务,等它恢复正常了,在返回数据。

```bash
$ curl $DEVICE_URL/dump/hierarchy
{
"jsonrpc":"2.0",
"id":1559113464,
"result": "<?xml version='1.0> ... hierarchy ..."
}

# 停止掉uiautomator
$ curl -X DELETE $DEVICE_URL/uiautomator
Success

# 再次调用, 依然OK,只是可能要等个7~8s
$ curl $DEVICE_URL/dump/hierarchy
{
"jsonrpc": "2.0",
...
}
```

## 安装应用
```bash
$ curl -X POST -d url="http://some-host/some.apk" $DEVICE_URL/install
Expand Down Expand Up @@ -274,41 +297,6 @@ Websocket连接 `$DEVICE_URL/minitouch`, 一行行的按照JSON的格式写入
{"operation": "c"}
```

## Whatsinput交互协议
感谢 项目<https://github.com/willerce/WhatsInput>

Websocket连接 `$DEVICE_URL/whatsinput`, 接收JSON格式

### 手机 --> 前端
- 设置文本框内容

```json
{"text":"hello world", "type":"InputStart"}
```

开始编辑时的内容

- 结束编辑

```json
{"type": "InputFinish"}
```

### 前端 --> 手机
- KeyCode的输入

```json
{"type": "InputKey", "code": 66}
```

[KeyCode参考列表](https://testerhome.com/topics/1386)

- 编辑框内容输入

```json
{"type": "InputEdit", "text": "some text"}
```

# TODO
1. 目前安全性还是个问题,以后再想办法改善
2. 补全接口文档
Expand All @@ -317,19 +305,6 @@ Websocket连接 `$DEVICE_URL/whatsinput`, 接收JSON格式
# Logs
log path `/sdcard/atx-agent.log`

# Build from source
```bash
GOOS=linux GOARCH=arm go build
```

with html resource buildin

```bash
go get github.com/shurcooL/vfsgen
go generate
go build -tags vfs
```

## TODO
- [ ] 使用支持多线程下载的库 https://github.com/cavaliercoder/grab

Expand Down
49 changes: 23 additions & 26 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,46 @@ require (
github.com/codeskyblue/goreq v0.0.0-20180831024223-49450746aaef
github.com/codeskyblue/heartbeat v0.1.0
github.com/codeskyblue/procfs v0.0.0-20180206025622-a4741d578088
github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76
github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76 // indirect
github.com/franela/goblin v0.0.0-20181003173013-ead4ad1d2727 // indirect
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8
github.com/getlantern/context v0.0.0-20181106182922-539649cc3118
github.com/getlantern/errors v0.0.0-20180829142810-e24b7f4ff7c7
github.com/getlantern/context v0.0.0-20181106182922-539649cc3118 // indirect
github.com/getlantern/errors v0.0.0-20180829142810-e24b7f4ff7c7 // indirect
github.com/getlantern/go-update v0.0.0-20170504001518-d7c3f1ac97f8
github.com/getlantern/golog v0.0.0-20170508214112-cca714f7feb5
github.com/getlantern/hex v0.0.0-20160523043825-083fba3033ad
github.com/getlantern/hidden v0.0.0-20160523043807-d52a649ab33a
github.com/getlantern/ops v0.0.0-20170904182230-37353306c908
github.com/getlantern/stack v1.5.0
github.com/getlantern/golog v0.0.0-20170508214112-cca714f7feb5 // indirect
github.com/getlantern/hex v0.0.0-20160523043825-083fba3033ad // indirect
github.com/getlantern/hidden v0.0.0-20160523043807-d52a649ab33a // indirect
github.com/getlantern/ops v0.0.0-20170904182230-37353306c908 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db
github.com/gorilla/context v1.1.1
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/handlers v1.4.0
github.com/gorilla/mux v1.6.2
github.com/gorilla/websocket v1.4.0
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kr/binarydist v0.1.0
github.com/kr/binarydist v0.1.0 // indirect
github.com/kr/pty v1.1.1
github.com/levigross/grequests v0.0.0-20190130132859-37c80f76a0da
github.com/mholt/archiver v2.0.1-0.20171012052341-26cf5bb32d07+incompatible
github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e
github.com/nwaples/rardecode v1.0.0
github.com/nwaples/rardecode v1.0.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/openatx/androidutils v1.0.0
github.com/openatx/atx-server v0.0.0-20181121094919-899e3078125e
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2
github.com/pierrec/lz4 v2.0.5+incompatible
github.com/pierrec/xxHash v0.1.1
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 // indirect
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
github.com/pkg/errors v0.8.1
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d // indirect
github.com/qiniu/log v0.0.0-20140728010919-a304a74568d6
github.com/rs/cors v1.6.0
github.com/sevlyar/go-daemon v0.1.4
github.com/shogo82148/androidbinary v0.0.2-0.20190122102046-6d4ec43b2255
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371
github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca // indirect
github.com/sirupsen/logrus v1.1.1
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.3.0
github.com/ulikunitz/xz v0.5.5
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
golang.org/x/sys v0.0.0-20181121002834-0cf1ed9e522b
golang.org/x/tools v0.0.0-20181120234846-b5f2cae84da8 // indirect
github.com/ulikunitz/xz v0.5.5 // indirect
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 // indirect
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect
golang.org/x/sys v0.0.0-20181121002834-0cf1ed9e522b // indirect
)
140 changes: 23 additions & 117 deletions httprestapi.go → httpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"image/jpeg"
Expand All @@ -22,6 +21,8 @@ import (
"syscall"
"time"

"github.com/openatx/atx-agent/jsonrpc"

"github.com/codeskyblue/procfs"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -60,125 +61,30 @@ func (server *Server) initHTTPServer() {
renderHTML(w, "remote.html")
})

/* WhatsInput */
var whatsinput = struct {
ChangeC chan string
EditC chan string
KeyCodeC chan int
Recent string
}{make(chan string, 0), make(chan string, 0), make(chan int, 0), ""}

const whatsInputFinishedMagic = "__inputFinished__"

// simple pubsub system
m.HandleFunc("/whatsinput", func(w http.ResponseWriter, r *http.Request) {
host := r.Header.Get("Host")
log.Println("CONNECT", host)
conn, err := hijackHTTPRequest(w)
if err != nil {
log.Println("Hijack failed:", err)
return
}
quit := make(chan bool, 1)
go func() {
for {
select {
case text := <-whatsinput.EditC:
base64Str := base64.StdEncoding.EncodeToString([]byte(text)) + "\n"
conn.Write([]byte("I" + base64Str))
case keyCode := <-whatsinput.KeyCodeC:
conn.Write([]byte("K" + strconv.Itoa(keyCode)))
case <-time.After(10 * time.Second):
conn.Write([]byte("P")) // ping message
case <-quit:
return
}
}
}()

buf := make([]byte, 4096)
for {
_, err := conn.Read(buf)
if err != nil {
quit <- true
break
}
}
}).Methods("CONNECT")

// Send input to device
// Highly affected by project https://github.com/willerce/WhatsInput
// Also thanks to https://github.com/senzhk/ADBKeyBoard
m.HandleFunc("/whatsinput", singleFightNewerWebsocket(func(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
var v struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
Code int `json:"code,omitempty"`
}
quit := make(chan bool, 1)
go func() {
for {
select {
case msg := <-whatsinput.ChangeC:
log.Println("Receive msg", msg)
if msg == whatsInputFinishedMagic {
log.Println("FinishedInput")
ws.WriteJSON(map[string]string{
"type": "InputFinish",
})
} else {
ws.WriteJSON(map[string]string{
"type": "InputStart",
"text": msg,
})
}
case <-quit:
return
}
}
}()
for {
if err := ws.ReadJSON(&v); err != nil {
quit <- true
log.Println(err)
break
}
switch v.Type {
case "InputEdit":
select {
case whatsinput.EditC <- v.Text:
log.Println("Message sended", v.Text)
case <-time.After(100 * time.Millisecond):
}
// runShell("am", "broadcast", "-a", "ADB_SET_TEXT", "--es", "text", strconv.Quote(base64Str))
case "InputKey":
runShell("input", "keyevent", "KEYCODE_ENTER") // HOTFIX(ssx): need fix later
// runShell("am", "broadcast", "-a", "ADB_INPUT_KEYCODE", "--ei", "code", strconv.Itoa(v.Code))
}
// jsonrpc client to call uiautomator
rpcc := jsonrpc.NewClient("http://127.0.0.1:9008/jsonrpc/0")
rpcc.ErrorCallback = func() error {
if !service.Running("uiautomator") {
service.Start("uiautomator")
}
})).Methods("GET")
return nil
}
rpcc.ErrorFixTimeout = 20 * time.Second
rpcc.ServerOK = func() bool {
return service.Running("uiautomator")
}

m.HandleFunc("/whatsinput", func(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
if string(data) == "" {
http.Error(w, "Empty body", http.StatusForbidden)
// robust communicate with uiautomator
// If the service is down, restart it and wait it recover
m.HandleFunc("/dump/hierarchy", func(w http.ResponseWriter, r *http.Request) {
resp, err := rpcc.RobustCall("dumpWindowHierarchy", false) // false: no compress
if err != nil {
log.Println("Err:", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var input string
if data[0] == 'I' {
input = string(data[1:])
whatsinput.Recent = input
} else {
input = whatsInputFinishedMagic
whatsinput.Recent = ""
}
select {
case whatsinput.ChangeC <- input:
io.WriteString(w, "Success")
case <-time.After(100 * time.Millisecond):
io.WriteString(w, "No WebSocket client connected")
}
}).Methods("POST")
renderJSON(w, resp)
})

m.HandleFunc("/pidof/{pkgname}", func(w http.ResponseWriter, r *http.Request) {
pkgname := mux.Vars(r)["pkgname"]
Expand Down Expand Up @@ -211,7 +117,7 @@ func (server *Server) initHTTPServer() {
timeout := r.FormValue("timeout") // supported value: 60s, 1m. 60 is invalid
duration, err := time.ParseDuration(timeout)
if err != nil {
duration = 60*time.Second
duration = 60 * time.Second
}

output, err := runShellTimeout(duration, "am", "start", flags, "-n", packageName+"/"+mainActivity)
Expand Down
Loading

0 comments on commit 79f0529

Please sign in to comment.