mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 05:32:43 +08:00
refactor ios: replace gidevice with go-ios
This commit is contained in:
35
examples/uitest/demo_harmony_test.go
Normal file
35
examples/uitest/demo_harmony_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
//go:build localtest
|
||||
|
||||
package uitest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
func TestHamonyDouyinFeedTest(t *testing.T) {
|
||||
testCase := &hrp.TestCase{
|
||||
Config: hrp.NewConfig("点播_抖音_滑动场景_随机间隔_android").
|
||||
WithVariables(map[string]interface{}{
|
||||
"device": "a38c2c5c",
|
||||
"query": "${ENV(query)}",
|
||||
}).
|
||||
SetAndroid(uixt.WithSerialNumber("$device")),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("启动抖音").
|
||||
Android().
|
||||
AppTerminate("com.ss.hm.ugc.aweme"),
|
||||
},
|
||||
}
|
||||
|
||||
if err := testCase.Dump2JSON("demo_android_swipe.json"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := hrp.Run(t, testCase)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,6 @@ func TestIOSDouyinLive(t *testing.T) {
|
||||
uixt.WithWDALogOn(true),
|
||||
uixt.WithWDAPort(8700),
|
||||
uixt.WithWDAMjpegPort(8800),
|
||||
uixt.WithIOSPerfOptions(
|
||||
uixt.WithIOSPerfSystemCPU(true),
|
||||
uixt.WithIOSPerfSystemMem(true),
|
||||
),
|
||||
),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("启动抖音").
|
||||
|
||||
@@ -18,13 +18,6 @@ func TestWDALog(t *testing.T) {
|
||||
SetIOS(
|
||||
uixt.WithWDALogOn(true),
|
||||
uixt.WithWDAPort(8700), uixt.WithWDAMjpegPort(8800),
|
||||
uixt.WithIOSPerfOptions(
|
||||
uixt.WithIOSPerfSystemCPU(true),
|
||||
uixt.WithIOSPerfSystemMem(true),
|
||||
uixt.WithIOSPerfNetwork(true),
|
||||
uixt.WithIOSPerfFPS(true),
|
||||
),
|
||||
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
|
||||
),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("启动抖音").
|
||||
|
||||
@@ -37,33 +37,11 @@ func convertTimeToSeconds(timeStr string) (int, error) {
|
||||
}
|
||||
|
||||
func initIOSDevice(uuid string) uixt.IDevice {
|
||||
perfOptions := []uixt.IOSPerfOption{}
|
||||
for _, p := range perf {
|
||||
switch p {
|
||||
case "sys_cpu":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemCPU(true))
|
||||
case "sys_mem":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemMem(true))
|
||||
case "sys_net":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemNetwork(true))
|
||||
case "sys_disk":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemDisk(true))
|
||||
case "network":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfNetwork(true))
|
||||
case "fps":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfFPS(true))
|
||||
case "gpu":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfGPU(true))
|
||||
}
|
||||
}
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfOutputInterval(interval*1000))
|
||||
|
||||
device, err := uixt.NewIOSDevice(
|
||||
uixt.WithUDID(uuid),
|
||||
uixt.WithWDAPort(8700), uixt.WithWDAMjpegPort(8800),
|
||||
uixt.WithResetHomeOnStartup(false), // not reset home on startup
|
||||
uixt.WithIOSPerfOptions(perfOptions...),
|
||||
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
|
||||
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to init ios device")
|
||||
@@ -267,7 +245,6 @@ func (wc *WorldCupLive) dumpResult() error {
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", " ")
|
||||
|
||||
wc.PerfFile = wc.driver.Device.StopPerf()
|
||||
err := encoder.Encode(wc)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("encode json failed")
|
||||
|
||||
@@ -59,17 +59,6 @@ func TestIOSDouyinWorldCupLive(t *testing.T) {
|
||||
uixt.WithWDALogOn(true),
|
||||
uixt.WithWDAPort(8700),
|
||||
uixt.WithWDAMjpegPort(8800),
|
||||
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
|
||||
uixt.WithIOSPerfOptions(
|
||||
uixt.WithIOSPerfNetwork(true),
|
||||
// uixt.WithIOSPerfBundleID("com.ss.iphone.ugc.Aweme"),
|
||||
),
|
||||
uixt.WithIOSPcapOptions(
|
||||
// uixt.WithIOSPcapAll(true),
|
||||
// uixt.WithIOSPcapPID(1234),
|
||||
// uixt.WithIOSPcapProcName("Awe"),
|
||||
uixt.WithIOSPcapBundleID("com.ss.iphone.ugc.Aweme"),
|
||||
),
|
||||
),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("启动抖音").
|
||||
|
||||
40
go.mod
40
go.mod
@@ -1,12 +1,15 @@
|
||||
module github.com/httprunner/httprunner/v4
|
||||
|
||||
go 1.22
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.7
|
||||
|
||||
require (
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241011071205-a1bec975e48c
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241009025217-ecb76cf5bd27
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/andybalholm/brotli v1.0.4
|
||||
github.com/danielpaulus/go-ios v1.0.144
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/getsentry/sentry-go v0.13.0
|
||||
@@ -29,9 +32,9 @@ require (
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/oauth2 v0.8.0
|
||||
golang.org/x/text v0.15.0
|
||||
golang.org/x/text v0.16.0
|
||||
google.golang.org/grpc v1.57.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -45,6 +48,7 @@ require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
@@ -60,9 +64,13 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/grandcat/zeroconf v1.0.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.10 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
@@ -76,29 +84,47 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.57 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/quic-go/quic-go v0.40.1-0.20231203135336-87ef8ec48d55 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.5.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20240405191320-0878b34101b5 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
|
||||
)
|
||||
|
||||
// replace github.com/httprunner/funplugin => ../funplugin
|
||||
|
||||
103
go.sum
103
go.sum
@@ -34,23 +34,15 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240918093157-b4feef0e5af0 h1:qsKGQS3A530QpOXY80ogzzhVpf25Q4WfHusSlRyNvLU=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240918093157-b4feef0e5af0/go.mod h1:0IrKgKT75jmlpi9N0Mi5xWKctJuKHFM6f+ZMQe5vnNs=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240920083733-6b2acbe67b03 h1:VddSSSko1t4Z9LICi2p2IGEuXjqasHCEweFpk1iqGR4=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240920083733-6b2acbe67b03/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240925072739-7441749ee5a7 h1:QX1dADVurN1suyofdznrDEdz8QCRIGd2h/jU5xkGcQU=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240925072739-7441749ee5a7/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240930033019-572b6e5ca845 h1:AiHYk33kEbDC3+lIvGj0WJx47JvmuX27pOmWdd2/zAc=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20240930033019-572b6e5ca845/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241008133702-1c71219a0303 h1:98fx5eC2fYzZKL48QMT1ykxBHii3JhY/b7QBFEpsC/8=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241008133702-1c71219a0303/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241009130937-5efa701d3f56 h1:KolYCn3C/sgWUotywSO4L/8r4lOAgP/2bh6YeIofOJY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241009130937-5efa701d3f56/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241011071205-a1bec975e48c h1:MagwhUnFDkOB/feAUSagsFQuaiqmZDBY0fFvs1tj8uU=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241011071205-a1bec975e48c/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241009025217-ecb76cf5bd27 h1:+wNJiEXXIUP6luKJRA4tfwDqfnWUON6LIopKD9tvUns=
|
||||
code.byted.org/iesqa/ghdc v0.0.0-20241009025217-ecb76cf5bd27/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
@@ -66,6 +58,8 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -84,11 +78,15 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/danielpaulus/go-ios v1.0.144 h1:gojkhtScKYMwC4YHKnoB7u910GmtArg5ugsfAk7W4wY=
|
||||
github.com/danielpaulus/go-ios v1.0.144/go.mod h1:ZkUcaC59yNba47j/+ULKsCi3dYPFwY9r39PxdmVmLHE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0=
|
||||
github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -96,6 +94,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
|
||||
@@ -117,6 +117,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
@@ -139,6 +141,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -174,6 +178,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -195,13 +201,19 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.4.10 h1:xUbmA4jC6Dq163/fWcp8P3JuHilrHHMLNRxzGQJ9hNk=
|
||||
@@ -213,6 +225,7 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
|
||||
github.com/httprunner/funplugin v0.5.5 h1:VU1a6kj1AsJ/ucIhhI5NLHXOP4xnW2JGgk50vBV3Zis=
|
||||
github.com/httprunner/funplugin v0.5.5/go.mod h1:YZzBBSOSdLZEpHZz0P2E5SOQ+o1+Fbn30oWS4RGHBz0=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
@@ -277,6 +290,9 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -295,8 +311,14 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -331,6 +353,10 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.40.1-0.20231203135336-87ef8ec48d55 h1:I4N3ZRnkZPbDN935Tg8QDf8fRpHp3bZ0U0/L42jBgNE=
|
||||
github.com/quic-go/quic-go v0.40.1-0.20231203135336-87ef8ec48d55/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -349,6 +375,10 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -357,6 +387,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -370,6 +401,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 h1:aeN+ghOV0b2VCmKKO3gqnDQ8mLbpABZgRR2FVYx4ouI=
|
||||
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9/go.mod h1:roo6cZ/uqpwKMuvPG0YmzI5+AmUiMWfjCBZpGXqbTxE=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
@@ -385,11 +418,15 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
@@ -400,8 +437,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -412,6 +450,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -433,6 +473,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -447,6 +489,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -463,11 +506,12 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -487,6 +531,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -500,6 +546,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -532,12 +579,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -548,11 +596,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -573,6 +623,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -594,10 +645,14 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -702,6 +757,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20240405191320-0878b34101b5 h1:DOUDfNS+CFMM46k18FRF5k/0yz5NhZYMiUQxf4xglIU=
|
||||
gvisor.dev/gvisor v0.0.0-20240405191320-0878b34101b5/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -716,3 +773,5 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
@@ -37,21 +37,20 @@ var listAppsCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
var applicationType gidevice.ApplicationType
|
||||
device.GetDeviceInfo()
|
||||
var applicationType uixt.ApplicationType
|
||||
switch appType {
|
||||
case "user":
|
||||
applicationType = gidevice.ApplicationTypeUser
|
||||
applicationType = uixt.ApplicationTypeUser
|
||||
case "system":
|
||||
applicationType = gidevice.ApplicationTypeSystem
|
||||
applicationType = uixt.ApplicationTypeSystem
|
||||
case "internal":
|
||||
applicationType = gidevice.ApplicationTypeInternal
|
||||
applicationType = uixt.ApplicationTypeInternal
|
||||
case "all":
|
||||
applicationType = gidevice.ApplicationTypeAny
|
||||
applicationType = uixt.ApplicationTypeAny
|
||||
}
|
||||
|
||||
result, err := device.InstallationProxyBrowse(
|
||||
gidevice.WithApplicationType(applicationType),
|
||||
gidevice.WithReturnAttributes("CFBundleVersion", "CFBundleDisplayName", "CFBundleIdentifier"))
|
||||
result, err := device.ListApps(applicationType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get app list failed %v", err)
|
||||
}
|
||||
|
||||
@@ -7,52 +7,20 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/danielpaulus/go-ios/ios"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
d gidevice.Device
|
||||
UDID string `json:"UDID"`
|
||||
Status string `json:"status"`
|
||||
ConnectionType string `json:"connectionType"`
|
||||
ConnectionSpeed int `json:"connectionSpeed"`
|
||||
DeviceDetail *DeviceDetail `json:"deviceDetail,omitempty"`
|
||||
}
|
||||
|
||||
type DeviceDetail struct {
|
||||
DeviceName string `json:"deviceName,omitempty"`
|
||||
DeviceClass string `json:"deviceClass,omitempty"`
|
||||
ProductVersion string `json:"productVersion,omitempty"`
|
||||
ProductType string `json:"productType,omitempty"`
|
||||
ProductName string `json:"productName,omitempty"`
|
||||
PasswordProtected bool `json:"passwordProtected,omitempty"`
|
||||
ModelNumber string `json:"modelNumber,omitempty"`
|
||||
SerialNumber string `json:"serialNumber,omitempty"`
|
||||
SIMStatus string `json:"simStatus,omitempty"`
|
||||
PhoneNumber string `json:"phoneNumber,omitempty"`
|
||||
CPUArchitecture string `json:"cpuArchitecture,omitempty"`
|
||||
ProtocolVersion string `json:"protocolVersion,omitempty"`
|
||||
RegionInfo string `json:"regionInfo,omitempty"`
|
||||
TimeZone string `json:"timeZone,omitempty"`
|
||||
UniqueDeviceID string `json:"uniqueDeviceID,omitempty"`
|
||||
WiFiAddress string `json:"wifiAddress,omitempty"`
|
||||
BuildVersion string `json:"buildVersion,omitempty"`
|
||||
}
|
||||
|
||||
func (device *Device) GetDetail() (*DeviceDetail, error) {
|
||||
value, err := device.d.GetValue("", "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get device detail failed")
|
||||
}
|
||||
detailByte, _ := json.Marshal(value)
|
||||
detail := &DeviceDetail{}
|
||||
json.Unmarshal(detailByte, detail)
|
||||
return detail, nil
|
||||
d ios.DeviceEntry
|
||||
UDID string `json:"UDID"`
|
||||
Status string `json:"status"`
|
||||
ConnectionType string `json:"connectionType"`
|
||||
ConnectionSpeed int `json:"connectionSpeed"`
|
||||
DeviceDetail *uixt.DeviceDetail `json:"deviceDetail,omitempty"`
|
||||
}
|
||||
|
||||
func (device *Device) GetStatus() string {
|
||||
@@ -89,7 +57,7 @@ var listDevicesCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
for _, d := range devices {
|
||||
deviceProperties := d.Properties()
|
||||
deviceProperties := d.Properties
|
||||
device := &Device{
|
||||
d: d,
|
||||
UDID: deviceProperties.SerialNumber,
|
||||
@@ -98,27 +66,16 @@ var listDevicesCmd = &cobra.Command{
|
||||
}
|
||||
device.Status = device.GetStatus()
|
||||
|
||||
if isDetail {
|
||||
device.DeviceDetail, err = device.GetDetail()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(device.ToFormat())
|
||||
} else {
|
||||
fmt.Println(device.UDID, device.ConnectionType, device.Status)
|
||||
}
|
||||
fmt.Println(device.UDID, device.ConnectionType, device.Status)
|
||||
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
udid string
|
||||
isDetail bool
|
||||
)
|
||||
var udid string
|
||||
|
||||
func init() {
|
||||
listDevicesCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's udid")
|
||||
listDevicesCmd.Flags().BoolVarP(&isDetail, "detail", "d", false, "print device's detail")
|
||||
iosRootCmd.AddCommand(listDevicesCmd)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package ios
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
@@ -14,15 +11,12 @@ var iosRootCmd = &cobra.Command{
|
||||
Short: "simple utils for ios device management",
|
||||
}
|
||||
|
||||
func getDevice(udid string) (gidevice.Device, error) {
|
||||
devices, err := uixt.GetIOSDevices(udid)
|
||||
func getDevice(udid string) (*uixt.IOSDevice, error) {
|
||||
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(devices) > 1 {
|
||||
return nil, fmt.Errorf("found multiple attached devices, please specify ios udid")
|
||||
}
|
||||
return devices[0], nil
|
||||
return device, nil
|
||||
}
|
||||
|
||||
func Init(rootCmd *cobra.Command) {
|
||||
|
||||
@@ -34,13 +34,8 @@ var installCmd = &cobra.Command{
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
driverExt, err := device.NewDriver()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = driverExt.Install(args[0])
|
||||
err = device.Install(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
@@ -51,7 +46,7 @@ var installCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().StringVarP(&udid, "serial", "s", "", "filter by device's serial")
|
||||
installCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's serial")
|
||||
|
||||
iosRootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package ios
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -33,22 +31,18 @@ var mountCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := device.GetValue("", "ProductVersion")
|
||||
images, errImage := device.ListImage()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get device ProductVersion failed: %v", err)
|
||||
return fmt.Errorf("list device images failed: %v", err)
|
||||
}
|
||||
log.Info().Str("version", value.(string)).Msg("get device version")
|
||||
|
||||
imageSignatures, errImage := device.Images()
|
||||
|
||||
if listDeveloperDiskImage {
|
||||
for i, imgSign := range imageSignatures {
|
||||
fmt.Printf("[%d] %s\n", i+1, base64.StdEncoding.EncodeToString(imgSign))
|
||||
for i, imgSign := range images {
|
||||
fmt.Printf("[%d] %s\n", i+1, imgSign)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if errImage == nil && len(imageSignatures) > 0 {
|
||||
if errImage == nil && len(images) > 0 {
|
||||
log.Info().Msg("ios developer image is already mounted")
|
||||
return nil
|
||||
}
|
||||
@@ -59,27 +53,8 @@ var mountCmd = &cobra.Command{
|
||||
return fmt.Errorf("developer disk image directory not exist: %s", developerDiskImageDir)
|
||||
}
|
||||
|
||||
ver := strings.Split(value.(string), ".")
|
||||
if len(ver) < 2 {
|
||||
return fmt.Errorf("got invalid device ProductVersion: %v", value)
|
||||
}
|
||||
version := ver[0] + "." + ver[1]
|
||||
|
||||
var dmgPath, signaturePath string
|
||||
if builtin.IsFilePathExists(filepath.Join(developerDiskImageDir, "DeveloperDiskImage.dmg")) {
|
||||
dmgPath = filepath.Join(developerDiskImageDir, "DeveloperDiskImage.dmg")
|
||||
signaturePath = filepath.Join(developerDiskImageDir, "DeveloperDiskImage.dmg.signature")
|
||||
} else if builtin.IsFilePathExists(filepath.Join(developerDiskImageDir, version, "DeveloperDiskImage.dmg")) {
|
||||
dmgPath = filepath.Join(developerDiskImageDir, version, "DeveloperDiskImage.dmg")
|
||||
signaturePath = filepath.Join(developerDiskImageDir, version, "DeveloperDiskImage.dmg.signature")
|
||||
} else {
|
||||
log.Error().Str("dir", developerDiskImageDir).Msgf(
|
||||
"developer disk image %s not found in directory", version)
|
||||
return fmt.Errorf("developer disk image %s not found", version)
|
||||
}
|
||||
|
||||
if err = device.MountDeveloperDiskImage(dmgPath, signaturePath); err != nil {
|
||||
return fmt.Errorf("mount developer disk image %s failed: %s", version, err)
|
||||
if err = device.MountImage(developerDiskImageDir); err != nil {
|
||||
return fmt.Errorf("mount developer disk image failed: %s", err)
|
||||
}
|
||||
|
||||
log.Info().Msg("mount developer disk image successfully")
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
package ios
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/config"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
var pcapCmd = &cobra.Command{
|
||||
Use: "pcap",
|
||||
Short: "capture ios network packets",
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
sdk.SendGA4Event("hrp_ios_pcap", map[string]interface{}{
|
||||
"args": strings.Join(args, "-"),
|
||||
"success": err == nil,
|
||||
"engagement_time_msec": time.Since(startTime).Milliseconds(),
|
||||
})
|
||||
}()
|
||||
|
||||
pcapOptions := []uixt.IOSPcapOption{}
|
||||
if pid > 0 {
|
||||
pcapOptions = append(pcapOptions, uixt.WithIOSPcapPID(pid))
|
||||
}
|
||||
if procName != "" {
|
||||
pcapOptions = append(pcapOptions, uixt.WithIOSPcapProcName(procName))
|
||||
}
|
||||
if bundleID != "" {
|
||||
pcapOptions = append(pcapOptions, uixt.WithIOSPcapBundleID(bundleID))
|
||||
}
|
||||
if len(pcapOptions) == 0 {
|
||||
pcapOptions = append(pcapOptions, uixt.WithIOSPcapAll(true))
|
||||
}
|
||||
|
||||
device, err := uixt.NewIOSDevice(
|
||||
uixt.WithUDID(udid),
|
||||
uixt.WithIOSPcapOptions(pcapOptions...),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to init ios device")
|
||||
}
|
||||
|
||||
err = builtin.EnsureFolderExists(config.ResultsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = device.StartPcap(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer device.StopPcap()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
|
||||
timer := time.NewTimer(time.Duration(timeDuration) * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
return nil
|
||||
case <-c:
|
||||
log.Warn().Msg("received signal, stop pcap")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
timeDuration int
|
||||
pid int
|
||||
procName string
|
||||
)
|
||||
|
||||
func init() {
|
||||
pcapCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
|
||||
pcapCmd.Flags().IntVarP(&pid, "pid", "p", 0, "specify process ID")
|
||||
pcapCmd.Flags().StringVarP(&procName, "procName", "n", "", "specify process name")
|
||||
pcapCmd.Flags().StringVarP(&bundleID, "bundleID", "b", "", "specify bundle ID")
|
||||
pcapCmd.Flags().IntVarP(&timeDuration, "duration", "t", 10, "specify time duraion in seconds")
|
||||
iosRootCmd.AddCommand(pcapCmd)
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package ios
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/config"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
var perfCmd = &cobra.Command{
|
||||
Use: "perf",
|
||||
Short: "capture ios performance data (cpu,mem,disk,net,fps,etc.)",
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
sdk.SendGA4Event("hrp_ios_perf", map[string]interface{}{
|
||||
"args": strings.Join(args, "-"),
|
||||
"success": err == nil,
|
||||
"engagement_time_msec": time.Since(startTime).Milliseconds(),
|
||||
})
|
||||
}()
|
||||
|
||||
perfOptions := []uixt.IOSPerfOption{}
|
||||
for _, p := range indicators {
|
||||
switch p {
|
||||
case "sys_cpu":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemCPU(true))
|
||||
case "sys_mem":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemMem(true))
|
||||
case "sys_net":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemNetwork(true))
|
||||
case "sys_disk":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemDisk(true))
|
||||
case "network":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfNetwork(true))
|
||||
case "fps":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfFPS(true))
|
||||
case "gpu":
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfGPU(true))
|
||||
}
|
||||
}
|
||||
perfOptions = append(perfOptions, uixt.WithIOSPerfOutputInterval(interval*1000))
|
||||
|
||||
device, err := uixt.NewIOSDevice(
|
||||
uixt.WithUDID(udid),
|
||||
uixt.WithIOSPerfOptions(perfOptions...),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to init ios device")
|
||||
}
|
||||
|
||||
err = builtin.EnsureFolderExists(config.ResultsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = device.StartPerf(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer device.StopPerf()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
|
||||
timer := time.NewTimer(time.Duration(timeDuration) * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
return nil
|
||||
case <-c:
|
||||
log.Warn().Msg("received signal, stop perf")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
interval int
|
||||
indicators []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
perfCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
|
||||
perfCmd.Flags().StringSliceVarP(&indicators, "indicators", "p", []string{"sys_cpu", "sys_mem"},
|
||||
"specify performance monitor, e.g. sys_cpu,sys_mem,sys_net,sys_disk,fps,network,gpu")
|
||||
perfCmd.Flags().IntVarP(&timeDuration, "duration", "t", 10, "specify time duraion in seconds")
|
||||
perfCmd.Flags().IntVarP(&interval, "interval", "i", 3, "set interval in seconds")
|
||||
iosRootCmd.AddCommand(perfCmd)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
@@ -30,34 +29,12 @@ var psCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
apps, err := device.AppList()
|
||||
runningProcesses, err := device.ListProcess(!isAll)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get ios apps failed")
|
||||
}
|
||||
|
||||
maxNameLen := 0
|
||||
mapper := make(map[string]interface{})
|
||||
for _, app := range apps {
|
||||
mapper[app.ExecutableName] = app.CFBundleIdentifier
|
||||
if len(app.ExecutableName) > maxNameLen {
|
||||
maxNameLen = len(app.ExecutableName)
|
||||
}
|
||||
}
|
||||
|
||||
runningProcesses, err := device.AppRunningProcesses()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get running processes failed")
|
||||
return err
|
||||
}
|
||||
for _, p := range runningProcesses {
|
||||
if !isAll && !p.IsApplication {
|
||||
continue
|
||||
}
|
||||
bundleID, ok := mapper[p.Name]
|
||||
if !ok {
|
||||
bundleID = ""
|
||||
}
|
||||
|
||||
fmt.Printf("%4d %-"+fmt.Sprintf("%d", maxNameLen)+"s %20s %s\n",
|
||||
fmt.Printf("%4d %-"+fmt.Sprintf("%d", len(runningProcesses))+"s %20s %s\n",
|
||||
p.Pid, p.Name, time.Since(p.StartDate).String(), bundleID)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
var rebootCmd = &cobra.Command{
|
||||
Use: "reboot",
|
||||
Short: "reboot or shutdown ios device",
|
||||
Short: "reboot ios device",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
startTime := time.Now()
|
||||
@@ -29,23 +29,16 @@ var rebootCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if isShutdown {
|
||||
err = device.Shutdown()
|
||||
} else {
|
||||
err = device.Reboot()
|
||||
}
|
||||
err = device.Reboot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("reboot %s success\n", device.Properties().UDID)
|
||||
fmt.Printf("reboot %s success\n", device.UDID)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var isShutdown bool
|
||||
|
||||
func init() {
|
||||
rebootCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
|
||||
rebootCmd.Flags().BoolVarP(&isShutdown, "shutdown", "s", false, "shutdown ios device")
|
||||
iosRootCmd.AddCommand(rebootCmd)
|
||||
}
|
||||
|
||||
59
hrp/cmd/ios/uninstall.go
Normal file
59
hrp/cmd/ios/uninstall.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package ios
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
)
|
||||
|
||||
var uninstallCmd = &cobra.Command{
|
||||
Use: "uninstall [flags] PACKAGE",
|
||||
Short: "uninstall Package atomically",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
sdk.SendGA4Event("hrp_adb_devices", map[string]interface{}{
|
||||
"args": strings.Join(args, "-"),
|
||||
"success": err == nil,
|
||||
"engagement_time_msec": time.Since(startTime).Milliseconds(),
|
||||
})
|
||||
}()
|
||||
if len(bundleId) == 0 {
|
||||
return fmt.Errorf("bundleId is empty")
|
||||
}
|
||||
|
||||
_, err = getDevice(udid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = device.Uninstall(bundleId)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("success")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var bundleId string
|
||||
|
||||
func init() {
|
||||
uninstallCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's serial")
|
||||
uninstallCmd.Flags().StringVarP(&bundleId, "bundleId", "b", "", "bundleId to uninstall")
|
||||
|
||||
iosRootCmd.AddCommand(uninstallCmd)
|
||||
}
|
||||
@@ -2,10 +2,7 @@ package ios
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -37,33 +34,24 @@ var xctestCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
log.Info().Str("bundleID", bundleID).Msg("run xctest")
|
||||
out, cancel, err := device.XCTest(bundleID)
|
||||
err = device.RunXCTest(bundleID, testRunnerBundleID, xctestConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "run xctest failed")
|
||||
}
|
||||
|
||||
done := make(chan os.Signal, 1)
|
||||
signal.Notify(done, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
// print xctest running logs
|
||||
go func() {
|
||||
for s := range out {
|
||||
fmt.Print(s)
|
||||
}
|
||||
done <- os.Interrupt
|
||||
}()
|
||||
|
||||
<-done
|
||||
cancel()
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var bundleID string
|
||||
var (
|
||||
bundleID string
|
||||
testRunnerBundleID string
|
||||
xctestConfig string
|
||||
)
|
||||
|
||||
func init() {
|
||||
xctestCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's udid")
|
||||
xctestCmd.Flags().StringVarP(&bundleID, "bundleID", "b", "", "specify ios bundleID")
|
||||
xctestCmd.Flags().StringVarP(&testRunnerBundleID, "testRunnerBundleID", "t", "", "specify ios testRunnerBundleID")
|
||||
xctestCmd.Flags().StringVarP(&xctestConfig, "xctestConfig", "x", "", "specify ios xctestConfig")
|
||||
iosRootCmd.AddCommand(xctestCmd)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/csv"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/locker"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -492,6 +494,69 @@ func DownloadFile(filePath string, fileUrl string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileExists(filepath string) bool {
|
||||
_, err := os.Stat(filepath)
|
||||
if os.IsNotExist(err) {
|
||||
return false // 文件不存在
|
||||
}
|
||||
return err == nil // 文件存在,且没有其他错误
|
||||
}
|
||||
|
||||
func DownloadFileByUrl(fileUrl string) (filePath string, err error) {
|
||||
// 使用 UUID 生成唯一文件名
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hash := md5.Sum([]byte(fileUrl))
|
||||
fileName := fmt.Sprintf("%x", hash)
|
||||
filePath = filepath.Join(cwd, fileName)
|
||||
locker.Lock(filePath)
|
||||
defer locker.Unlock(filePath)
|
||||
if fileExists(filePath) {
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
fmt.Printf("Downloading file to %s from URL %s\n", filePath, fileUrl)
|
||||
|
||||
// Create an HTTP client with default settings.
|
||||
client := &http.Client{}
|
||||
|
||||
// Build the HTTP GET request.
|
||||
req, err := http.NewRequest("GET", fileUrl, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Perform the request.
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Check the HTTP status code.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("failed to download file: %s", resp.Status)
|
||||
}
|
||||
|
||||
// Create the output file.
|
||||
outFile, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Copy the response body to the file.
|
||||
_, err = io.Copy(outFile, resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Printf("File downloaded successfully: %s\n", fileName)
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func RunCommand(cmdName string, args ...string) error {
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
log.Info().Str("command", cmd.String()).Msg("exec command")
|
||||
|
||||
@@ -1 +1 @@
|
||||
v5.0.0+2411211916
|
||||
v5.0.0+2411231506
|
||||
|
||||
@@ -1,446 +0,0 @@
|
||||
# gidevice
|
||||
|
||||
This module is initially forked from [electricbubble/gidevice@v0.6.2].
|
||||
|
||||
#### Devices
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, dev := range devices {
|
||||
log.Println(dev.Properties().SerialNumber, dev.Properties().ProductID, dev.Properties().DeviceID)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### GetValue
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
type DeviceDetail struct {
|
||||
DeviceName string `json:"DeviceName,omitempty"`
|
||||
DeviceColor string `json:"DeviceColor,omitempty"`
|
||||
DeviceClass string `json:"DeviceClass,omitempty"`
|
||||
ProductVersion string `json:"ProductVersion,omitempty"`
|
||||
ProductType string `json:"ProductType,omitempty"`
|
||||
ProductName string `json:"ProductName,omitempty"`
|
||||
ModelNumber string `json:"ModelNumber,omitempty"`
|
||||
SerialNumber string `json:"SerialNumber,omitempty"`
|
||||
SIMStatus string `json:"SIMStatus,omitempty"`
|
||||
PhoneNumber string `json:"PhoneNumber,omitempty"`
|
||||
CPUArchitecture string `json:"CPUArchitecture,omitempty"`
|
||||
ProtocolVersion string `json:"ProtocolVersion,omitempty"`
|
||||
RegionInfo string `json:"RegionInfo,omitempty"`
|
||||
TelephonyCapability bool `json:"TelephonyCapability,omitempty"`
|
||||
TimeZone string `json:"TimeZone,omitempty"`
|
||||
UniqueDeviceID string `json:"UniqueDeviceID,omitempty"`
|
||||
WiFiAddress string `json:"WiFiAddress,omitempty"`
|
||||
WirelessBoardSerialNumber string `json:"WirelessBoardSerialNumber,omitempty"`
|
||||
BluetoothAddress string `json:"BluetoothAddress,omitempty"`
|
||||
BuildVersion string `json:"BuildVersion,omitempty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatal("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
detail, err1 := d.GetValue("", "")
|
||||
if err1 != nil {
|
||||
fmt.Errorf("get %s device detail fail : %w", d.Properties().SerialNumber, err1)
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(detail)
|
||||
d1 := &DeviceDetail{}
|
||||
json.Unmarshal(data, d1)
|
||||
fmt.Println(d1)
|
||||
}
|
||||
```
|
||||
|
||||
#### DeveloperDiskImage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatal("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
imageSignatures, err := d.Images()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for i, imgSign := range imageSignatures {
|
||||
log.Printf("[%d] %s\n", i+1, base64.StdEncoding.EncodeToString(imgSign))
|
||||
}
|
||||
|
||||
dmgPath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg"
|
||||
signaturePath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg.signature"
|
||||
|
||||
err = d.MountDeveloperDiskImage(dmgPath, signaturePath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### App
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatalln("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
bundleID := "com.apple.Preferences"
|
||||
pid, err := d.AppLaunch(bundleID)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
err = d.AppKill(pid)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
runningProcesses, err := d.AppRunningProcesses()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for _, process := range runningProcesses {
|
||||
if process.IsApplication {
|
||||
log.Printf("%4d\t%-24s\t%-36s\t%s\n", process.Pid, process.Name, filepath.Base(process.RealAppName), process.StartDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Screenshot
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatalln("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
raw, err := d.Screenshot()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
img, format, err := image.Decode(raw)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
userHomeDir, _ := os.UserHomeDir()
|
||||
file, err := os.Create(userHomeDir + "/Desktop/s1." + format)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
switch format {
|
||||
case "png":
|
||||
err = png.Encode(file, img)
|
||||
case "jpeg":
|
||||
err = jpeg.Encode(file, img, nil)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Println(file.Name())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### SimulateLocation
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatalln("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
// https://api.map.baidu.com/lbsapi/getpoint/index.html
|
||||
if err = d.SimulateLocationUpdate(116.024067, 40.362639, gidevice.CoordinateSystemBD09); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// https://developer.amap.com/tools/picker
|
||||
// https://lbs.qq.com/tool/getpoint/index.html
|
||||
// if err = d.SimulateLocationUpdate(120.116979, 30.252876, gidevice.CoordinateSystemGCJ02); err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
|
||||
// if err = d.SimulateLocationUpdate(121.499763, 31.239580,gidevice.CoordinateSystemWGS84); err != nil {
|
||||
// if err = d.SimulateLocationUpdate(121.499763, 31.239580); err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
|
||||
// err = d.SimulateLocationRecover()
|
||||
// if err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### XCTest
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatal("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
out, cancel, err := d.XCTest("com.leixipaopao.WebDriverAgentRunner.xctrunner")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
done := make(chan os.Signal, 1)
|
||||
signal.Notify(done, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
for s := range out {
|
||||
fmt.Print(s)
|
||||
}
|
||||
}()
|
||||
|
||||
<-done
|
||||
cancel()
|
||||
fmt.Println()
|
||||
log.Println("DONE")
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Connect and Forward
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
"syscall"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usbmux, err := gidevice.NewUsbmux()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
devices, err := usbmux.Devices()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatal("No Device")
|
||||
}
|
||||
|
||||
d := devices[0]
|
||||
|
||||
localPort, remotePort := 8100, 8100
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", localPort))
|
||||
|
||||
go func(listener net.Listener) {
|
||||
for {
|
||||
var accept net.Conn
|
||||
if accept, err = listener.Accept(); err != nil {
|
||||
log.Println("accept:", err)
|
||||
}
|
||||
|
||||
fmt.Println("accept", accept.RemoteAddr())
|
||||
|
||||
rInnerConn, err := d.NewConnect(remotePort)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
rConn := rInnerConn.RawConn()
|
||||
_ = rConn.SetDeadline(time.Time{})
|
||||
|
||||
go func(lConn net.Conn) {
|
||||
go func(lConn, rConn net.Conn) {
|
||||
if _, err := io.Copy(lConn, rConn); err != nil {
|
||||
//do sth
|
||||
}
|
||||
}(lConn, rConn)
|
||||
go func(lConn, rConn net.Conn) {
|
||||
if _, err := io.Copy(rConn, lConn); err != nil {
|
||||
//do sth
|
||||
}
|
||||
}(lConn, rConn)
|
||||
}(accept)
|
||||
}
|
||||
}(listener)
|
||||
|
||||
done := make(chan os.Signal, syscall.SIGTERM)
|
||||
signal.Notify(done, os.Interrupt, os.Kill)
|
||||
<-done
|
||||
}
|
||||
```
|
||||
|
||||
[electricbubble/gidevice@v0.6.2]: https://github.com/electricbubble/gidevice/tree/v0.6.2
|
||||
@@ -1,552 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var ErrAfcStatNotExist = errors.New("afc stat: no such file or directory")
|
||||
|
||||
var _ Afc = (*afc)(nil)
|
||||
|
||||
func newAfc(client *libimobiledevice.AfcClient) *afc {
|
||||
return &afc{client: client}
|
||||
}
|
||||
|
||||
type afc struct {
|
||||
client *libimobiledevice.AfcClient
|
||||
}
|
||||
|
||||
func (c *afc) DiskInfo() (info *AfcDiskInfo, err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationGetDeviceInfo, nil, nil); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'DiskInfo': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return nil, fmt.Errorf("afc receive 'DiskInfo': %w", err)
|
||||
}
|
||||
|
||||
m := respMsg.Map()
|
||||
info = &AfcDiskInfo{
|
||||
Model: m["Model"],
|
||||
}
|
||||
if info.TotalBytes, err = strconv.ParseUint(m["FSTotalBytes"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
|
||||
}
|
||||
if info.FreeBytes, err = strconv.ParseUint(m["FSFreeBytes"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
|
||||
}
|
||||
if info.BlockSize, err = strconv.ParseUint(m["FSBlockSize"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) ReadDir(dirname string) (names []string, err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationReadDir, toCString(dirname), nil); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'ReadDir': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return nil, fmt.Errorf("afc receive 'ReadDir': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return nil, fmt.Errorf("afc 'ReadDir': %w", err)
|
||||
}
|
||||
|
||||
names = respMsg.Strings()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Stat(filename string) (info *AfcFileInfo, err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationGetFileInfo, toCString(filename), nil); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'Stat': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return nil, fmt.Errorf("afc receive 'Stat': %w", err)
|
||||
}
|
||||
|
||||
m := respMsg.Map()
|
||||
|
||||
if len(m) == 0 {
|
||||
return nil, ErrAfcStatNotExist
|
||||
}
|
||||
|
||||
info = &AfcFileInfo{
|
||||
source: m,
|
||||
name: path.Base(filename),
|
||||
ifmt: m["st_ifmt"],
|
||||
}
|
||||
if info.creationTime, err = strconv.ParseUint(m["st_birthtime"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Stat': %w", err)
|
||||
}
|
||||
if info.blocks, err = strconv.ParseUint(m["st_blocks"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Stat': %w", err)
|
||||
}
|
||||
if info.modTime, err = strconv.ParseUint(m["st_mtime"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Stat': %w", err)
|
||||
}
|
||||
if info.nlink, err = strconv.ParseUint(m["st_nlink"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Stat': %w", err)
|
||||
}
|
||||
if info.size, err = strconv.ParseUint(m["st_size"], 10, 64); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Stat': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Open(filename string, mode AfcFileMode) (file *AfcFile, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err = binary.Write(buf, binary.LittleEndian, uint64(mode)); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'Open': %w", err)
|
||||
}
|
||||
buf.Write(toCString(filename))
|
||||
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationFileOpen, buf.Bytes(), nil); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'Open': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return nil, fmt.Errorf("afc receive 'Open': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Open': %w", err)
|
||||
}
|
||||
|
||||
if respMsg.Operation != libimobiledevice.AfcOperationFileOpenResult {
|
||||
return nil, fmt.Errorf("afc operation mistake 'Open': '%d'", respMsg.Operation)
|
||||
}
|
||||
|
||||
file = &AfcFile{
|
||||
client: c.client,
|
||||
fd: respMsg.Uint64(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Remove(filePath string) (err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationRemovePath, toCString(filePath), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'Remove': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'Remove': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'Remove': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Rename(oldPath string, newPath string) (err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationRenamePath, toCString(oldPath, newPath), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'Rename': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'Rename': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'Rename': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Mkdir(path string) (err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationMakeDir, toCString(path), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'Mkdir': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'Mkdir': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'Mkdir': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Link(oldName string, newName string, linkType AfcLinkType) (err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, uint64(linkType))
|
||||
buf.Write(toCString(oldName, newName))
|
||||
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationMakeLink, buf.Bytes(), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'Link': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'Link': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'Link': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Truncate(filePath string, size int64) (err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, uint64(size))
|
||||
buf.Write(toCString(filePath))
|
||||
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationTruncateFile, buf.Bytes(), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'Truncate': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'Truncate': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'Truncate': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) SetFileModTime(filePath string, modTime time.Time) (err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, uint64(modTime.Unix()))
|
||||
buf.Write(toCString(filePath))
|
||||
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationSetFileModTime, buf.Bytes(), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'SetFileModTime': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'SetFileModTime': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'SetFileModTime': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) Hash(filePath string) ([]byte, error) {
|
||||
var err error
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationGetFileHash, toCString(filePath), nil); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'Hash': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return nil, fmt.Errorf("afc receive 'Hash': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return nil, fmt.Errorf("afc 'Hash': %w", err)
|
||||
}
|
||||
|
||||
return respMsg.Payload, nil
|
||||
}
|
||||
|
||||
func (c *afc) HashWithRange(filePath string, start, end uint64) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, start)
|
||||
_ = binary.Write(buf, binary.LittleEndian, end)
|
||||
buf.Write(toCString(filePath))
|
||||
|
||||
var err error
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationGetFileHashRange, buf.Bytes(), nil); err != nil {
|
||||
return nil, fmt.Errorf("afc send 'HashWithRange': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return nil, fmt.Errorf("afc receive 'HashWithRange': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return nil, fmt.Errorf("afc 'HashWithRange': %w", err)
|
||||
}
|
||||
|
||||
return respMsg.Payload, nil
|
||||
}
|
||||
|
||||
// RemoveAll since iOS6+
|
||||
func (c *afc) RemoveAll(path string) (err error) {
|
||||
if err = c.client.Send(libimobiledevice.AfcOperationRemovePathAndContents, toCString(path), nil); err != nil {
|
||||
return fmt.Errorf("afc send 'RemoveAll': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = c.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc receive 'RemoveAll': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc 'RemoveAll': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *afc) WriteFile(filename string, data []byte, perm AfcFileMode) (err error) {
|
||||
var file *AfcFile
|
||||
if file, err = c.Open(filename, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = file.Close()
|
||||
}()
|
||||
|
||||
if _, err = file.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func toCString(s ...string) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
for _, v := range s {
|
||||
buf.WriteString(v)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
type AfcDiskInfo struct {
|
||||
Model string
|
||||
TotalBytes uint64
|
||||
FreeBytes uint64
|
||||
BlockSize uint64
|
||||
}
|
||||
|
||||
type AfcFileInfo struct {
|
||||
name string
|
||||
|
||||
creationTime uint64
|
||||
blocks uint64
|
||||
ifmt string
|
||||
modTime uint64
|
||||
nlink uint64
|
||||
size uint64
|
||||
|
||||
source map[string]string
|
||||
}
|
||||
|
||||
func (f *AfcFileInfo) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *AfcFileInfo) Size() int64 {
|
||||
return int64(f.size)
|
||||
}
|
||||
|
||||
// func (f *AfcFileInfo) Mode() os.FileMode {
|
||||
// return os.ModeType
|
||||
// }
|
||||
|
||||
func (f *AfcFileInfo) ModTime() time.Time {
|
||||
return time.Unix(0, int64(f.modTime))
|
||||
}
|
||||
|
||||
func (f *AfcFileInfo) IsDir() bool {
|
||||
return f.ifmt == "S_IFDIR"
|
||||
}
|
||||
|
||||
// func (f *AfcFileInfo) Sys() interface{} {
|
||||
// return f.source
|
||||
// }
|
||||
|
||||
func (f *AfcFileInfo) CreationTime() time.Time {
|
||||
return time.Unix(0, int64(f.creationTime))
|
||||
}
|
||||
|
||||
// func (f *AfcFileInfo) Blocks() uint64 {
|
||||
// return f.blocks
|
||||
// }
|
||||
|
||||
// func (f *AfcFileInfo) Format() string {
|
||||
// return f.ifmt
|
||||
// }
|
||||
|
||||
// func (f *AfcFileInfo) Link() uint64 {
|
||||
// return f.nlink
|
||||
// }
|
||||
|
||||
// func (f *AfcFileInfo) PhysicalSize(info *AfcDiskInfo) int64 {
|
||||
// return int64(f.blocks * (info.BlockSize / 8))
|
||||
// }
|
||||
|
||||
type AfcFileMode uint32
|
||||
|
||||
const (
|
||||
AfcFileModeRdOnly AfcFileMode = 0x00000001
|
||||
AfcFileModeRw AfcFileMode = 0x00000002
|
||||
AfcFileModeWrOnly AfcFileMode = 0x00000003
|
||||
AfcFileModeWr AfcFileMode = 0x00000004
|
||||
AfcFileModeAppend AfcFileMode = 0x00000005
|
||||
AfcFileModeRdAppend AfcFileMode = 0x00000006
|
||||
)
|
||||
|
||||
type AfcLockType int
|
||||
|
||||
const (
|
||||
AfcLockTypeSharedLock AfcLockType = 1 | 4
|
||||
AfcLockTypeExclusiveLock AfcLockType = 2 | 4
|
||||
AfcLockTypeUnlock AfcLockType = 8 | 4
|
||||
)
|
||||
|
||||
type AfcFile struct {
|
||||
client *libimobiledevice.AfcClient
|
||||
fd uint64
|
||||
reader *bytes.Reader
|
||||
}
|
||||
|
||||
func (f *AfcFile) op(o ...uint64) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, f.fd)
|
||||
|
||||
if len(o) == 0 {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
for _, v := range o {
|
||||
_ = binary.Write(buf, binary.LittleEndian, v)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (f *AfcFile) Lock(lockType AfcLockType) (err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileRefLock, f.op(uint64(lockType)), nil); err != nil {
|
||||
return fmt.Errorf("afc file send 'Lock': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = f.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc file receive 'Lock': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc file 'Lock': %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *AfcFile) Unlock() (err error) {
|
||||
return f.Lock(AfcLockTypeUnlock)
|
||||
}
|
||||
|
||||
func (f *AfcFile) Read(b []byte) (n int, err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileRead, f.op(uint64(len(b))), nil); err != nil {
|
||||
return -1, fmt.Errorf("afc file send 'Read': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = f.client.Receive(); err != nil {
|
||||
return -1, fmt.Errorf("afc file receive 'Read': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return -1, fmt.Errorf("afc file 'Read': %w", err)
|
||||
}
|
||||
|
||||
if respMsg.Payload == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if f.reader == nil {
|
||||
f.reader = bytes.NewReader(respMsg.Payload)
|
||||
} else {
|
||||
f.reader.Reset(respMsg.Payload)
|
||||
}
|
||||
|
||||
return f.reader.Read(b)
|
||||
}
|
||||
|
||||
func (f *AfcFile) Write(b []byte) (n int, err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileWrite, f.op(), b); err != nil {
|
||||
return -1, fmt.Errorf("afc file send 'Write': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = f.client.Receive(); err != nil {
|
||||
return -1, fmt.Errorf("afc file receive 'Write': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return -1, fmt.Errorf("afc file 'Write': %w", err)
|
||||
}
|
||||
|
||||
n = len(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *AfcFile) Tell() (n uint64, err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileTell, f.op(), nil); err != nil {
|
||||
return 0, fmt.Errorf("afc file 'Tell': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = f.client.Receive(); err != nil {
|
||||
return 0, fmt.Errorf("afc file receive 'Tell': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return 0, fmt.Errorf("afc file 'Tell': %w", err)
|
||||
}
|
||||
|
||||
if respMsg.Operation != libimobiledevice.AfcOperationFileTellResult {
|
||||
return 0, fmt.Errorf("afc operation mistake 'Tell': '%d'", respMsg.Operation)
|
||||
}
|
||||
|
||||
n = respMsg.Uint64()
|
||||
return
|
||||
}
|
||||
|
||||
func (f *AfcFile) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileSeek, f.op(uint64(whence), uint64(offset)), nil); err != nil {
|
||||
return -1, fmt.Errorf("afc file 'Seek': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = f.client.Receive(); err != nil {
|
||||
return -1, fmt.Errorf("afc file receive 'Seek': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return -1, fmt.Errorf("afc file 'Seek': %w", err)
|
||||
}
|
||||
|
||||
var tell uint64
|
||||
if tell, err = f.Tell(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
ret = int64(tell)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *AfcFile) Truncate(size int64) (err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileSetSize, f.op(uint64(size)), nil); err != nil {
|
||||
return fmt.Errorf("afc file 'Truncate': %w", err)
|
||||
}
|
||||
var respMsg *libimobiledevice.AfcMessage
|
||||
if respMsg, err = f.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc file receive 'Truncate': %w", err)
|
||||
}
|
||||
if err = respMsg.Err(); err != nil {
|
||||
return fmt.Errorf("afc file 'Truncate': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (f *AfcFile) Close() (err error) {
|
||||
if err = f.client.Send(libimobiledevice.AfcOperationFileClose, f.op(), nil); err != nil {
|
||||
return fmt.Errorf("afc file 'Close': %w", err)
|
||||
}
|
||||
if _, err = f.client.Receive(); err != nil {
|
||||
return fmt.Errorf("afc file receive 'Close': %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type AfcLinkType int
|
||||
|
||||
const (
|
||||
AfcLinkTypeHardLink AfcLinkType = 1
|
||||
AfcLinkTypeSymLink AfcLinkType = 2
|
||||
)
|
||||
@@ -1,102 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var afcSrv Afc
|
||||
|
||||
func setupAfcSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if afcSrv, err = lockdownSrv.AfcService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_afc_DiskInfo(t *testing.T) {
|
||||
setupAfcSrv(t)
|
||||
|
||||
info, err := afcSrv.DiskInfo()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.Printf("%10s: %s\n", "Model", info.Model)
|
||||
log.Printf("%10s: %d\n", "BlockSize", info.BlockSize/8)
|
||||
log.Printf("%10s: %s\n", "FreeSpace", byteCountDecimal(int64(info.FreeBytes)))
|
||||
log.Printf("%10s: %s\n", "UsedSpace", byteCountDecimal(int64(info.TotalBytes-info.FreeBytes)))
|
||||
log.Printf("%10s: %s\n", "TotalSpace", byteCountDecimal(int64(info.TotalBytes)))
|
||||
}
|
||||
|
||||
func byteCountDecimal(b int64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%dB", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
func Test_afc_ReadDir(t *testing.T) {
|
||||
setupAfcSrv(t)
|
||||
|
||||
names, err := afcSrv.ReadDir("Downloads")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range names {
|
||||
t.Log(name)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_afc_Stat(t *testing.T) {
|
||||
setupAfcSrv(t)
|
||||
|
||||
fileInfo, err := afcSrv.Stat("Downloads/downloads.28.sqlitedb")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(fileInfo.Name())
|
||||
t.Log(fileInfo.IsDir())
|
||||
t.Log(fileInfo.CreationTime())
|
||||
t.Log(fileInfo.ModTime())
|
||||
t.Log(fileInfo.Size())
|
||||
t.Log(byteCountDecimal(fileInfo.Size()))
|
||||
}
|
||||
|
||||
func Test_afc_Open(t *testing.T) {
|
||||
setupAfcSrv(t)
|
||||
|
||||
afcFile, err := afcSrv.Open("DCIM/105APPLE/IMG_5977.JPEG", AfcFileModeRdOnly)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = afcFile.Close()
|
||||
}()
|
||||
|
||||
userHomeDir, _ := os.UserHomeDir()
|
||||
file, err := os.Create(userHomeDir + "/Desktop/tmp.jpeg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err = io.Copy(file, afcFile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"howett.net/plist"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ CrashReportMover = (*crashReportMover)(nil)
|
||||
|
||||
func newCrashReportMover(client *libimobiledevice.CrashReportMoverClient) *crashReportMover {
|
||||
return &crashReportMover{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type crashReportMover struct {
|
||||
client *libimobiledevice.CrashReportMoverClient
|
||||
afc Afc
|
||||
}
|
||||
|
||||
func (c *crashReportMover) readPing() (err error) {
|
||||
var data []byte
|
||||
if data, err = c.client.InnerConn().Read(4); err != nil {
|
||||
return err
|
||||
}
|
||||
if string(data) != "ping" {
|
||||
return fmt.Errorf("crashReportMover ping: %v", data)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *crashReportMover) Move(hostDir string, opts ...CrashReportMoverOption) (err error) {
|
||||
opt := defaultCrashReportMoverOption()
|
||||
for _, fn := range opts {
|
||||
fn(opt)
|
||||
}
|
||||
|
||||
toExtract := make([]string, 0, 64)
|
||||
|
||||
fn := func(cwd string, info *AfcFileInfo) {
|
||||
if info.IsDir() {
|
||||
return
|
||||
}
|
||||
if cwd == "." {
|
||||
cwd = ""
|
||||
}
|
||||
|
||||
devFilename := path.Join(cwd, info.Name())
|
||||
hostElem := strings.Split(devFilename, "/")
|
||||
hostFilename := filepath.Join(hostDir, filepath.Join(hostElem...))
|
||||
hostFilename = strings.TrimSuffix(hostFilename, ".synced")
|
||||
|
||||
if opt.extract && strings.HasSuffix(hostFilename, ".plist") {
|
||||
toExtract = append(toExtract, hostFilename)
|
||||
}
|
||||
|
||||
var afcFile *AfcFile
|
||||
if afcFile, err = c.afc.Open(devFilename, AfcFileModeRdOnly); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover open %s: %s", devFilename, err))
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err = afcFile.Close(); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover device file close: %s", err))
|
||||
}
|
||||
}()
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(hostFilename), 0o755); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover mkdir %s: %s", filepath.Dir(hostFilename), err))
|
||||
return
|
||||
}
|
||||
var hostFile *os.File
|
||||
if hostFile, err = os.Create(hostFilename); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover create %s: %s", hostFilename, err))
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err = hostFile.Close(); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover host file close: %s", err))
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err = io.Copy(hostFile, afcFile); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover copy %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
opt.whenDone(devFilename)
|
||||
|
||||
if opt.keep {
|
||||
return
|
||||
}
|
||||
|
||||
if err = c.afc.Remove(devFilename); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover remove %s: %s", devFilename, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = c.walkDir(".", fn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !opt.extract {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, name := range toExtract {
|
||||
data, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover extract read %s: %s", name, err))
|
||||
continue
|
||||
}
|
||||
m := make(map[string]interface{})
|
||||
if _, err = plist.Unmarshal(data, &m); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover extract plist %s: %s", name, err))
|
||||
continue
|
||||
}
|
||||
|
||||
desc, ok := m["description"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
hostExtCrash := strings.TrimSuffix(name, ".plist") + ".crash"
|
||||
if err = os.WriteFile(hostExtCrash, []byte(fmt.Sprintf("%v", desc)), 0o755); err != nil {
|
||||
debugLog(fmt.Sprintf("crashReportMover extract save %s: %s", name, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *crashReportMover) walkDir(dirname string, fn func(path string, info *AfcFileInfo)) (err error) {
|
||||
var names []string
|
||||
if names, err = c.afc.ReadDir(dirname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cwd := dirname
|
||||
|
||||
for _, n := range names {
|
||||
if n == "." || n == ".." {
|
||||
continue
|
||||
}
|
||||
|
||||
var info *AfcFileInfo
|
||||
if info, err = c.afc.Stat(path.Join(cwd, n)); err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if err = c.walkDir(path.Join(cwd, info.name), fn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fn(cwd, info)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var crashReportMoverSrv CrashReportMover
|
||||
|
||||
func setupCrashReportMoverSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if crashReportMoverSrv, err = lockdownSrv.CrashReportMoverService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_crashReportMover_Move(t *testing.T) {
|
||||
setupCrashReportMoverSrv(t)
|
||||
|
||||
SetDebug(true)
|
||||
userHomeDir, _ := os.UserHomeDir()
|
||||
// err := crashReportMoverSrv.Move(userHomeDir + "/Documents/temp/2021-04/out_gidevice")
|
||||
// err := crashReportMoverSrv.Move(userHomeDir+"/Documents/temp/2021-04/out_gidevice",
|
||||
err := crashReportMoverSrv.Move(userHomeDir+"/Documents/temp/2021-04/out_gidevice_extract",
|
||||
WithKeepCrashReport(true),
|
||||
WithExtractRawCrashReport(true),
|
||||
WithWhenMoveIsDone(func(filename string) {
|
||||
fmt.Println("Copy:", filename)
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,959 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"howett.net/plist"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/ipa"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
const LockdownPort = 62078
|
||||
|
||||
var _ Device = (*device)(nil)
|
||||
|
||||
func newDevice(client *libimobiledevice.UsbmuxClient, properties DeviceProperties) *device {
|
||||
return &device{
|
||||
umClient: client,
|
||||
properties: &properties,
|
||||
}
|
||||
}
|
||||
|
||||
type device struct {
|
||||
umClient *libimobiledevice.UsbmuxClient
|
||||
lockdownClient *libimobiledevice.LockdownClient
|
||||
|
||||
properties *DeviceProperties
|
||||
|
||||
lockdown *lockdown
|
||||
imageMounter ImageMounter
|
||||
screenshot Screenshot
|
||||
simulateLocation SimulateLocation
|
||||
installationProxy InstallationProxy
|
||||
instruments Instruments
|
||||
afc Afc
|
||||
houseArrest HouseArrest
|
||||
syslogRelay SyslogRelay
|
||||
diagnosticsRelay DiagnosticsRelay
|
||||
springBoard SpringBoard
|
||||
crashReportMover CrashReportMover
|
||||
pcapd Pcapd
|
||||
perfd []Perfd
|
||||
}
|
||||
|
||||
func (d *device) Properties() DeviceProperties {
|
||||
return *d.properties
|
||||
}
|
||||
|
||||
func (d *device) NewConnect(port int, timeout ...time.Duration) (InnerConn, error) {
|
||||
newClient, err := libimobiledevice.NewUsbmuxClient(timeout...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = newClient.NewPlistPacket(
|
||||
newClient.NewConnectRequest(d.properties.DeviceID, port),
|
||||
); err != nil {
|
||||
newClient.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = newClient.SendPacket(pkt); err != nil {
|
||||
newClient.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = newClient.ReceivePacket(); err != nil {
|
||||
newClient.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newClient.InnerConn(), err
|
||||
}
|
||||
|
||||
func (d *device) ReadPairRecord() (pairRecord *PairRecord, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = d.umClient.NewPlistPacket(
|
||||
d.umClient.NewReadPairRecordRequest(d.properties.SerialNumber),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = d.umClient.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = d.umClient.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply := struct {
|
||||
Data []byte `plist:"PairRecordData"`
|
||||
}{}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var record PairRecord
|
||||
if _, err = plist.Unmarshal(reply.Data, &record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pairRecord = &record
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) SavePairRecord(pairRecord *PairRecord) (err error) {
|
||||
var data []byte
|
||||
if data, err = plist.Marshal(pairRecord, plist.XMLFormat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = d.umClient.NewPlistPacket(
|
||||
d.umClient.NewSavePairRecordRequest(d.properties.SerialNumber, d.properties.DeviceID, data),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = d.umClient.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = d.umClient.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) DeletePairRecord() (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = d.umClient.NewPlistPacket(
|
||||
d.umClient.NewDeletePairRecordRequest(d.properties.SerialNumber),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = d.umClient.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = d.umClient.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) lockdownService() (lockdown Lockdown, err error) {
|
||||
// if d.lockdown != nil {
|
||||
// return d.lockdown, nil
|
||||
// }
|
||||
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = d.NewConnect(LockdownPort, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.lockdownClient = libimobiledevice.NewLockdownClient(innerConn)
|
||||
d.lockdown = newLockdown(d)
|
||||
_, err = d.lockdown._getProductVersion()
|
||||
lockdown = d.lockdown
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) QueryType() (LockdownType, error) {
|
||||
if _, err := d.lockdownService(); err != nil {
|
||||
return LockdownType{}, err
|
||||
}
|
||||
return d.lockdown.QueryType()
|
||||
}
|
||||
|
||||
func (d *device) GetValue(domain, key string) (v interface{}, err error) {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.lockdown.pairRecord == nil {
|
||||
if err = d.lockdown.handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = d.lockdown.startSession(d.lockdown.pairRecord); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, err = d.lockdown.GetValue(domain, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = d.lockdown.stopSession()
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) Pair() (pairRecord *PairRecord, err error) {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.lockdown.Pair()
|
||||
}
|
||||
|
||||
func (d *device) imageMounterService() (imageMounter ImageMounter, err error) {
|
||||
if d.imageMounter != nil {
|
||||
return d.imageMounter, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.imageMounter, err = d.lockdown.ImageMounterService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageMounter = d.imageMounter
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) Images(imgType ...string) (imageSignatures [][]byte, err error) {
|
||||
if _, err = d.imageMounterService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(imgType) == 0 {
|
||||
imgType = []string{"Developer"}
|
||||
}
|
||||
return d.imageMounter.Images(imgType[0])
|
||||
}
|
||||
|
||||
func (d *device) MountDeveloperDiskImage(dmgPath string, signaturePath string) (err error) {
|
||||
if _, err = d.imageMounterService(); err != nil {
|
||||
return err
|
||||
}
|
||||
devImgPath := "/private/var/mobile/Media/PublicStaging/staging.dimage"
|
||||
return d.imageMounter.UploadImageAndMount("Developer", devImgPath, dmgPath, signaturePath)
|
||||
}
|
||||
|
||||
func (d *device) screenshotService() (screenshot Screenshot, err error) {
|
||||
if d.screenshot != nil {
|
||||
return d.screenshot, nil
|
||||
}
|
||||
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.screenshot, err = d.lockdown.ScreenshotService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
screenshot = d.screenshot
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) Screenshot() (raw *bytes.Buffer, err error) {
|
||||
if _, err = d.screenshotService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.screenshot.Take()
|
||||
}
|
||||
|
||||
func (d *device) simulateLocationService() (simulateLocation SimulateLocation, err error) {
|
||||
if d.simulateLocation != nil {
|
||||
return d.simulateLocation, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.simulateLocation, err = d.lockdown.SimulateLocationService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
simulateLocation = d.simulateLocation
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) SimulateLocationUpdate(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error) {
|
||||
if _, err = d.simulateLocationService(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.simulateLocation.Update(longitude, latitude, coordinateSystem...)
|
||||
}
|
||||
|
||||
func (d *device) SimulateLocationRecover() (err error) {
|
||||
if _, err = d.simulateLocationService(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.simulateLocation.Recover()
|
||||
}
|
||||
|
||||
func (d *device) installationProxyService() (installationProxy InstallationProxy, err error) {
|
||||
if d.installationProxy != nil {
|
||||
return d.installationProxy, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.installationProxy, err = d.lockdown.InstallationProxyService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installationProxy = d.installationProxy
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) InstallationProxyBrowse(opts ...InstallationProxyOption) (currentList []interface{}, err error) {
|
||||
if _, err = d.installationProxyService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.installationProxy.Browse(opts...)
|
||||
}
|
||||
|
||||
func (d *device) InstallationProxyLookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error) {
|
||||
if _, err = d.installationProxyService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.installationProxy.Lookup(opts...)
|
||||
}
|
||||
|
||||
func (d *device) newInstrumentsService() (instruments Instruments, err error) {
|
||||
// NOTICE: each instruments service should have individual connection, otherwise it will be blocked
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return
|
||||
}
|
||||
return d.lockdown.InstrumentsService()
|
||||
}
|
||||
|
||||
func (d *device) instrumentsService() (instruments Instruments, err error) {
|
||||
if d.instruments != nil {
|
||||
return d.instruments, nil
|
||||
}
|
||||
if d.instruments, err = d.newInstrumentsService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instruments = d.instruments
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error) {
|
||||
if _, err = d.instrumentsService(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return d.instruments.AppLaunch(bundleID, opts...)
|
||||
}
|
||||
|
||||
func (d *device) AppKill(pid int) (err error) {
|
||||
if _, err = d.instrumentsService(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.instruments.AppKill(pid)
|
||||
}
|
||||
|
||||
func (d *device) AppRunningProcesses() (processes []Process, err error) {
|
||||
if _, err = d.instrumentsService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.instruments.AppRunningProcesses()
|
||||
}
|
||||
|
||||
func (d *device) AppList(opts ...AppListOption) (apps []Application, err error) {
|
||||
if _, err = d.instrumentsService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.instruments.AppList(opts...)
|
||||
}
|
||||
|
||||
func (d *device) DeviceInfo() (devInfo *DeviceInfo, err error) {
|
||||
if _, err = d.instrumentsService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.instruments.DeviceInfo()
|
||||
}
|
||||
|
||||
func (d *device) testmanagerdService() (testmanagerd Testmanagerd, err error) {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if testmanagerd, err = d.lockdown.TestmanagerdService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) AfcService() (afc Afc, err error) {
|
||||
if d.afc != nil {
|
||||
return d.afc, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.afc, err = d.lockdown.AfcService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
afc = d.afc
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) AppInstall(ipaPath string) (err error) {
|
||||
if _, err = d.AfcService(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stagingPath := "PublicStaging"
|
||||
if _, err = d.afc.Stat(stagingPath); err != nil {
|
||||
if err != ErrAfcStatNotExist {
|
||||
return err
|
||||
}
|
||||
if err = d.afc.Mkdir(stagingPath); err != nil {
|
||||
return fmt.Errorf("app install: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var info map[string]interface{}
|
||||
if info, err = ipa.Info(ipaPath); err != nil {
|
||||
return err
|
||||
}
|
||||
bundleID, ok := info["CFBundleIdentifier"]
|
||||
if !ok {
|
||||
return errors.New("can't find 'CFBundleIdentifier'")
|
||||
}
|
||||
|
||||
installationPath := path.Join(stagingPath, fmt.Sprintf("%s.ipa", bundleID))
|
||||
|
||||
var data []byte
|
||||
if data, err = os.ReadFile(ipaPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = d.afc.WriteFile(installationPath, data, AfcFileModeWr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = d.installationProxyService(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.installationProxy.Install(fmt.Sprintf("%s", bundleID), installationPath)
|
||||
}
|
||||
|
||||
func (d *device) AppUninstall(bundleID string) (err error) {
|
||||
if _, err = d.installationProxyService(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.installationProxy.Uninstall(bundleID)
|
||||
}
|
||||
|
||||
func (d *device) HouseArrestService() (houseArrest HouseArrest, err error) {
|
||||
if d.houseArrest != nil {
|
||||
return d.houseArrest, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.houseArrest, err = d.lockdown.HouseArrestService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
houseArrest = d.houseArrest
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) syslogRelayService() (syslogRelay SyslogRelay, err error) {
|
||||
if d.syslogRelay != nil {
|
||||
return d.syslogRelay, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.syslogRelay, err = d.lockdown.SyslogRelayService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
syslogRelay = d.syslogRelay
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) Syslog() (lines <-chan string, err error) {
|
||||
if _, err = d.syslogRelayService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.syslogRelay.Lines(), nil
|
||||
}
|
||||
|
||||
func (d *device) SyslogStop() {
|
||||
if d.syslogRelay == nil {
|
||||
return
|
||||
}
|
||||
d.syslogRelay.Stop()
|
||||
}
|
||||
|
||||
func (d *device) Reboot() (err error) {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return
|
||||
}
|
||||
if d.diagnosticsRelay, err = d.lockdown.DiagnosticsRelayService(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = d.diagnosticsRelay.Reboot(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) Shutdown() (err error) {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return
|
||||
}
|
||||
if d.diagnosticsRelay, err = d.lockdown.DiagnosticsRelayService(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = d.diagnosticsRelay.Shutdown(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) springBoardService() (springBoard SpringBoard, err error) {
|
||||
if d.springBoard != nil {
|
||||
return d.springBoard, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.springBoard, err = d.lockdown.SpringBoardService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
springBoard = d.springBoard
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error) {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return
|
||||
}
|
||||
if d.springBoard, err = d.lockdown.SpringBoardService(); err != nil {
|
||||
return
|
||||
}
|
||||
if raw, err = d.springBoard.GetIconPNGData(bundleId); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) GetInterfaceOrientation() (orientation libimobiledevice.OrientationState, err error) {
|
||||
if _, err = d.springBoardService(); err != nil {
|
||||
return
|
||||
}
|
||||
if orientation, err = d.springBoard.GetInterfaceOrientation(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) PcapStart(opts ...PcapOption) (lines <-chan []byte, err error) {
|
||||
pcapOptions := &PcapOptions{}
|
||||
for _, fn := range opts {
|
||||
fn(pcapOptions)
|
||||
}
|
||||
log.Info().Interface("options", pcapOptions).Msg("pcap start")
|
||||
|
||||
// wait until get pid for bundle id
|
||||
if pcapOptions.BundleID != "" {
|
||||
instruments, err := d.newInstrumentsService()
|
||||
if err != nil {
|
||||
fmt.Printf("get pid by bundle id failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for {
|
||||
pid, err := instruments.getPidByBundleID(pcapOptions.BundleID)
|
||||
if err != nil {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
pcapOptions.Pid = pid
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if d.pcapd == nil {
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.pcapd, err = d.lockdown.PcapdService(
|
||||
pcapOptions.Pid, pcapOptions.ProcName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return d.pcapd.Packet(), nil
|
||||
}
|
||||
|
||||
func (d *device) PcapStop() {
|
||||
if d.pcapd == nil {
|
||||
return
|
||||
}
|
||||
d.pcapd.Stop()
|
||||
}
|
||||
|
||||
func (d *device) PerfStart(opts ...PerfOption) (data <-chan []byte, err error) {
|
||||
perfOptions := defaulPerfOption()
|
||||
for _, fn := range opts {
|
||||
fn(perfOptions)
|
||||
}
|
||||
|
||||
// wait until get pid for bundle id
|
||||
if perfOptions.BundleID != "" {
|
||||
instruments, err := d.newInstrumentsService()
|
||||
if err != nil {
|
||||
fmt.Printf("get pid by bundle id failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for {
|
||||
pid, err := instruments.getPidByBundleID(perfOptions.BundleID)
|
||||
if err != nil {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
perfOptions.Pid = pid
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// processAttributes must contain pid, or it can't get process info, reason unknown
|
||||
if !containString(perfOptions.ProcessAttributes, "pid") {
|
||||
perfOptions.ProcessAttributes = append(perfOptions.ProcessAttributes, "pid")
|
||||
}
|
||||
|
||||
outCh := make(chan []byte, 100)
|
||||
|
||||
if perfOptions.SysCPU || perfOptions.SysMem || perfOptions.SysDisk ||
|
||||
perfOptions.SysNetwork {
|
||||
perfd, err := d.newPerfdSysmontap(perfOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := perfd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
outCh <- (<-data)
|
||||
}
|
||||
}()
|
||||
d.perfd = append(d.perfd, perfd)
|
||||
}
|
||||
|
||||
if perfOptions.Network {
|
||||
perfd, err := d.newPerfdNetworking(perfOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := perfd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
outCh <- (<-data)
|
||||
}
|
||||
}()
|
||||
d.perfd = append(d.perfd, perfd)
|
||||
}
|
||||
|
||||
if perfOptions.FPS || perfOptions.gpu {
|
||||
perfd, err := d.newPerfdGraphicsOpengl(perfOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := perfd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
outCh <- (<-data)
|
||||
}
|
||||
}()
|
||||
d.perfd = append(d.perfd, perfd)
|
||||
}
|
||||
|
||||
return outCh, nil
|
||||
}
|
||||
|
||||
func (d *device) PerfStop() {
|
||||
if d.perfd == nil {
|
||||
return
|
||||
}
|
||||
for _, p := range d.perfd {
|
||||
p.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) crashReportMoverService() (crashReportMover CrashReportMover, err error) {
|
||||
if d.crashReportMover != nil {
|
||||
return d.crashReportMover, nil
|
||||
}
|
||||
if _, err = d.lockdownService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.crashReportMover, err = d.lockdown.CrashReportMoverService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crashReportMover = d.crashReportMover
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) MoveCrashReport(hostDir string, opts ...CrashReportMoverOption) (err error) {
|
||||
if _, err = d.crashReportMoverService(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.crashReportMover.Move(hostDir, opts...)
|
||||
}
|
||||
|
||||
func (d *device) XCTest(bundleID string, opts ...XCTestOption) (out <-chan string, cancel context.CancelFunc, err error) {
|
||||
xcTestOpt := defaultXCTestOption()
|
||||
for _, fn := range opts {
|
||||
fn(xcTestOpt)
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.TODO())
|
||||
_out := make(chan string)
|
||||
|
||||
xcodeVersion := uint64(30)
|
||||
|
||||
var tmSrv1 Testmanagerd
|
||||
if tmSrv1, err = d.testmanagerdService(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
var xcTestManager1 XCTestManagerDaemon
|
||||
if xcTestManager1, err = tmSrv1.newXCTestManagerDaemon(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
var version []int
|
||||
if version, err = d.lockdown._getProductVersion(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
if DeviceVersion(version...) >= DeviceVersion(11, 0, 0) {
|
||||
if err = xcTestManager1.initiateControlSession(xcodeVersion); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
}
|
||||
|
||||
var tmSrv2 Testmanagerd
|
||||
if tmSrv2, err = d.testmanagerdService(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
var xcTestManager2 XCTestManagerDaemon
|
||||
if xcTestManager2, err = tmSrv2.newXCTestManagerDaemon(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
xcTestManager2.registerCallback("_XCT_logDebugMessage:", func(m libimobiledevice.DTXMessageResult) {
|
||||
// more information ( each operation )
|
||||
// fmt.Println("###### xcTestManager2 ### -->", m)
|
||||
if strings.Contains(fmt.Sprintf("%s", m), "Received test runner ready reply with error: (null)") {
|
||||
// fmt.Println("###### xcTestManager2 ### -->", fmt.Sprintf("%v", m.Aux[0]))
|
||||
time.Sleep(time.Second)
|
||||
if err = xcTestManager2.startExecutingTestPlan(xcodeVersion); err != nil {
|
||||
debugLog(fmt.Sprintf("startExecutingTestPlan %d: %s", xcodeVersion, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
xcTestManager2.registerCallback("_Golang-iDevice_Unregistered", func(m libimobiledevice.DTXMessageResult) {
|
||||
// more information
|
||||
// _XCT_testRunnerReadyWithCapabilities:
|
||||
// _XCT_didBeginExecutingTestPlan
|
||||
// _XCT_didBeginInitializingForUITesting
|
||||
// _XCT_testSuite:didStartAt:
|
||||
// _XCT_testCase:method:willStartActivity:
|
||||
// _XCT_testCase:method:didFinishActivity:
|
||||
// _XCT_testCaseDidStartForTestClass:method:
|
||||
// fmt.Println("###### xcTestManager2 ### _Unregistered -->", m)
|
||||
})
|
||||
|
||||
sessionId := uuid.NewV4()
|
||||
if err = xcTestManager2.initiateSession(xcodeVersion, nskeyedarchiver.NewNSUUID(sessionId.Bytes())); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
if _, err = d.installationProxyService(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
var vResult interface{}
|
||||
if vResult, err = d.installationProxy.Lookup(WithBundleIDs(bundleID)); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
lookupResult := vResult.(map[string]interface{})
|
||||
lookupResult = lookupResult[bundleID].(map[string]interface{})
|
||||
appContainer := lookupResult["Container"].(string)
|
||||
appPath := lookupResult["Path"].(string)
|
||||
|
||||
var pathXCTestCfg string
|
||||
if pathXCTestCfg, err = d._uploadXCTestConfiguration(bundleID, sessionId, lookupResult); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
if _, err = d.instrumentsService(); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
if err = d.instruments.appProcess(bundleID); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
pathXCTestConfiguration := appContainer + pathXCTestCfg
|
||||
|
||||
appEnv := map[string]interface{}{
|
||||
"CA_ASSERT_MAIN_THREAD_TRANSACTIONS": "0",
|
||||
"CA_DEBUG_TRANSACTIONS": "0",
|
||||
"DYLD_FRAMEWORK_PATH": appPath + "/Frameworks:",
|
||||
"DYLD_LIBRARY_PATH": appPath + "/Frameworks",
|
||||
"NSUnbufferedIO": "YES",
|
||||
"SQLITE_ENABLE_THREAD_ASSERTIONS": "1",
|
||||
"WDA_PRODUCT_BUNDLE_IDENTIFIER": "",
|
||||
"XCTestConfigurationFilePath": pathXCTestConfiguration, // Running tests with active test configuration:
|
||||
// "XCTestBundlePath": fmt.Sprintf("%s/PlugIns/%s.xctest", appPath, name), // !!! ERROR
|
||||
// "XCTestSessionIdentifier": sessionId.String(), // !!! ERROR
|
||||
// "XCTestSessionIdentifier": "",
|
||||
"XCODE_DBG_XPC_EXCLUSIONS": "com.apple.dt.xctestSymbolicator",
|
||||
"MJPEG_SERVER_PORT": "",
|
||||
"USE_PORT": "",
|
||||
"LLVM_PROFILE_FILE": appContainer + "/tmp/%p.profraw",
|
||||
}
|
||||
if DeviceVersion(version...) >= DeviceVersion(11, 0, 0) {
|
||||
appEnv["DYLD_INSERT_LIBRARIES"] = "/Developer/usr/lib/libMainThreadChecker.dylib"
|
||||
appEnv["OS_ACTIVITY_DT_MODE"] = "YES"
|
||||
}
|
||||
appArgs := []interface{}{
|
||||
"-NSTreatUnknownArgumentsAsOpen", "NO",
|
||||
"-ApplePersistenceIgnoreState", "YES",
|
||||
}
|
||||
appOpt := map[string]interface{}{
|
||||
"StartSuspendedKey": uint64(0),
|
||||
}
|
||||
if DeviceVersion(version...) >= DeviceVersion(12, 0, 0) {
|
||||
appOpt["ActivateSuspended"] = uint64(1)
|
||||
}
|
||||
|
||||
if len(xcTestOpt.appEnv) != 0 {
|
||||
for k, v := range xcTestOpt.appEnv {
|
||||
appEnv[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if len(xcTestOpt.appOpt) != 0 {
|
||||
for k, v := range xcTestOpt.appEnv {
|
||||
appOpt[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
d.instruments.registerCallback("outputReceived:fromProcess:atTime:", func(m libimobiledevice.DTXMessageResult) {
|
||||
// fmt.Println("###### instruments ### -->", m.Aux[0])
|
||||
_out <- fmt.Sprintf("%s", m.Aux[0])
|
||||
})
|
||||
|
||||
var pid int
|
||||
if pid, err = d.instruments.AppLaunch(bundleID,
|
||||
WithAppPath(appPath),
|
||||
WithEnvironment(appEnv),
|
||||
WithArguments(appArgs),
|
||||
WithOptions(appOpt),
|
||||
WithKillExisting(true),
|
||||
); err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
// see https://github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/issues/31
|
||||
// if err = d.instruments.startObserving(pid); err != nil {
|
||||
// return _out, cancelFunc, err
|
||||
// }
|
||||
|
||||
if DeviceVersion(version...) >= DeviceVersion(12, 0, 0) {
|
||||
err = xcTestManager1.authorizeTestSession(pid)
|
||||
} else if DeviceVersion(version...) <= DeviceVersion(9, 0, 0) {
|
||||
err = xcTestManager1.initiateControlSessionForTestProcessID(pid)
|
||||
} else {
|
||||
err = xcTestManager1.initiateControlSessionForTestProcessIDProtocolVersion(pid, xcodeVersion)
|
||||
}
|
||||
if err != nil {
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
d.instruments.registerCallback("_Golang-iDevice_Over", func(_ libimobiledevice.DTXMessageResult) {
|
||||
cancelFunc()
|
||||
})
|
||||
|
||||
<-ctx.Done()
|
||||
tmSrv1.close()
|
||||
tmSrv2.close()
|
||||
xcTestManager1.close()
|
||||
xcTestManager2.close()
|
||||
if _err := d.AppKill(pid); _err != nil {
|
||||
debugLog(fmt.Sprintf("xctest kill: %d", pid))
|
||||
}
|
||||
// time.Sleep(time.Second)
|
||||
close(_out)
|
||||
}()
|
||||
|
||||
return _out, cancelFunc, err
|
||||
}
|
||||
|
||||
func (d *device) _uploadXCTestConfiguration(bundleID string, sessionId uuid.UUID, lookupResult map[string]interface{}) (pathXCTestCfg string, err error) {
|
||||
if _, err = d.HouseArrestService(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var appAfc Afc
|
||||
if appAfc, err = d.houseArrest.Container(bundleID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
appTmpFilenames, err := appAfc.ReadDir("/tmp")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, tName := range appTmpFilenames {
|
||||
if strings.HasSuffix(tName, ".xctestconfiguration") {
|
||||
if _err := appAfc.Remove(fmt.Sprintf("/tmp/%s", tName)); _err != nil {
|
||||
debugLog(fmt.Sprintf("remove /tmp/%s: %s", tName, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nameExec := lookupResult["CFBundleExecutable"].(string)
|
||||
name := nameExec[:len(nameExec)-len("-Runner")]
|
||||
appPath := lookupResult["Path"].(string)
|
||||
|
||||
pathXCTestCfg = fmt.Sprintf("/tmp/%s-%s.xctestconfiguration", name, strings.ToUpper(sessionId.String()))
|
||||
|
||||
var content []byte
|
||||
if content, err = nskeyedarchiver.Marshal(
|
||||
nskeyedarchiver.NewXCTestConfiguration(
|
||||
nskeyedarchiver.NewNSUUID(sessionId.Bytes()),
|
||||
nskeyedarchiver.NewNSURL(fmt.Sprintf("%s/PlugIns/%s.xctest", appPath, name)),
|
||||
bundleID,
|
||||
appPath,
|
||||
),
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = appAfc.WriteFile(pathXCTestCfg, content, AfcFileModeWr); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dev Device
|
||||
|
||||
func setupDevice(t *testing.T) {
|
||||
setupUsbmux(t)
|
||||
devices, err := um.Devices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
t.Fatal("No Device")
|
||||
}
|
||||
|
||||
dev = devices[0]
|
||||
}
|
||||
func Test_device_ReadPairRecord(t *testing.T) {
|
||||
setupDevice(t)
|
||||
|
||||
pairRecord, err := dev.ReadPairRecord()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(pairRecord.HostID, pairRecord.SystemBUID, pairRecord.WiFiMACAddress)
|
||||
}
|
||||
|
||||
func Test_device_NewConnect(t *testing.T) {
|
||||
setupDevice(t)
|
||||
|
||||
if _, err := dev.NewConnect(LockdownPort); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_device_DeletePairRecord(t *testing.T) {
|
||||
setupDevice(t)
|
||||
|
||||
if err := dev.DeletePairRecord(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_device_SavePairRecord(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
pairRecord, err := lockdownSrv.Pair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = dev.SavePairRecord(pairRecord)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_device_XCTest(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
bundleID = "com.leixipaopao.WebDriverAgentRunner.xctrunner"
|
||||
out, cancel, err := dev.XCTest(bundleID)
|
||||
// out, cancel, err := dev.XCTest(bundleID, WithXCTestEnv(map[string]interface{}{"USE_PORT": 8222, "MJPEG_SERVER_PORT": 8333}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
done := make(chan os.Signal, 1)
|
||||
signal.Notify(done, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
for s := range out {
|
||||
fmt.Print(s)
|
||||
}
|
||||
done <- os.Interrupt
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
cancel()
|
||||
fmt.Println()
|
||||
t.Log("DONE")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_device_AppInstall(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
ipaPath := "/private/tmp/derivedDataPath/Build/Products/Release-iphoneos/WebDriverAgentRunner-Runner.ipa"
|
||||
err := dev.AppInstall(ipaPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_device_AppUninstall(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
bundleID = "com.leixipaopao.WebDriverAgentRunner.xctrunner"
|
||||
err := dev.AppUninstall(bundleID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_device_Syslog(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
dev.SyslogStop()
|
||||
|
||||
lines, err := dev.Syslog()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
done := make(chan os.Signal, 1)
|
||||
|
||||
go func() {
|
||||
for line := range lines {
|
||||
fmt.Println(line)
|
||||
}
|
||||
done <- os.Interrupt
|
||||
t.Log("DONE!!!")
|
||||
}()
|
||||
|
||||
signal.Notify(done, os.Interrupt, os.Kill)
|
||||
|
||||
// <-done
|
||||
time.Sleep(3 * time.Second)
|
||||
dev.SyslogStop()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
func Test_device_Reboot(t *testing.T) {
|
||||
setupDevice(t)
|
||||
dev.Reboot()
|
||||
}
|
||||
|
||||
func Test_device_Shutdown(t *testing.T) {
|
||||
setupDevice(t)
|
||||
dev.Shutdown()
|
||||
}
|
||||
|
||||
func Test_device_InstallationProxyBrowse(t *testing.T) {
|
||||
setupDevice(t)
|
||||
|
||||
list, err := dev.InstallationProxyBrowse(
|
||||
WithApplicationType(ApplicationTypeUser),
|
||||
WithReturnAttributes("CFBundleDisplayName", "CFBundleIdentifier", "SequenceNumber", "SequenceNumber"),
|
||||
)
|
||||
// list, err := dev.InstallationProxyBrowse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(len(list))
|
||||
|
||||
for _, l := range list {
|
||||
t.Logf("%#v", l)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import "github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
|
||||
func newDiagnosticsRelay(client *libimobiledevice.DiagnosticsRelayClient) *diagnostics {
|
||||
return &diagnostics{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type diagnostics struct {
|
||||
client *libimobiledevice.DiagnosticsRelayClient
|
||||
}
|
||||
|
||||
func (d *diagnostics) Reboot() (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = d.client.NewXmlPacket(
|
||||
d.client.NewBasicRequest("Restart"),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
if err = d.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *diagnostics) Shutdown() (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = d.client.NewXmlPacket(
|
||||
d.client.NewBasicRequest("Shutdown"),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
if err = d.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import "github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
|
||||
var _ HouseArrest = (*houseArrest)(nil)
|
||||
|
||||
func newHouseArrest(client *libimobiledevice.HouseArrestClient) *houseArrest {
|
||||
return &houseArrest{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type houseArrest struct {
|
||||
client *libimobiledevice.HouseArrestClient
|
||||
}
|
||||
|
||||
func (h *houseArrest) Documents(bundleID string) (afc Afc, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = h.client.NewXmlPacket(
|
||||
h.client.NewDocumentsRequest(bundleID),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = h.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = h.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
afcClient := libimobiledevice.NewAfcClient(h.client.InnerConn())
|
||||
afc = newAfc(afcClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (h *houseArrest) Container(bundleID string) (afc Afc, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = h.client.NewXmlPacket(
|
||||
h.client.NewContainerRequest(bundleID),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = h.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = h.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
afcClient := libimobiledevice.NewAfcClient(h.client.InnerConn())
|
||||
afc = newAfc(afcClient)
|
||||
return
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var houseArrestSrv HouseArrest
|
||||
|
||||
func setupHouseArrestSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if houseArrestSrv, err = lockdownSrv.HouseArrestService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_houseArrest_Documents(t *testing.T) {
|
||||
setupHouseArrestSrv(t)
|
||||
|
||||
bundleID = "com.apple.iMovie"
|
||||
appAfc, err := houseArrestSrv.Documents(bundleID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
names, err := appAfc.ReadDir("Documents")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
t.Log(name)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_houseArrest_Container(t *testing.T) {
|
||||
setupHouseArrestSrv(t)
|
||||
|
||||
bundleID = "com.apple.iMovie"
|
||||
appAfc, err := houseArrestSrv.Documents(bundleID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
names, err := appAfc.ReadDir("Documents")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
t.Log(name)
|
||||
}
|
||||
}
|
||||
@@ -1,481 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
type Usbmux interface {
|
||||
Devices() ([]Device, error)
|
||||
ReadBUID() (string, error)
|
||||
Listen(chan Device) (context.CancelFunc, error)
|
||||
}
|
||||
|
||||
type Device interface {
|
||||
Properties() DeviceProperties
|
||||
|
||||
NewConnect(port int, timeout ...time.Duration) (InnerConn, error)
|
||||
ReadPairRecord() (pairRecord *PairRecord, err error)
|
||||
SavePairRecord(pairRecord *PairRecord) (err error)
|
||||
DeletePairRecord() (err error)
|
||||
|
||||
lockdownService() (lockdown Lockdown, err error)
|
||||
QueryType() (LockdownType, error)
|
||||
GetValue(domain, key string) (v interface{}, err error)
|
||||
Pair() (pairRecord *PairRecord, err error)
|
||||
|
||||
imageMounterService() (imageMounter ImageMounter, err error)
|
||||
Images(imgType ...string) (imageSignatures [][]byte, err error)
|
||||
MountDeveloperDiskImage(dmgPath string, signaturePath string) (err error)
|
||||
|
||||
screenshotService() (lockdown Screenshot, err error)
|
||||
Screenshot() (raw *bytes.Buffer, err error)
|
||||
|
||||
simulateLocationService() (simulateLocation SimulateLocation, err error)
|
||||
SimulateLocationUpdate(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error)
|
||||
SimulateLocationRecover() (err error)
|
||||
|
||||
installationProxyService() (installationProxy InstallationProxy, err error)
|
||||
InstallationProxyBrowse(opts ...InstallationProxyOption) (currentList []interface{}, err error)
|
||||
InstallationProxyLookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error)
|
||||
|
||||
instrumentsService() (instruments Instruments, err error)
|
||||
AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error)
|
||||
AppKill(pid int) (err error)
|
||||
AppRunningProcesses() (processes []Process, err error)
|
||||
AppList(opts ...AppListOption) (apps []Application, err error)
|
||||
DeviceInfo() (devInfo *DeviceInfo, err error)
|
||||
|
||||
AfcService() (afc Afc, err error)
|
||||
AppInstall(ipaPath string) (err error)
|
||||
AppUninstall(bundleID string) (err error)
|
||||
|
||||
HouseArrestService() (houseArrest HouseArrest, err error)
|
||||
|
||||
syslogRelayService() (syslogRelay SyslogRelay, err error)
|
||||
Syslog() (lines <-chan string, err error)
|
||||
SyslogStop()
|
||||
|
||||
PcapStart(opts ...PcapOption) (packet <-chan []byte, err error)
|
||||
PcapStop()
|
||||
|
||||
Reboot() error
|
||||
Shutdown() error
|
||||
|
||||
crashReportMoverService() (crashReportMover CrashReportMover, err error)
|
||||
MoveCrashReport(hostDir string, opts ...CrashReportMoverOption) (err error)
|
||||
|
||||
XCTest(bundleID string, opts ...XCTestOption) (out <-chan string, cancel context.CancelFunc, err error)
|
||||
|
||||
springBoardService() (springBoard SpringBoard, err error)
|
||||
GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error)
|
||||
GetInterfaceOrientation() (orientation OrientationState, err error)
|
||||
|
||||
PerfStart(opts ...PerfOption) (data <-chan []byte, err error)
|
||||
PerfStop()
|
||||
}
|
||||
|
||||
type DeviceProperties = libimobiledevice.DeviceProperties
|
||||
|
||||
type OrientationState = libimobiledevice.OrientationState
|
||||
|
||||
type Lockdown interface {
|
||||
QueryType() (LockdownType, error)
|
||||
GetValue(domain, key string) (v interface{}, err error)
|
||||
SetValue(domain, key string, value interface{}) (err error)
|
||||
Pair() (pairRecord *PairRecord, err error)
|
||||
EnterRecovery() (err error)
|
||||
|
||||
handshake() (err error)
|
||||
|
||||
startSession(pairRecord *PairRecord) (err error)
|
||||
stopSession() (err error)
|
||||
startService(service string, escrowBag []byte) (dynamicPort int, enableSSL bool, err error)
|
||||
|
||||
ImageMounterService() (imageMounter ImageMounter, err error)
|
||||
ScreenshotService() (screenshot Screenshot, err error)
|
||||
SimulateLocationService() (simulateLocation SimulateLocation, err error)
|
||||
InstallationProxyService() (installationProxy InstallationProxy, err error)
|
||||
InstrumentsService() (instruments Instruments, err error)
|
||||
TestmanagerdService() (testmanagerd Testmanagerd, err error)
|
||||
AfcService() (afc Afc, err error)
|
||||
HouseArrestService() (houseArrest HouseArrest, err error)
|
||||
SyslogRelayService() (syslogRelay SyslogRelay, err error)
|
||||
DiagnosticsRelayService() (diagnostics DiagnosticsRelay, err error)
|
||||
CrashReportMoverService() (crashReportMover CrashReportMover, err error)
|
||||
SpringBoardService() (springBoard SpringBoard, err error)
|
||||
}
|
||||
|
||||
type ImageMounter interface {
|
||||
Images(imgType string) (imageSignatures [][]byte, err error)
|
||||
UploadImage(imgType, dmgPath string, signatureData []byte) (err error)
|
||||
Mount(imgType, devImgPath string, signatureData []byte) (err error)
|
||||
|
||||
UploadImageAndMount(imgType, devImgPath, dmgPath, signaturePath string) (err error)
|
||||
}
|
||||
|
||||
type Screenshot interface {
|
||||
exchange() (err error)
|
||||
Take() (raw *bytes.Buffer, err error)
|
||||
}
|
||||
|
||||
type SimulateLocation interface {
|
||||
Update(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error)
|
||||
// Recover try to revert back
|
||||
Recover() (err error)
|
||||
}
|
||||
|
||||
type InstallationProxy interface {
|
||||
Browse(opts ...InstallationProxyOption) (currentList []interface{}, err error)
|
||||
Lookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error)
|
||||
Install(bundleID, packagePath string) (err error)
|
||||
Uninstall(bundleID string) (err error)
|
||||
}
|
||||
|
||||
type Instruments interface {
|
||||
AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error)
|
||||
AppKill(pid int) (err error)
|
||||
AppRunningProcesses() (processes []Process, err error)
|
||||
AppList(opts ...AppListOption) (apps []Application, err error)
|
||||
DeviceInfo() (devInfo *DeviceInfo, err error)
|
||||
|
||||
getPidByBundleID(bundleID string) (pid int, err error)
|
||||
appProcess(bundleID string) (err error)
|
||||
startObserving(pid int) (err error)
|
||||
|
||||
notifyOfPublishedCapabilities() (err error)
|
||||
requestChannel(channel string) (id uint32, err error)
|
||||
call(channel, selector string, auxiliaries ...interface{}) (result *libimobiledevice.DTXMessageResult, err error)
|
||||
|
||||
// sysMonSetConfig(cfg ...interface{}) (err error)
|
||||
// SysMonStart(cfg ...interface{}) (_ interface{}, err error)
|
||||
|
||||
registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult))
|
||||
}
|
||||
|
||||
type Testmanagerd interface {
|
||||
notifyOfPublishedCapabilities() (err error)
|
||||
requestChannel(channel string) (id uint32, err error)
|
||||
newXCTestManagerDaemon() (xcTestManager XCTestManagerDaemon, err error)
|
||||
|
||||
invoke(selector string, args *libimobiledevice.AuxBuffer, channel uint32, expectsReply bool) (*libimobiledevice.DTXMessageResult, error)
|
||||
|
||||
registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult))
|
||||
close()
|
||||
}
|
||||
|
||||
type Afc interface {
|
||||
DiskInfo() (diskInfo *AfcDiskInfo, err error)
|
||||
ReadDir(dirname string) (names []string, err error)
|
||||
Stat(filename string) (info *AfcFileInfo, err error)
|
||||
Open(filename string, mode AfcFileMode) (file *AfcFile, err error)
|
||||
Remove(filePath string) (err error)
|
||||
Rename(oldPath string, newPath string) (err error)
|
||||
Mkdir(path string) (err error)
|
||||
Link(oldName string, newName string, linkType AfcLinkType) (err error)
|
||||
Truncate(filePath string, size int64) (err error)
|
||||
SetFileModTime(filePath string, modTime time.Time) (err error)
|
||||
// Hash sha1 algorithm
|
||||
Hash(filePath string) ([]byte, error)
|
||||
// HashWithRange sha1 algorithm with file range
|
||||
HashWithRange(filePath string, start, end uint64) ([]byte, error)
|
||||
RemoveAll(path string) (err error)
|
||||
|
||||
WriteFile(filename string, data []byte, perm AfcFileMode) (err error)
|
||||
}
|
||||
|
||||
type HouseArrest interface {
|
||||
Documents(bundleID string) (afc Afc, err error)
|
||||
Container(bundleID string) (afc Afc, err error)
|
||||
}
|
||||
|
||||
type XCTestManagerDaemon interface {
|
||||
// initiateControlSession iOS 11+
|
||||
initiateControlSession(XcodeVersion uint64) (err error)
|
||||
startExecutingTestPlan(XcodeVersion uint64) (err error)
|
||||
initiateSession(XcodeVersion uint64, nsUUID *nskeyedarchiver.NSUUID) (err error)
|
||||
// authorizeTestSession iOS 12+
|
||||
authorizeTestSession(pid int) (err error)
|
||||
// initiateControlSessionForTestProcessID <= iOS 9
|
||||
initiateControlSessionForTestProcessID(pid int) (err error)
|
||||
// initiateControlSessionForTestProcessIDProtocolVersion iOS > 9 && iOS < 12
|
||||
initiateControlSessionForTestProcessIDProtocolVersion(pid int, XcodeVersion uint64) (err error)
|
||||
|
||||
registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult))
|
||||
close()
|
||||
}
|
||||
|
||||
type SyslogRelay interface {
|
||||
Lines() <-chan string
|
||||
Stop()
|
||||
}
|
||||
|
||||
type Pcapd interface {
|
||||
Packet() <-chan []byte
|
||||
Stop()
|
||||
}
|
||||
|
||||
type Perfd interface {
|
||||
Start() (data <-chan []byte, err error)
|
||||
Stop()
|
||||
}
|
||||
|
||||
type DiagnosticsRelay interface {
|
||||
Reboot() error
|
||||
Shutdown() error
|
||||
}
|
||||
|
||||
type CrashReportMover interface {
|
||||
Move(hostDir string, opts ...CrashReportMoverOption) (err error)
|
||||
walkDir(dirname string, fn func(path string, info *AfcFileInfo)) (err error)
|
||||
}
|
||||
|
||||
type SpringBoard interface {
|
||||
GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error)
|
||||
GetInterfaceOrientation() (orientation OrientationState, err error)
|
||||
}
|
||||
|
||||
type InnerConn = libimobiledevice.InnerConn
|
||||
|
||||
type LockdownType = libimobiledevice.LockdownType
|
||||
|
||||
type PairRecord = libimobiledevice.PairRecord
|
||||
|
||||
type CoordinateSystem = libimobiledevice.CoordinateSystem
|
||||
|
||||
const (
|
||||
CoordinateSystemWGS84 = libimobiledevice.CoordinateSystemWGS84
|
||||
CoordinateSystemBD09 = libimobiledevice.CoordinateSystemBD09
|
||||
CoordinateSystemGCJ02 = libimobiledevice.CoordinateSystemGCJ02
|
||||
)
|
||||
|
||||
type ApplicationType = libimobiledevice.ApplicationType
|
||||
|
||||
const (
|
||||
ApplicationTypeSystem = libimobiledevice.ApplicationTypeSystem
|
||||
ApplicationTypeUser = libimobiledevice.ApplicationTypeUser
|
||||
ApplicationTypeInternal = libimobiledevice.ApplicationTypeInternal
|
||||
ApplicationTypeAny = libimobiledevice.ApplicationTypeAny
|
||||
)
|
||||
|
||||
type installationProxyOption = libimobiledevice.InstallationProxyOption
|
||||
|
||||
type InstallationProxyOption func(*installationProxyOption)
|
||||
|
||||
func WithApplicationType(appType ApplicationType) InstallationProxyOption {
|
||||
return func(opt *installationProxyOption) {
|
||||
opt.ApplicationType = appType
|
||||
}
|
||||
}
|
||||
|
||||
func WithReturnAttributes(attrs ...string) InstallationProxyOption {
|
||||
return func(opt *installationProxyOption) {
|
||||
if len(opt.ReturnAttributes) == 0 {
|
||||
opt.ReturnAttributes = attrs
|
||||
} else {
|
||||
opt.ReturnAttributes = append(opt.ReturnAttributes, attrs...)
|
||||
}
|
||||
opt.ReturnAttributes = _removeDuplicate(opt.ReturnAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
func WithBundleIDs(BundleIDs ...string) InstallationProxyOption {
|
||||
return func(opt *installationProxyOption) {
|
||||
if len(opt.BundleIDs) == 0 {
|
||||
opt.BundleIDs = BundleIDs
|
||||
} else {
|
||||
opt.BundleIDs = append(opt.BundleIDs, BundleIDs...)
|
||||
}
|
||||
opt.BundleIDs = _removeDuplicate(opt.BundleIDs)
|
||||
}
|
||||
}
|
||||
|
||||
func WithMetaData(b bool) InstallationProxyOption {
|
||||
return func(opt *installationProxyOption) {
|
||||
opt.MetaData = b
|
||||
}
|
||||
}
|
||||
|
||||
type appLaunchOption struct {
|
||||
appPath string
|
||||
environment map[string]interface{}
|
||||
arguments []interface{}
|
||||
options map[string]interface{}
|
||||
}
|
||||
|
||||
type AppLaunchOption func(option *appLaunchOption)
|
||||
|
||||
func WithAppPath(appPath string) AppLaunchOption {
|
||||
return func(opt *appLaunchOption) {
|
||||
opt.appPath = appPath
|
||||
}
|
||||
}
|
||||
|
||||
func WithEnvironment(environment map[string]interface{}) AppLaunchOption {
|
||||
return func(opt *appLaunchOption) {
|
||||
opt.environment = environment
|
||||
}
|
||||
}
|
||||
|
||||
func WithArguments(arguments []interface{}) AppLaunchOption {
|
||||
return func(opt *appLaunchOption) {
|
||||
opt.arguments = arguments
|
||||
}
|
||||
}
|
||||
|
||||
func WithOptions(options map[string]interface{}) AppLaunchOption {
|
||||
return func(opt *appLaunchOption) {
|
||||
for k, v := range options {
|
||||
opt.options[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithKillExisting(b bool) AppLaunchOption {
|
||||
return func(opt *appLaunchOption) {
|
||||
v := uint64(0)
|
||||
if b {
|
||||
v = uint64(1)
|
||||
}
|
||||
opt.options["KillExisting"] = v
|
||||
}
|
||||
}
|
||||
|
||||
type appListOption struct {
|
||||
appsMatching map[string]interface{}
|
||||
updateToken string
|
||||
}
|
||||
|
||||
type AppListOption func(option *appListOption)
|
||||
|
||||
func WithAppsMatching(appsMatching map[string]interface{}) AppListOption {
|
||||
return func(opt *appListOption) {
|
||||
opt.appsMatching = appsMatching
|
||||
}
|
||||
}
|
||||
|
||||
func WithUpdateToken(updateToken string) AppListOption {
|
||||
return func(opt *appListOption) {
|
||||
opt.updateToken = updateToken
|
||||
}
|
||||
}
|
||||
|
||||
type Process struct {
|
||||
IsApplication bool `json:"isApplication"`
|
||||
Name string `json:"name"`
|
||||
Pid int `json:"pid"`
|
||||
RealAppName string `json:"realAppName"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
}
|
||||
|
||||
type crashReportMoverOption struct {
|
||||
whenDone func(filename string)
|
||||
keep bool
|
||||
extract bool
|
||||
}
|
||||
|
||||
func defaultCrashReportMoverOption() *crashReportMoverOption {
|
||||
return &crashReportMoverOption{
|
||||
whenDone: func(filename string) {},
|
||||
keep: false,
|
||||
}
|
||||
}
|
||||
|
||||
type CrashReportMoverOption func(opt *crashReportMoverOption)
|
||||
|
||||
func WithKeepCrashReport(b bool) CrashReportMoverOption {
|
||||
return func(opt *crashReportMoverOption) {
|
||||
opt.keep = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithExtractRawCrashReport(b bool) CrashReportMoverOption {
|
||||
return func(opt *crashReportMoverOption) {
|
||||
opt.extract = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithWhenMoveIsDone(whenDone func(filename string)) CrashReportMoverOption {
|
||||
return func(opt *crashReportMoverOption) {
|
||||
opt.whenDone = whenDone
|
||||
}
|
||||
}
|
||||
|
||||
type xcTestOption struct {
|
||||
appEnv map[string]interface{}
|
||||
appArgs []interface{}
|
||||
appOpt map[string]interface{}
|
||||
}
|
||||
|
||||
func defaultXCTestOption() *xcTestOption {
|
||||
return &xcTestOption{
|
||||
appEnv: make(map[string]interface{}),
|
||||
appArgs: make([]interface{}, 0, 2),
|
||||
appOpt: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
type XCTestOption func(opt *xcTestOption)
|
||||
|
||||
func WithXCTestEnv(env map[string]interface{}) XCTestOption {
|
||||
return func(opt *xcTestOption) {
|
||||
opt.appEnv = env
|
||||
}
|
||||
}
|
||||
|
||||
// func WithXCTestArgs(args []interface{}) XCTestOption {
|
||||
// return func(opt *xcTestOption) {
|
||||
// opt.appArgs = args
|
||||
// }
|
||||
// }
|
||||
|
||||
func WithXCTestOpt(appOpt map[string]interface{}) XCTestOption {
|
||||
return func(opt *xcTestOption) {
|
||||
opt.appOpt = appOpt
|
||||
}
|
||||
}
|
||||
|
||||
func _removeDuplicate(strSlice []string) []string {
|
||||
existed := make(map[string]bool, len(strSlice))
|
||||
noRepeat := make([]string, 0, len(strSlice))
|
||||
for _, str := range strSlice {
|
||||
if _, ok := existed[str]; ok {
|
||||
continue
|
||||
}
|
||||
existed[str] = true
|
||||
noRepeat = append(noRepeat, str)
|
||||
}
|
||||
return noRepeat
|
||||
}
|
||||
|
||||
func DeviceVersion(version ...int) int {
|
||||
if len(version) < 3 {
|
||||
tmp := make([]int, 3)
|
||||
copy(tmp, version)
|
||||
version = tmp
|
||||
}
|
||||
maj, min, patch := version[0], version[1], version[2]
|
||||
return ((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF)
|
||||
}
|
||||
|
||||
var debugFlag = false
|
||||
|
||||
// SetDebug sets debug mode
|
||||
func SetDebug(debug bool, libDebug ...bool) {
|
||||
debugFlag = debug
|
||||
if len(libDebug) >= 1 {
|
||||
libimobiledevice.SetDebug(libDebug[0])
|
||||
}
|
||||
}
|
||||
|
||||
func debugLog(msg string) {
|
||||
if !debugFlag {
|
||||
return
|
||||
}
|
||||
fmt.Printf("[go-iDevice-debug] %s\n", msg)
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ ImageMounter = (*imageMounter)(nil)
|
||||
|
||||
func newImageMounter(client *libimobiledevice.ImageMounterClient) *imageMounter {
|
||||
return &imageMounter{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type imageMounter struct {
|
||||
client *libimobiledevice.ImageMounterClient
|
||||
}
|
||||
|
||||
func (m *imageMounter) Images(imgType string) (imageSignatures [][]byte, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = m.client.NewXmlPacket(
|
||||
m.client.NewBasicRequest(libimobiledevice.CommandTypeLookupImage, imgType),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = m.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = m.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.ImageMounterLookupImageResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imageSignatures = reply.ImageSignature
|
||||
return
|
||||
}
|
||||
|
||||
func (m *imageMounter) UploadImage(imgType, dmgPath string, signatureData []byte) (err error) {
|
||||
var dmgFileInfo os.FileInfo
|
||||
if dmgFileInfo, err = os.Stat(dmgPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = m.client.NewXmlPacket(
|
||||
m.client.NewReceiveBytesRequest(imgType, uint32(dmgFileInfo.Size()), signatureData),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = m.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = m.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.ImageMounterBasicResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reply.Status != "ReceiveBytesAck" {
|
||||
return fmt.Errorf("image mounter 'ReceiveBytes' status: %s", reply.Status)
|
||||
}
|
||||
|
||||
var dmgData []byte
|
||||
if dmgData, err = os.ReadFile(dmgPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = m.client.SendDmg(dmgData); err != nil {
|
||||
return err
|
||||
}
|
||||
if respPkt, err = m.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reply.Status != "Complete" {
|
||||
return fmt.Errorf("image mounter 'SendDmg' status: %s", reply.Status)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *imageMounter) Mount(imgType, devImgPath string, signatureData []byte) (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = m.client.NewXmlPacket(
|
||||
m.client.NewMountImageRequest(imgType, devImgPath, signatureData),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = m.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = m.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.ImageMounterBasicResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reply.Status != "Complete" {
|
||||
return fmt.Errorf("image mounter 'MountImage' status: %s", reply.Status)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *imageMounter) UploadImageAndMount(imgType, devImgPath, dmgPath, signaturePath string) (err error) {
|
||||
var signatureData []byte
|
||||
if signatureData, err = os.ReadFile(signaturePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.UploadImage(imgType, dmgPath, signatureData); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.Mount(imgType, devImgPath, signatureData); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var imageMounterSrv ImageMounter
|
||||
|
||||
func setupImageMounterSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Once
|
||||
// dev.Images()
|
||||
if imageMounterSrv, err = lockdownSrv.ImageMounterService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_imageMounter_Images(t *testing.T) {
|
||||
setupImageMounterSrv(t)
|
||||
|
||||
// imageSignatures, err := dev.Images()
|
||||
imageSignatures, err := imageMounterSrv.Images("Developer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, imgSign := range imageSignatures {
|
||||
t.Logf("%2d, %s", i+1, base64.StdEncoding.EncodeToString(imgSign))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_imageMounter_UploadImageAndMount(t *testing.T) {
|
||||
setupImageMounterSrv(t)
|
||||
|
||||
devImgPath := "/private/var/mobile/Media/PublicStaging/staging.dimage"
|
||||
dmgPath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg"
|
||||
signaturePath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg.signature"
|
||||
|
||||
if err := imageMounterSrv.UploadImageAndMount("Developer", devImgPath, dmgPath, signaturePath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ InstallationProxy = (*installationProxy)(nil)
|
||||
|
||||
func newInstallationProxy(client *libimobiledevice.InstallationProxyClient) *installationProxy {
|
||||
return &installationProxy{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type installationProxy struct {
|
||||
client *libimobiledevice.InstallationProxyClient
|
||||
}
|
||||
|
||||
func (p *installationProxy) Browse(opts ...InstallationProxyOption) (currentList []interface{}, err error) {
|
||||
opt := new(installationProxyOption)
|
||||
if len(opts) == 0 {
|
||||
opt = nil
|
||||
} else {
|
||||
for _, optFunc := range opts {
|
||||
optFunc(opt)
|
||||
}
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = p.client.NewXmlPacket(
|
||||
p.client.NewBasicRequest(libimobiledevice.CommandTypeBrowse, opt),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = p.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = p.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.InstallationProxyBrowseResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for reply.Status != "Complete" {
|
||||
if respPkt, err = p.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
currentList = reply.CurrentList
|
||||
return
|
||||
}
|
||||
|
||||
func (p *installationProxy) Lookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error) {
|
||||
opt := new(installationProxyOption)
|
||||
if len(opts) == 0 {
|
||||
opt = nil
|
||||
} else {
|
||||
for _, optFunc := range opts {
|
||||
optFunc(opt)
|
||||
}
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = p.client.NewXmlPacket(
|
||||
p.client.NewBasicRequest(libimobiledevice.CommandTypeLookup, opt),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = p.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = p.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.InstallationProxyLookupResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Status != "Complete" {
|
||||
return nil, fmt.Errorf("installation proxy 'Lookup' status: %s", reply.Status)
|
||||
}
|
||||
|
||||
lookupResult = reply.LookupResult
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *installationProxy) Install(bundleID, packagePath string) (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = p.client.NewXmlPacket(
|
||||
p.client.NewInstallRequest(bundleID, packagePath),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = p.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.InstallationProxyInstallResponse
|
||||
for len(reply.Error) == 0 {
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = p.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
if reply.Status == "Complete" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(reply.Error) != 0 {
|
||||
return fmt.Errorf("installation proxy 'Install' status: %s (err: %s, desc: %s)", reply.Status, reply.Error, reply.ErrorDescription)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *installationProxy) Uninstall(bundleID string) (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = p.client.NewXmlPacket(
|
||||
p.client.NewUninstallRequest(bundleID),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = p.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.InstallationProxyInstallResponse
|
||||
for len(reply.Error) == 0 {
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = p.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
if reply.Status == "Complete" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(reply.Error) != 0 {
|
||||
return fmt.Errorf("installation proxy 'Uninstall' status: %s (err: %s, desc: %s)", reply.Status, reply.Error, reply.ErrorDescription)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var installationProxySrv InstallationProxy
|
||||
|
||||
func setupInstallationProxySrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if installationProxySrv, err = lockdownSrv.InstallationProxyService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_installationProxy_Browse(t *testing.T) {
|
||||
setupInstallationProxySrv(t)
|
||||
|
||||
// currentList, err := installationProxySrv.Browse(WithMetaData(true))
|
||||
// currentList, err := installationProxySrv.Browse(WithReturnAttributes("CFBundleIdentifier", "SequenceNumber", "SequenceNumber"))
|
||||
// currentList, err := installationProxySrv.Browse(WithApplicationType(ApplicationTypeSystem))
|
||||
// currentList, err := installationProxySrv.Browse(WithApplicationType(ApplicationTypeSystem), WithReturnAttributes("ApplicationType", "ApplicationType"))
|
||||
// currentList, err := dev.InstallationProxyBrowse()
|
||||
currentList, err := installationProxySrv.Browse()
|
||||
// currentList, err := installationProxySrv.Browse(WithBundleIDs("com.apple.MusicUIService"), WithBundleIDs("com.apple.Home.HomeControlService"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(len(currentList))
|
||||
|
||||
for _, cl := range currentList {
|
||||
app, ok := cl.(map[string]interface{})
|
||||
if ok {
|
||||
t.Log(app)
|
||||
} else {
|
||||
t.Log(cl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_installationProxy_Lookup(t *testing.T) {
|
||||
setupInstallationProxySrv(t)
|
||||
|
||||
// lookupResult, err := installationProxySrv.Lookup()
|
||||
// lookupResult, err := dev.InstallationProxyLookup(
|
||||
lookupResult, err := installationProxySrv.Lookup(
|
||||
// WithApplicationType(ApplicationTypeUser),
|
||||
// WithApplicationType(ApplicationTypeSystem),
|
||||
// WithReturnAttributes("CFBundleDevelopmentRegion"),
|
||||
// WithReturnAttributes("CFBundleDisplayName", "CFBundleIdentifier"),
|
||||
// WithBundleIDs("com.apple.mobilephone"),
|
||||
WithBundleIDs("com.leixipaopao.WebDriverAgentRunner.xctrunner"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ret := lookupResult.(map[string]interface{})
|
||||
t.Log(len(ret))
|
||||
|
||||
for k, v := range ret {
|
||||
t.Log(k, "-->", v)
|
||||
}
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
// instruments services
|
||||
const (
|
||||
instrumentsServiceDeviceInfo = "com.apple.instruments.server.services.deviceinfo"
|
||||
instrumentsServiceProcessControl = "com.apple.instruments.server.services.processcontrol"
|
||||
instrumentsServiceDeviceApplictionListing = "com.apple.instruments.server.services.device.applictionListing"
|
||||
instrumentsServiceGraphicsOpengl = "com.apple.instruments.server.services.graphics.opengl" // 获取 GPU/FPS
|
||||
instrumentsServiceSysmontap = "com.apple.instruments.server.services.sysmontap" // 获取 CPU/Mem/Disk/Network 性能数据
|
||||
instrumentsServiceNetworking = "com.apple.instruments.server.services.networking" // 获取所有网络详情数据
|
||||
instrumentsServiceMobileNotifications = "com.apple.instruments.server.services.mobilenotifications" // 监控应用状态
|
||||
)
|
||||
|
||||
const (
|
||||
instrumentsServiceXcodeNetworkStatistics = "com.apple.xcode.debug-gauge-data-providers.NetworkStatistics" // 获取单进程网络数据
|
||||
instrumentsServiceXcodeEnergyStatistics = "com.apple.xcode.debug-gauge-data-providers.Energy" // 获取功耗数据
|
||||
)
|
||||
|
||||
var _ Instruments = (*instruments)(nil)
|
||||
|
||||
func newInstruments(client *libimobiledevice.InstrumentsClient) *instruments {
|
||||
return &instruments{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type instruments struct {
|
||||
client *libimobiledevice.InstrumentsClient
|
||||
}
|
||||
|
||||
func (i *instruments) notifyOfPublishedCapabilities() (err error) {
|
||||
_, err = i.client.NotifyOfPublishedCapabilities()
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) requestChannel(channel string) (id uint32, err error) {
|
||||
return i.client.RequestChannel(channel)
|
||||
}
|
||||
|
||||
func (i *instruments) call(channel, selector string, auxiliaries ...interface{}) (
|
||||
result *libimobiledevice.DTXMessageResult, err error) {
|
||||
|
||||
chanID, err := i.requestChannel(channel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
for _, aux := range auxiliaries {
|
||||
if err = args.AppendObject(aux); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return i.client.Invoke(selector, args, chanID, true)
|
||||
}
|
||||
|
||||
func (i *instruments) getPidByBundleID(bundleID string) (pid int, err error) {
|
||||
apps, err := i.AppList()
|
||||
if err != nil {
|
||||
fmt.Printf("get app list error: %v\n", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
mapper := make(map[string]interface{})
|
||||
for _, app := range apps {
|
||||
mapper[app.ExecutableName] = app.CFBundleIdentifier
|
||||
}
|
||||
|
||||
processes, err := i.AppRunningProcesses()
|
||||
if err != nil {
|
||||
fmt.Printf("get running app processes error: %v\n", err)
|
||||
return 0, err
|
||||
}
|
||||
for _, proc := range processes {
|
||||
b, ok := mapper[proc.Name]
|
||||
if ok && bundleID == b {
|
||||
fmt.Printf("get pid %d by bundleId %s\n", proc.Pid, bundleID)
|
||||
return proc.Pid, nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("can't find pid by bundleID: %s\n", bundleID)
|
||||
return 0, fmt.Errorf("can't find pid by bundleID: %s", bundleID)
|
||||
}
|
||||
|
||||
func (i *instruments) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error) {
|
||||
opt := new(appLaunchOption)
|
||||
opt.appPath = ""
|
||||
opt.options = map[string]interface{}{
|
||||
"StartSuspendedKey": uint64(0),
|
||||
"KillExisting": uint64(0),
|
||||
}
|
||||
if len(opts) != 0 {
|
||||
for _, optFunc := range opts {
|
||||
optFunc(opt)
|
||||
}
|
||||
}
|
||||
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(opt.appPath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = args.AppendObject(bundleID); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = args.AppendObject(opt.environment); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = args.AppendObject(opt.arguments); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = args.AppendObject(opt.options); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var result *libimobiledevice.DTXMessageResult
|
||||
selector := "launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:"
|
||||
if result, err = i.client.Invoke(selector, args, id, true); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nsErr, ok := result.Obj.(libimobiledevice.NSError); ok {
|
||||
return 0, fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
|
||||
return int(result.Obj.(uint64)), nil
|
||||
}
|
||||
|
||||
func (i *instruments) appProcess(bundleID string) (err error) {
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(bundleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "processIdentifierForBundleIdentifier:"
|
||||
if _, err = i.client.Invoke(selector, args, id, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) startObserving(pid int) (err error) {
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var result *libimobiledevice.DTXMessageResult
|
||||
selector := "startObservingPid:"
|
||||
if result, err = i.client.Invoke(selector, args, id, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nsErr, ok := result.Obj.(libimobiledevice.NSError); ok {
|
||||
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) AppKill(pid int) (err error) {
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "killPid:"
|
||||
if _, err = i.client.Invoke(selector, args, id, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) AppRunningProcesses() (processes []Process, err error) {
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceDeviceInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selector := "runningProcesses"
|
||||
|
||||
var result *libimobiledevice.DTXMessageResult
|
||||
if result, err = i.client.Invoke(selector, libimobiledevice.NewAuxBuffer(), id, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objs := result.Obj.([]interface{})
|
||||
|
||||
processes = make([]Process, 0, len(objs))
|
||||
|
||||
for _, v := range objs {
|
||||
m := v.(map[string]interface{})
|
||||
|
||||
var data []byte
|
||||
if data, err = json.Marshal(m); err != nil {
|
||||
debugLog(fmt.Sprintf("process marshal: %v\n%v\n", err, m))
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
|
||||
var tp Process
|
||||
if err = json.Unmarshal(data, &tp); err != nil {
|
||||
debugLog(fmt.Sprintf("process unmarshal: %v\n%v\n", err, m))
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
|
||||
processes = append(processes, tp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) AppList(opts ...AppListOption) (apps []Application, err error) {
|
||||
opt := new(appListOption)
|
||||
opt.updateToken = ""
|
||||
opt.appsMatching = make(map[string]interface{})
|
||||
if len(opts) != 0 {
|
||||
for _, optFunc := range opts {
|
||||
optFunc(opt)
|
||||
}
|
||||
}
|
||||
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceDeviceApplictionListing); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(opt.appsMatching); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = args.AppendObject(opt.updateToken); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selector := "installedApplicationsMatching:registerUpdateToken:"
|
||||
|
||||
var result *libimobiledevice.DTXMessageResult
|
||||
if result, err = i.client.Invoke(selector, args, id, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objs := result.Obj.([]interface{})
|
||||
|
||||
for _, v := range objs {
|
||||
m := v.(map[string]interface{})
|
||||
|
||||
var data []byte
|
||||
if data, err = json.Marshal(m); err != nil {
|
||||
debugLog(fmt.Sprintf("application marshal: %v\n%v\n", err, m))
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
|
||||
var app Application
|
||||
if err = json.Unmarshal(data, &app); err != nil {
|
||||
debugLog(fmt.Sprintf("application unmarshal: %v\n%v\n", err, m))
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
apps = append(apps, app)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) DeviceInfo() (devInfo *DeviceInfo, err error) {
|
||||
var id uint32
|
||||
if id, err = i.requestChannel(instrumentsServiceDeviceInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selector := "systemInformation"
|
||||
|
||||
var result *libimobiledevice.DTXMessageResult
|
||||
if result, err = i.client.Invoke(selector, libimobiledevice.NewAuxBuffer(), id, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := json.Marshal(result.Obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devInfo = new(DeviceInfo)
|
||||
err = json.Unmarshal(data, devInfo)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instruments) registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult)) {
|
||||
i.client.RegisterCallback(obj, cb)
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
AppExtensionUUIDs []string `json:"AppExtensionUUIDs,omitempty"`
|
||||
BundlePath string `json:"BundlePath"`
|
||||
CFBundleIdentifier string `json:"CFBundleIdentifier"`
|
||||
ContainerBundleIdentifier string `json:"ContainerBundleIdentifier,omitempty"`
|
||||
ContainerBundlePath string `json:"ContainerBundlePath,omitempty"`
|
||||
DisplayName string `json:"DisplayName"`
|
||||
ExecutableName string `json:"ExecutableName,omitempty"`
|
||||
Placeholder bool `json:"Placeholder,omitempty"`
|
||||
PluginIdentifier string `json:"PluginIdentifier,omitempty"`
|
||||
PluginUUID string `json:"PluginUUID,omitempty"`
|
||||
Restricted int `json:"Restricted"`
|
||||
Type string `json:"Type"`
|
||||
Version string `json:"Version"`
|
||||
}
|
||||
|
||||
type DeviceInfo struct {
|
||||
Description string `json:"_deviceDescription"`
|
||||
DisplayName string `json:"_deviceDisplayName"`
|
||||
Identifier string `json:"_deviceIdentifier"`
|
||||
Version string `json:"_deviceVersion"`
|
||||
ProductType string `json:"_productType"`
|
||||
ProductVersion string `json:"_productVersion"`
|
||||
XRDeviceClassName string `json:"_xrdeviceClassName"`
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
instrumentsSrv Instruments
|
||||
bundleID = "com.apple.Preferences"
|
||||
)
|
||||
|
||||
func setupInstrumentsSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if instrumentsSrv, err = lockdownSrv.InstrumentsService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_instruments_AppLaunch(t *testing.T) {
|
||||
setupInstrumentsSrv(t)
|
||||
|
||||
// bundleID = "com.leixipaopao.WebDriverAgentRunner.xctrunner"
|
||||
|
||||
// pid, err := dev.AppLaunch(bundleID)
|
||||
pid, err := instrumentsSrv.AppLaunch(bundleID)
|
||||
// pid, err := instrumentsSrv.AppLaunch(bundleID, WithKillExisting(true))
|
||||
// pid, err := instrumentsSrv.AppLaunch(bundleID, WithKillExisting(true), WithArguments([]interface{}{"-AppleLanguages", "(Russian)"}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(pid)
|
||||
}
|
||||
|
||||
func Test_instruments_AppKill(t *testing.T) {
|
||||
setupInstrumentsSrv(t)
|
||||
|
||||
pid, err := instrumentsSrv.AppLaunch(bundleID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(pid)
|
||||
|
||||
// if err = dev.AppKill(pid); err != nil {
|
||||
if err = instrumentsSrv.AppKill(pid); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_instruments_AppRunningProcesses(t *testing.T) {
|
||||
setupInstrumentsSrv(t)
|
||||
|
||||
// processes, err := dev.AppRunningProcesses()
|
||||
processes, err := instrumentsSrv.AppRunningProcesses()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, p := range processes {
|
||||
t.Log(p.IsApplication, "\t", p.Pid, "\t", p.Name, "\t", p.RealAppName, "\t", p.StartDate)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_instruments_AppList(t *testing.T) {
|
||||
setupInstrumentsSrv(t)
|
||||
|
||||
// apps, err := dev.AppList()
|
||||
apps, err := instrumentsSrv.AppList()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, app := range apps {
|
||||
t.Logf("%v\t%v\t%v\t%v\t%v\n", app.Type, app.DisplayName, app.ExecutableName, app.AppExtensionUUIDs, app.BundlePath)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_instruments_DeviceInfo(t *testing.T) {
|
||||
setupInstrumentsSrv(t)
|
||||
|
||||
devInfo, err := instrumentsSrv.DeviceInfo()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(devInfo.Description)
|
||||
t.Log(devInfo.DisplayName)
|
||||
t.Log(devInfo.Identifier)
|
||||
t.Log(devInfo.Version)
|
||||
t.Log(devInfo.ProductType)
|
||||
t.Log(devInfo.ProductVersion)
|
||||
t.Log(devInfo.XRDeviceClassName)
|
||||
}
|
||||
@@ -1,684 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ Lockdown = (*lockdown)(nil)
|
||||
|
||||
func newLockdown(dev *device) *lockdown {
|
||||
return &lockdown{
|
||||
umClient: dev.umClient,
|
||||
client: dev.lockdownClient,
|
||||
dev: dev,
|
||||
}
|
||||
}
|
||||
|
||||
type lockdown struct {
|
||||
umClient *libimobiledevice.UsbmuxClient
|
||||
client *libimobiledevice.LockdownClient
|
||||
sessionID string
|
||||
|
||||
dev *device
|
||||
iOSVersion []int
|
||||
pairRecord *PairRecord
|
||||
}
|
||||
|
||||
func (c *lockdown) QueryType() (LockdownType, error) {
|
||||
pkt, err := c.client.NewXmlPacket(
|
||||
c.client.NewBasicRequest(libimobiledevice.RequestTypeQueryType),
|
||||
)
|
||||
if err != nil {
|
||||
return LockdownType{}, err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return LockdownType{}, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = c.client.ReceivePacket(); err != nil {
|
||||
return LockdownType{}, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownTypeResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return LockdownType{}, err
|
||||
}
|
||||
|
||||
return LockdownType{Type: reply.Type}, nil
|
||||
}
|
||||
|
||||
func (c *lockdown) GetValue(domain, key string) (v interface{}, err error) {
|
||||
pkt, err := c.client.NewXmlPacket(
|
||||
c.client.NewGetValueRequest(domain, key),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = c.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownValueResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v = reply.Value
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) SetValue(domain, key string, value interface{}) (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = c.client.NewXmlPacket(
|
||||
c.client.NewSetValueRequest(domain, key, value),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = c.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownValueResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !reply.Value.(bool) {
|
||||
return errors.New("lockdown SetValue: Failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) EnterRecovery() (err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = c.client.NewXmlPacket(
|
||||
c.client.NewEnterRecoveryRequest(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = c.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) handshake() (err error) {
|
||||
var lockdownType LockdownType
|
||||
if lockdownType, err = c.QueryType(); err != nil {
|
||||
return err
|
||||
}
|
||||
if lockdownType.Type != "com.apple.mobile.lockdown" {
|
||||
return fmt.Errorf("lockdown handshake 'QueryType': %s", lockdownType.Type)
|
||||
}
|
||||
|
||||
// if (device->version < DEVICE_VERSION(7,0,0))
|
||||
// for older devices, we need to validate pairing to receive trusted host status
|
||||
|
||||
if c.pairRecord, err = c.dev.ReadPairRecord(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), libimobiledevice.ReplyCodeBadDevice.String()) {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.pairRecord, err = c.Pair(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.dev.SavePairRecord(c.pairRecord)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) Pair() (pairRecord *PairRecord, err error) {
|
||||
var buid string
|
||||
if buid, err = newUsbmux(c.umClient).ReadBUID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var devPublicKeyPem []byte
|
||||
var devWiFiAddr string
|
||||
|
||||
if lockdownValue, err := c.GetValue("", "DevicePublicKey"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
devPublicKeyPem = lockdownValue.([]byte)
|
||||
}
|
||||
if lockdownValue, err := c.GetValue("", "WiFiAddress"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
devWiFiAddr = lockdownValue.(string)
|
||||
}
|
||||
|
||||
if pairRecord, err = generatePairRecord(devPublicKeyPem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pairRecord.SystemBUID = buid
|
||||
pairRecord.HostID = strings.ToUpper(uuid.NewV4().String())
|
||||
hostPrivateKey := pairRecord.HostPrivateKey
|
||||
pairRecord.HostPrivateKey = nil
|
||||
rootPrivateKey := pairRecord.RootPrivateKey
|
||||
pairRecord.RootPrivateKey = nil
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = c.client.NewXmlPacket(
|
||||
c.client.NewPairRequest(pairRecord),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = c.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownPairResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pairRecord.EscrowBag = reply.EscrowBag
|
||||
pairRecord.WiFiMACAddress = devWiFiAddr
|
||||
pairRecord.HostPrivateKey = hostPrivateKey
|
||||
pairRecord.RootPrivateKey = rootPrivateKey
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) startSession(pairRecord *PairRecord) (err error) {
|
||||
// if we have a running session, stop current one first
|
||||
if c.sessionID != "" {
|
||||
if err = c.stopSession(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = c.client.NewXmlPacket(
|
||||
c.client.NewStartSessionRequest(pairRecord.SystemBUID, pairRecord.HostID),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = c.client.ReceivePacket(); err != nil {
|
||||
return fmt.Errorf("lockdown start session: %w", err)
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownStartSessionResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reply.EnableSessionSSL {
|
||||
if err = c.client.EnableSSL(c.iOSVersion, pairRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.sessionID = reply.SessionID
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) stopSession() (err error) {
|
||||
if c.sessionID == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = c.client.NewXmlPacket(
|
||||
c.client.NewStopSessionRequest(c.sessionID),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = c.client.ReceivePacket(); err != nil {
|
||||
return fmt.Errorf("lockdown stop session: %w", err)
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownBasicResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.sessionID = ""
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) startService(service string, escrowBag []byte) (dynamicPort int, enableSSL bool, err error) {
|
||||
req := c.client.NewStartServiceRequest(service)
|
||||
if escrowBag != nil {
|
||||
req.EscrowBag = escrowBag
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = c.client.NewXmlPacket(req); err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
if err = c.client.SendPacket(pkt); err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
respPkt, err := c.client.ReceivePacket()
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
var reply libimobiledevice.LockdownStartServiceResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
if reply.Error != "" {
|
||||
return 0, false, fmt.Errorf("lockdown start service: %s", reply.Error)
|
||||
}
|
||||
|
||||
dynamicPort = reply.Port
|
||||
enableSSL = reply.EnableServiceSSL
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) ImageMounterService() (imageMounter ImageMounter, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.ImageMounterServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageMounterClient := libimobiledevice.NewImageMounterClient(innerConn)
|
||||
imageMounter = newImageMounter(imageMounterClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) ScreenshotService() (screenshot Screenshot, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.ScreenshotServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
screenshotClient := libimobiledevice.NewScreenshotClient(innerConn)
|
||||
screenshot = newScreenshot(screenshotClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) SimulateLocationService() (simulateLocation SimulateLocation, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.SimulateLocationServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
simulateLocationClient := libimobiledevice.NewSimulateLocationClient(innerConn)
|
||||
simulateLocation = newSimulateLocation(simulateLocationClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) InstallationProxyService() (installationProxy InstallationProxy, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.InstallationProxyServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installationProxyClient := libimobiledevice.NewInstallationProxyClient(innerConn)
|
||||
installationProxy = newInstallationProxy(installationProxyClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) InstrumentsService() (instruments Instruments, err error) {
|
||||
service := libimobiledevice.InstrumentsServiceName
|
||||
if DeviceVersion(c.iOSVersion...) >= DeviceVersion(14, 0, 0) {
|
||||
service = libimobiledevice.InstrumentsSecureProxyServiceName
|
||||
}
|
||||
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(service, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instrumentsClient := libimobiledevice.NewInstrumentsClient(innerConn)
|
||||
instruments = newInstruments(instrumentsClient)
|
||||
|
||||
if service == libimobiledevice.InstrumentsServiceName {
|
||||
_ = innerConn.DismissSSL()
|
||||
}
|
||||
|
||||
if err = instruments.notifyOfPublishedCapabilities(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) TestmanagerdService() (testmanagerd Testmanagerd, err error) {
|
||||
service := libimobiledevice.TestmanagerdServiceName
|
||||
if DeviceVersion(c.iOSVersion...) >= DeviceVersion(14, 0, 0) {
|
||||
service = libimobiledevice.TestmanagerdSecureServiceName
|
||||
}
|
||||
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(service, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
testmanagerdClient := libimobiledevice.NewTestmanagerdClient(innerConn)
|
||||
testmanagerd = newTestmanagerd(testmanagerdClient, c.iOSVersion)
|
||||
|
||||
if service == libimobiledevice.TestmanagerdServiceName {
|
||||
_ = innerConn.DismissSSL()
|
||||
}
|
||||
|
||||
if err = testmanagerd.notifyOfPublishedCapabilities(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) AfcService() (afc Afc, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.AfcServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
afcClient := libimobiledevice.NewAfcClient(innerConn)
|
||||
afc = newAfc(afcClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) HouseArrestService() (houseArrest HouseArrest, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.HouseArrestServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
houseArrestClient := libimobiledevice.NewHouseArrestClient(innerConn)
|
||||
houseArrest = newHouseArrest(houseArrestClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) SyslogRelayService() (syslogRelay SyslogRelay, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.SyslogRelayServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
syslogRelayClient := libimobiledevice.NewSyslogRelayClient(innerConn)
|
||||
syslogRelay = newSyslogRelay(syslogRelayClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) PcapdService(targetPID int, targetProcName string) (pcapd Pcapd, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.PcapdServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pcapdClient := libimobiledevice.NewPcapdClient(innerConn, targetPID, targetProcName)
|
||||
return newPcapdClient(pcapdClient), nil
|
||||
}
|
||||
|
||||
func (c *lockdown) DiagnosticsRelayService() (diagnostics DiagnosticsRelay, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.DiagnosticsRelayServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diagnosticsRelayClient := libimobiledevice.NewDiagnosticsRelayClient(innerConn)
|
||||
diagnostics = newDiagnosticsRelay(diagnosticsRelayClient)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) SpringBoardService() (springboard SpringBoard, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.SpringBoardServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
springBoardServiceClient := libimobiledevice.NewSpringBoardClient(innerConn)
|
||||
springboard = newSpringBoard(springBoardServiceClient)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) CrashReportMoverService() (crashReportMover CrashReportMover, err error) {
|
||||
var innerConn InnerConn
|
||||
if innerConn, err = c._startService(libimobiledevice.CrashReportMoverServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mover := newCrashReportMover(libimobiledevice.NewCrashReportMoverClient(innerConn))
|
||||
if err = mover.readPing(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if innerConn, err = c._startService(libimobiledevice.CrashReportCopyMobileServiceName, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mover.afc = newAfc(libimobiledevice.NewAfcClient(innerConn))
|
||||
|
||||
crashReportMover = mover
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) _startService(serviceName string, escrowBag []byte) (innerConn InnerConn, err error) {
|
||||
if err = c.handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.startSession(c.pairRecord); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dynamicPort, enableSSL, err := c.startService(serviceName, escrowBag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.stopSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if innerConn, err = c.dev.NewConnect(dynamicPort, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// clean deadline
|
||||
innerConn.Timeout(0)
|
||||
|
||||
if enableSSL {
|
||||
if err = innerConn.Handshake(c.iOSVersion, c.pairRecord); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *lockdown) _getProductVersion() (version []int, err error) {
|
||||
if c.iOSVersion != nil {
|
||||
return c.iOSVersion, nil
|
||||
}
|
||||
|
||||
var devProductVersion []string
|
||||
if lockdownValue, err := c.GetValue("", "ProductVersion"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
devProductVersion = strings.Split(lockdownValue.(string), ".")
|
||||
}
|
||||
|
||||
version = make([]int, len(devProductVersion))
|
||||
for i, v := range devProductVersion {
|
||||
version[i], _ = strconv.Atoi(v)
|
||||
}
|
||||
|
||||
// if len(version) == 2 {
|
||||
// version = append(version, 0)
|
||||
// }
|
||||
c.iOSVersion = version
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func generatePairRecord(devPublicKeyPem []byte) (pairRecord *PairRecord, err error) {
|
||||
block, _ := pem.Decode(devPublicKeyPem)
|
||||
var deviceKey *rsa.PublicKey
|
||||
if deviceKey, err = x509.ParsePKCS1PublicKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rootKey, hostKey *rsa.PrivateKey
|
||||
if rootKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hostKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serialNumber := big.NewInt(0)
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(time.Hour * (24 * 365) * 10)
|
||||
|
||||
rootTemplate := x509.Certificate{
|
||||
IsCA: true,
|
||||
SerialNumber: serialNumber,
|
||||
Version: 2,
|
||||
SignatureAlgorithm: x509.SHA1WithRSA,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
var caCert, cert []byte
|
||||
if caCert, err = x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, rootKey.Public(), rootKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostTemplate := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Version: 2,
|
||||
SignatureAlgorithm: x509.SHA1WithRSA,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
if cert, err = x509.CreateCertificate(rand.Reader, &hostTemplate, &rootTemplate, hostKey.Public(), rootKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := sha1.New()
|
||||
if _, err = h.Write(rootKey.N.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subjectKeyId := h.Sum(nil)
|
||||
|
||||
deviceTemplate := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Version: 2,
|
||||
SignatureAlgorithm: x509.SHA1WithRSA,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
BasicConstraintsValid: true,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
SubjectKeyId: subjectKeyId,
|
||||
}
|
||||
|
||||
var deviceCert []byte
|
||||
if deviceCert, err = x509.CreateCertificate(rand.Reader, &deviceTemplate, &rootTemplate, deviceKey, rootKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var deviceCertPEM []byte
|
||||
if deviceCertPEM, err = encodePemCertificate(deviceCert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var caPEM, caPrivatePEM []byte
|
||||
if caPEM, caPrivatePEM, err = encodePairPemFormat(caCert, rootKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var certPEM, certPrivatePEM []byte
|
||||
if certPEM, certPrivatePEM, err = encodePairPemFormat(cert, hostKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pairRecord = new(PairRecord)
|
||||
|
||||
pairRecord.DeviceCertificate = deviceCertPEM
|
||||
pairRecord.HostCertificate = certPEM
|
||||
pairRecord.HostPrivateKey = certPrivatePEM
|
||||
pairRecord.RootCertificate = caPEM
|
||||
pairRecord.RootPrivateKey = caPrivatePEM
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func encodePairPemFormat(cert []byte, key *rsa.PrivateKey) ([]byte, []byte, error) {
|
||||
p, err := encodePemCertificate(cert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := pem.Encode(buf, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
privy := buf.Bytes()
|
||||
|
||||
return p, privy, nil
|
||||
}
|
||||
|
||||
func encodePemCertificate(cert []byte) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := pem.Encode(buf, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var lockdownSrv Lockdown
|
||||
|
||||
func setupLockdownSrv(t *testing.T) {
|
||||
setupDevice(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_lockdown_QueryType(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
lockdownType, err := lockdownSrv.QueryType()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(lockdownType.Type)
|
||||
}
|
||||
|
||||
func Test_lockdown_GetValue(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
// v, err := dev.GetValue("com.apple.mobile.iTunes", "")
|
||||
// v, err := dev.GetValue("com.apple.mobile.internal", "")
|
||||
v, err := dev.GetValue("com.apple.mobile.battery", "")
|
||||
// v, err := lockdownSrv.GetValue("", "ProductVersion")
|
||||
// v, err := lockdownSrv.GetValue("", "DeviceName")
|
||||
// v, err := lockdownSrv.GetValue("com.apple.mobile.iTunes", "")
|
||||
// v, err := lockdownSrv.GetValue("com.apple.mobile.battery", "")
|
||||
// v, err := lockdownSrv.GetValue("com.apple.disk_usage", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(v)
|
||||
}
|
||||
|
||||
func Test_lockdown_SyslogRelayService(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
syslogRelaySrv, err := lockdownSrv.SyslogRelayService()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
syslogRelaySrv.Stop()
|
||||
|
||||
lines := syslogRelaySrv.Lines()
|
||||
|
||||
done := make(chan os.Signal, 1)
|
||||
|
||||
go func() {
|
||||
for line := range lines {
|
||||
fmt.Println(line)
|
||||
}
|
||||
done <- os.Interrupt
|
||||
fmt.Println("DONE!!!")
|
||||
}()
|
||||
|
||||
signal.Notify(done, os.Interrupt, os.Kill)
|
||||
|
||||
<-done
|
||||
syslogRelaySrv.Stop()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
func Test_lockdown_CrashReportMoverService(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
crashReportMoverSrv, err := lockdownSrv.CrashReportMoverService()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
filenames := make([]string, 0, 36)
|
||||
fn := func(cwd string, info *AfcFileInfo) {
|
||||
if cwd == "." {
|
||||
cwd = ""
|
||||
}
|
||||
filenames = append(filenames, path.Join(cwd, info.Name()))
|
||||
// fmt.Println(path.Join(cwd, name))
|
||||
}
|
||||
err = crashReportMoverSrv.walkDir(".", fn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, n := range filenames {
|
||||
fmt.Println(n)
|
||||
}
|
||||
|
||||
t.Log(len(filenames))
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
type PcapOptions struct {
|
||||
All bool // capture all packets
|
||||
Pid int // capture packets from specific PID
|
||||
ProcName string // capture packets from specific process name
|
||||
BundleID string // convert to PID first, then capture packets from the PID
|
||||
}
|
||||
|
||||
type PcapOption func(*PcapOptions)
|
||||
|
||||
func WithPcapAll(all bool) PcapOption {
|
||||
return func(opt *PcapOptions) {
|
||||
opt.All = all
|
||||
}
|
||||
}
|
||||
|
||||
func WithPcapProcName(procName string) PcapOption {
|
||||
return func(opt *PcapOptions) {
|
||||
opt.ProcName = procName
|
||||
}
|
||||
}
|
||||
|
||||
func WithPcapPID(pid int) PcapOption {
|
||||
return func(opt *PcapOptions) {
|
||||
opt.Pid = pid
|
||||
}
|
||||
}
|
||||
|
||||
func WithPcapBundleID(bundleID string) PcapOption {
|
||||
return func(opt *PcapOptions) {
|
||||
opt.BundleID = bundleID
|
||||
}
|
||||
}
|
||||
|
||||
type pcapdClient struct {
|
||||
stop chan struct{}
|
||||
c *libimobiledevice.PcapdClient
|
||||
}
|
||||
|
||||
func newPcapdClient(c *libimobiledevice.PcapdClient) *pcapdClient {
|
||||
return &pcapdClient{
|
||||
stop: make(chan struct{}),
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *pcapdClient) Packet() <-chan []byte {
|
||||
packetCh := make(chan []byte, 10)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
return
|
||||
default:
|
||||
pkt, err := c.c.ReceivePacket()
|
||||
if err != nil {
|
||||
close(packetCh)
|
||||
return
|
||||
}
|
||||
var payload []byte
|
||||
_ = pkt.Unmarshal(&payload)
|
||||
raw, err := c.c.GetPacket(payload)
|
||||
if err != nil {
|
||||
close(packetCh)
|
||||
return
|
||||
}
|
||||
if raw == nil {
|
||||
// filtered packet
|
||||
continue
|
||||
}
|
||||
res, err := c.c.CreatePacket(raw)
|
||||
if err != nil {
|
||||
log.Println("failed to create packet")
|
||||
return
|
||||
}
|
||||
packetCh <- res
|
||||
}
|
||||
}
|
||||
}()
|
||||
return packetCh
|
||||
}
|
||||
|
||||
func (c *pcapdClient) Stop() {
|
||||
select {
|
||||
case <-c.stop:
|
||||
default:
|
||||
close(c.stop)
|
||||
}
|
||||
c.c.Close()
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPcapWithPID(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PcapStart(WithPcapPID(1234))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PcapStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPcapWithProcName(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PcapStart(WithPcapProcName("Awe"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PcapStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPcapWithBundleID(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PcapStart(WithPcapBundleID("com.ss.iphone.ugc.Aweme"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PcapStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,893 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
type PerfOptions struct {
|
||||
// system
|
||||
SysCPU bool `json:"sys_cpu,omitempty" yaml:"sys_cpu,omitempty"`
|
||||
SysMem bool `json:"sys_mem,omitempty" yaml:"sys_mem,omitempty"`
|
||||
SysDisk bool `json:"sys_disk,omitempty" yaml:"sys_disk,omitempty"`
|
||||
SysNetwork bool `json:"sys_network,omitempty" yaml:"sys_network,omitempty"`
|
||||
gpu bool
|
||||
FPS bool `json:"fps,omitempty" yaml:"fps,omitempty"`
|
||||
Network bool `json:"network,omitempty" yaml:"network,omitempty"`
|
||||
// process
|
||||
BundleID string `json:"bundle_id,omitempty" yaml:"bundle_id,omitempty"`
|
||||
Pid int `json:"pid,omitempty" yaml:"pid,omitempty"`
|
||||
// config
|
||||
OutputInterval int `json:"output_interval,omitempty" yaml:"output_interval,omitempty"` // ms
|
||||
SystemAttributes []string `json:"system_attributes,omitempty" yaml:"system_attributes,omitempty"`
|
||||
ProcessAttributes []string `json:"process_attributes,omitempty" yaml:"process_attributes,omitempty"`
|
||||
}
|
||||
|
||||
func defaulPerfOption() *PerfOptions {
|
||||
return &PerfOptions{
|
||||
SysCPU: false,
|
||||
SysMem: false,
|
||||
SysDisk: false,
|
||||
SysNetwork: false,
|
||||
gpu: false,
|
||||
FPS: false,
|
||||
Network: false,
|
||||
OutputInterval: 1000, // default 1000ms
|
||||
SystemAttributes: []string{
|
||||
// disk
|
||||
"diskBytesRead",
|
||||
"diskBytesWritten",
|
||||
"diskReadOps",
|
||||
"diskWriteOps",
|
||||
// memory
|
||||
"vmCompressorPageCount",
|
||||
"vmExtPageCount",
|
||||
"vmFreeCount",
|
||||
"vmIntPageCount",
|
||||
"vmPurgeableCount",
|
||||
"vmWireCount",
|
||||
"vmUsedCount",
|
||||
"__vmSwapUsage",
|
||||
// network
|
||||
"netBytesIn",
|
||||
"netBytesOut",
|
||||
"netPacketsIn",
|
||||
"netPacketsOut",
|
||||
},
|
||||
ProcessAttributes: []string{
|
||||
"pid",
|
||||
"cpuUsage",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type PerfOption func(*PerfOptions)
|
||||
|
||||
func WithPerfSystemCPU(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.SysCPU = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfSystemMem(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.SysMem = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfSystemDisk(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.SysDisk = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfSystemNetwork(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.SysNetwork = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfBundleID(bundleID string) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.BundleID = bundleID
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfPID(pid int) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.Pid = pid
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfGPU(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.gpu = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfFPS(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.FPS = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfNetwork(b bool) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.Network = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfOutputInterval(intervalMilliseconds int) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.OutputInterval = intervalMilliseconds
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfProcessAttributes(attrs ...string) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.ProcessAttributes = attrs
|
||||
}
|
||||
}
|
||||
|
||||
func WithPerfSystemAttributes(attrs ...string) PerfOption {
|
||||
return func(opt *PerfOptions) {
|
||||
opt.SystemAttributes = attrs
|
||||
}
|
||||
}
|
||||
|
||||
type perfdClient struct {
|
||||
options *PerfOptions
|
||||
i Instruments
|
||||
stop chan struct{} // used to stop perf client
|
||||
cancel context.CancelFunc // used to cancel all iterators
|
||||
}
|
||||
|
||||
func (d *device) newPerfdSysmontap(options *PerfOptions) (*perfdSysmontap, error) {
|
||||
instruments, err := d.newInstrumentsService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &perfdSysmontap{
|
||||
perfdClient: perfdClient{
|
||||
i: instruments,
|
||||
options: options,
|
||||
stop: make(chan struct{}),
|
||||
},
|
||||
chanSysCPU: make(chan []byte, 10),
|
||||
chanSysMem: make(chan []byte, 10),
|
||||
chanSysDisk: make(chan []byte, 10),
|
||||
chanSysNetwork: make(chan []byte, 10),
|
||||
chanProcess: make(chan []byte, 10),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type perfdSysmontap struct {
|
||||
perfdClient
|
||||
chanSysCPU chan []byte // system cpu channel
|
||||
chanSysMem chan []byte // system mem channel
|
||||
chanSysDisk chan []byte // system disk channel
|
||||
chanSysNetwork chan []byte // system network channel
|
||||
chanProcess chan []byte // process channel
|
||||
}
|
||||
|
||||
func (c *perfdSysmontap) Start() (data <-chan []byte, err error) {
|
||||
// set config
|
||||
interval := time.Millisecond * time.Duration(c.options.OutputInterval)
|
||||
log.Printf("set sysmontap sample interval: %dms\n", c.options.OutputInterval)
|
||||
|
||||
config := map[string]interface{}{
|
||||
"bm": 0,
|
||||
"cpuUsage": true,
|
||||
"sampleInterval": interval, // time.Duration
|
||||
"ur": c.options.OutputInterval, // 输出频率
|
||||
"procAttrs": c.options.ProcessAttributes, // process performance
|
||||
"sysAttrs": c.options.SystemAttributes, // system performance
|
||||
}
|
||||
if _, err = c.i.call(
|
||||
instrumentsServiceSysmontap,
|
||||
"setConfig:",
|
||||
config,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start
|
||||
if _, err = c.i.call(
|
||||
instrumentsServiceSysmontap,
|
||||
"start",
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// register listener
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.i.call(instrumentsServiceSysmontap, "stop")
|
||||
return
|
||||
default:
|
||||
dataArray, ok := m.Obj.([]interface{})
|
||||
if !ok || len(dataArray) != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
if c.options.Pid != 0 {
|
||||
c.parseProcessData(dataArray)
|
||||
} else {
|
||||
c.parseSystemData(dataArray)
|
||||
}
|
||||
}
|
||||
})
|
||||
c.cancel = cancel
|
||||
|
||||
outCh := make(chan []byte, 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
c.cancel()
|
||||
return
|
||||
case cpuBytes, ok := <-c.chanSysCPU:
|
||||
if ok {
|
||||
outCh <- cpuBytes
|
||||
}
|
||||
case memBytes, ok := <-c.chanSysMem:
|
||||
if ok {
|
||||
outCh <- memBytes
|
||||
}
|
||||
case diskBytes, ok := <-c.chanSysDisk:
|
||||
if ok {
|
||||
outCh <- diskBytes
|
||||
}
|
||||
case networkBytes, ok := <-c.chanSysNetwork:
|
||||
if ok {
|
||||
outCh <- networkBytes
|
||||
}
|
||||
case processBytes, ok := <-c.chanProcess:
|
||||
if ok {
|
||||
outCh <- processBytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return outCh, nil
|
||||
}
|
||||
|
||||
func (c *perfdSysmontap) Stop() {
|
||||
close(c.stop)
|
||||
}
|
||||
|
||||
func (c *perfdSysmontap) parseProcessData(dataArray []interface{}) {
|
||||
// dataArray example:
|
||||
// [
|
||||
// map[
|
||||
// CPUCount:2
|
||||
// EnabledCPUs:2
|
||||
// PerCPUUsage:[
|
||||
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:3.6363636363636402 CPU_UserLoad:-1]
|
||||
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:2.7272727272727195 CPU_UserLoad:-1]
|
||||
// ]
|
||||
// System:[36408520704 6897049600 3031160 773697 15596 61940 1297 26942 588 17020 127346 1835008 119718056 107009899 174046 103548]
|
||||
// SystemCPUUsage:map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:6.36363636363636 CPU_UserLoad:-1]
|
||||
// StartMachAbsTime:5896602132889
|
||||
// EndMachAbsTime:5896628486761
|
||||
// Type:41
|
||||
// ]
|
||||
// map[
|
||||
// Processes:map[
|
||||
// 0:[1.3582834340402803 0]
|
||||
// 124:[0.011456702068519481 124]
|
||||
// 136:[0.05468332721703649 136]
|
||||
// ]
|
||||
// StartMachAbsTime:5896602295095
|
||||
// EndMachAbsTime:5896628780514
|
||||
// Type:5
|
||||
// ]
|
||||
// ]
|
||||
|
||||
processData := make(map[string]interface{})
|
||||
processData["type"] = "process"
|
||||
processData["timestamp"] = time.Now().Unix()
|
||||
processData["pid"] = c.options.Pid
|
||||
|
||||
defer func() {
|
||||
processBytes, _ := json.Marshal(processData)
|
||||
c.chanProcess <- processBytes
|
||||
}()
|
||||
|
||||
systemInfo := dataArray[0].(map[string]interface{})
|
||||
processInfo := dataArray[1].(map[string]interface{})
|
||||
if _, ok := systemInfo["System"]; !ok {
|
||||
systemInfo, processInfo = processInfo, systemInfo
|
||||
}
|
||||
|
||||
var targetProcessValue []interface{}
|
||||
processList := processInfo["Processes"].(map[string]interface{})
|
||||
for pid, v := range processList {
|
||||
if pid != strconv.Itoa(c.options.Pid) {
|
||||
continue
|
||||
}
|
||||
targetProcessValue = v.([]interface{})
|
||||
}
|
||||
|
||||
if targetProcessValue == nil {
|
||||
processData["msg"] = fmt.Sprintf("process %d not found", c.options.Pid)
|
||||
return
|
||||
}
|
||||
|
||||
processAttributesMap := make(map[string]interface{})
|
||||
for idx, value := range c.options.ProcessAttributes {
|
||||
processAttributesMap[value] = targetProcessValue[idx]
|
||||
}
|
||||
processData["proc_perf"] = processAttributesMap
|
||||
|
||||
systemAttributesValue := systemInfo["System"].([]interface{})
|
||||
systemAttributesMap := make(map[string]int64)
|
||||
for idx, value := range c.options.SystemAttributes {
|
||||
systemAttributesMap[value] = convert2Int64(systemAttributesValue[idx])
|
||||
}
|
||||
processData["sys_perf"] = systemAttributesMap
|
||||
}
|
||||
|
||||
func (c *perfdSysmontap) parseSystemData(dataArray []interface{}) {
|
||||
timestamp := time.Now().Unix()
|
||||
var systemInfo map[string]interface{}
|
||||
data1 := dataArray[0].(map[string]interface{})
|
||||
data2 := dataArray[1].(map[string]interface{})
|
||||
if _, ok := data1["SystemCPUUsage"]; ok {
|
||||
systemInfo = data1
|
||||
} else {
|
||||
systemInfo = data2
|
||||
}
|
||||
|
||||
// systemInfo example:
|
||||
// map[
|
||||
// CPUCount:2
|
||||
// EnabledCPUs:2
|
||||
// PerCPUUsage:[
|
||||
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:3.9215686274509807 CPU_UserLoad:-1]
|
||||
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:11.650485436893206 CPU_UserLoad:-1]]
|
||||
// ]
|
||||
// System:[704211 35486281728 6303789056 3001119 1001 11033 52668 1740 40022 2114 17310 126903 1835008 160323 107909856 95067 95808179]
|
||||
// SystemCPUUsage:map[
|
||||
// CPU_NiceLoad:0
|
||||
// CPU_SystemLoad:-1
|
||||
// CPU_TotalLoad:15.572054064344186
|
||||
// CPU_UserLoad:-1
|
||||
// ]
|
||||
// StartMachAbsTime:5339240248449
|
||||
// EndMachAbsTime:5339264441260
|
||||
// Type:41
|
||||
// ]
|
||||
|
||||
if c.options.SysCPU {
|
||||
sysCPUUsage := systemInfo["SystemCPUUsage"].(map[string]interface{})
|
||||
sysCPUInfo := SystemCPUData{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "sys_cpu",
|
||||
TimeStamp: timestamp,
|
||||
},
|
||||
NiceLoad: sysCPUUsage["CPU_NiceLoad"].(float64),
|
||||
SystemLoad: sysCPUUsage["CPU_SystemLoad"].(float64),
|
||||
TotalLoad: sysCPUUsage["CPU_TotalLoad"].(float64),
|
||||
UserLoad: sysCPUUsage["CPU_UserLoad"].(float64),
|
||||
}
|
||||
cpuBytes, _ := json.Marshal(sysCPUInfo)
|
||||
c.chanSysCPU <- cpuBytes
|
||||
}
|
||||
|
||||
systemAttributesValue := systemInfo["System"].([]interface{})
|
||||
systemAttributesMap := make(map[string]int64)
|
||||
for idx, value := range c.options.SystemAttributes {
|
||||
systemAttributesMap[value] = convert2Int64(systemAttributesValue[idx])
|
||||
}
|
||||
|
||||
if c.options.SysMem {
|
||||
kernelPageSize := int64(1) // why 16384 ?
|
||||
appMemory := (systemAttributesMap["vmIntPageCount"] - systemAttributesMap["vmPurgeableCount"]) * kernelPageSize
|
||||
cachedFiles := (systemAttributesMap["vmExtPageCount"] - systemAttributesMap["vmPurgeableCount"]) * kernelPageSize
|
||||
compressed := systemAttributesMap["vmCompressorPageCount"] * kernelPageSize
|
||||
usedMemory := (systemAttributesMap["vmUsedCount"] - systemAttributesMap["vmExtPageCount"]) * kernelPageSize
|
||||
wiredMemory := systemAttributesMap["vmWireCount"] * kernelPageSize
|
||||
swapUsed := systemAttributesMap["__vmSwapUsage"]
|
||||
freeMemory := systemAttributesMap["vmFreeCount"] * kernelPageSize
|
||||
|
||||
sysMemInfo := SystemMemData{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "sys_mem",
|
||||
TimeStamp: timestamp,
|
||||
},
|
||||
AppMemory: appMemory,
|
||||
UsedMemory: usedMemory,
|
||||
WiredMemory: wiredMemory,
|
||||
FreeMemory: freeMemory,
|
||||
CachedFiles: cachedFiles,
|
||||
Compressed: compressed,
|
||||
SwapUsed: swapUsed,
|
||||
}
|
||||
memBytes, _ := json.Marshal(sysMemInfo)
|
||||
c.chanSysMem <- memBytes
|
||||
}
|
||||
|
||||
if c.options.SysDisk {
|
||||
diskBytesRead := systemAttributesMap["diskBytesRead"]
|
||||
diskBytesWritten := systemAttributesMap["diskBytesWritten"]
|
||||
diskReadOps := systemAttributesMap["diskReadOps"]
|
||||
diskWriteOps := systemAttributesMap["diskWriteOps"]
|
||||
|
||||
sysDiskInfo := SystemDiskData{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "sys_disk",
|
||||
TimeStamp: timestamp,
|
||||
},
|
||||
DataRead: diskBytesRead,
|
||||
DataWritten: diskBytesWritten,
|
||||
ReadOps: diskReadOps,
|
||||
WriteOps: diskWriteOps,
|
||||
}
|
||||
diskBytes, _ := json.Marshal(sysDiskInfo)
|
||||
c.chanSysDisk <- diskBytes
|
||||
}
|
||||
|
||||
if c.options.SysNetwork {
|
||||
netBytesIn := systemAttributesMap["netBytesIn"]
|
||||
netBytesOut := systemAttributesMap["netBytesOut"]
|
||||
netPacketsIn := systemAttributesMap["netPacketsIn"]
|
||||
netPacketsOut := systemAttributesMap["netPacketsOut"]
|
||||
|
||||
sysNetworkInfo := SystemNetworkData{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "sys_network",
|
||||
TimeStamp: timestamp,
|
||||
},
|
||||
BytesIn: netBytesIn,
|
||||
BytesOut: netBytesOut,
|
||||
PacketsIn: netPacketsIn,
|
||||
PacketsOut: netPacketsOut,
|
||||
}
|
||||
networkBytes, _ := json.Marshal(sysNetworkInfo)
|
||||
c.chanSysNetwork <- networkBytes
|
||||
}
|
||||
}
|
||||
|
||||
type SystemCPUData struct {
|
||||
PerfDataBase // system cpu
|
||||
NiceLoad float64 `json:"nice_load"`
|
||||
SystemLoad float64 `json:"system_load"`
|
||||
TotalLoad float64 `json:"total_load"`
|
||||
UserLoad float64 `json:"user_load"`
|
||||
}
|
||||
|
||||
type SystemMemData struct {
|
||||
PerfDataBase // mem
|
||||
AppMemory int64 `json:"app_memory"`
|
||||
FreeMemory int64 `json:"free_memory"`
|
||||
UsedMemory int64 `json:"used_memory"`
|
||||
WiredMemory int64 `json:"wired_memory"`
|
||||
CachedFiles int64 `json:"cached_files"`
|
||||
Compressed int64 `json:"compressed"`
|
||||
SwapUsed int64 `json:"swap_used"`
|
||||
}
|
||||
|
||||
type SystemDiskData struct {
|
||||
PerfDataBase // disk
|
||||
DataRead int64 `json:"data_read"`
|
||||
DataWritten int64 `json:"data_written"`
|
||||
ReadOps int64 `json:"reads_in"`
|
||||
WriteOps int64 `json:"writes_out"`
|
||||
}
|
||||
|
||||
type SystemNetworkData struct {
|
||||
PerfDataBase // network
|
||||
BytesIn int64 `json:"bytes_in"`
|
||||
BytesOut int64 `json:"bytes_out"`
|
||||
PacketsIn int64 `json:"packets_in"`
|
||||
PacketsOut int64 `json:"packets_out"`
|
||||
}
|
||||
|
||||
func (d *device) newPerfdNetworking(options *PerfOptions) (*perfdNetworking, error) {
|
||||
instruments, err := d.newInstrumentsService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &perfdNetworking{
|
||||
perfdClient: perfdClient{
|
||||
i: instruments,
|
||||
options: options,
|
||||
stop: make(chan struct{}),
|
||||
},
|
||||
chanNetwork: make(chan []byte, 10),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type perfdNetworking struct {
|
||||
perfdClient
|
||||
chanNetwork chan []byte // network channel
|
||||
}
|
||||
|
||||
func (c *perfdNetworking) Start() (data <-chan []byte, err error) {
|
||||
if _, err = c.i.call(
|
||||
instrumentsServiceNetworking,
|
||||
"replayLastRecordedSession",
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = c.i.call(
|
||||
instrumentsServiceNetworking,
|
||||
"startMonitoring",
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.i.call(instrumentsServiceNetworking, "stopMonitoring")
|
||||
return
|
||||
default:
|
||||
c.parseNetworking(m.Obj)
|
||||
}
|
||||
})
|
||||
c.cancel = cancel
|
||||
|
||||
outCh := make(chan []byte, 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
c.cancel()
|
||||
return
|
||||
case networkBytes, ok := <-c.chanNetwork:
|
||||
if ok {
|
||||
outCh <- networkBytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return outCh, nil
|
||||
}
|
||||
|
||||
func (c *perfdNetworking) Stop() {
|
||||
close(c.stop)
|
||||
}
|
||||
|
||||
func (c *perfdNetworking) parseNetworking(data interface{}) {
|
||||
raw, ok := data.([]interface{})
|
||||
if !ok || len(raw) != 2 {
|
||||
fmt.Printf("invalid networking data: %v\n", data)
|
||||
return
|
||||
}
|
||||
|
||||
var netBytes []byte
|
||||
msgType := raw[0].(uint64)
|
||||
msgValue := raw[1].([]interface{})
|
||||
if msgType == 0 {
|
||||
// interface-detection
|
||||
// ['InterfaceIndex', "Name"]
|
||||
// e.g. [0, [14, 'en0']]
|
||||
netData := NetworkDataInterfaceDetection{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "network-interface-detection",
|
||||
TimeStamp: time.Now().Unix(),
|
||||
},
|
||||
InterfaceIndex: convert2Int64(msgValue[0]),
|
||||
Name: msgValue[1].(string),
|
||||
}
|
||||
netBytes, _ = json.Marshal(netData)
|
||||
} else if msgType == 1 {
|
||||
// connection-detected
|
||||
// ['LocalAddress', 'RemoteAddress', 'InterfaceIndex', 'Pid',
|
||||
// 'RecvBufferSize', 'RecvBufferUsed', 'SerialNumber', 'Kind']
|
||||
// e.g. [1 [[16 2 211 158 192 168 100 101 0 0 0 0 0 0 0 0]
|
||||
// [16 2 0 53 183 221 253 100 0 0 0 0 0 0 0 0]
|
||||
// 14 -2 786896 0 133 2]]
|
||||
|
||||
localAddr, err := parseSocketAddr(msgValue[0].([]byte))
|
||||
if err != nil {
|
||||
fmt.Printf("parse local socket address err: %v\n", err)
|
||||
}
|
||||
remoteAddr, err := parseSocketAddr(msgValue[1].([]byte))
|
||||
if err != nil {
|
||||
fmt.Printf("parse remote socket address err: %v\n", err)
|
||||
}
|
||||
netData := NetworkDataConnectionDetected{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "network-connection-detected",
|
||||
TimeStamp: time.Now().Unix(),
|
||||
},
|
||||
LocalAddress: localAddr,
|
||||
RemoteAddress: remoteAddr,
|
||||
InterfaceIndex: convert2Int64(msgValue[2]),
|
||||
Pid: convert2Int64(msgValue[3]),
|
||||
RecvBufferSize: convert2Int64(msgValue[4]),
|
||||
RecvBufferUsed: convert2Int64(msgValue[5]),
|
||||
SerialNumber: convert2Int64(msgValue[6]),
|
||||
Kind: convert2Int64(msgValue[7]),
|
||||
}
|
||||
netBytes, _ = json.Marshal(netData)
|
||||
} else if msgType == 2 {
|
||||
// connection-update
|
||||
// ['RxPackets', 'RxBytes', 'TxPackets', 'TxBytes',
|
||||
// 'RxDups', 'RxOOO', 'TxRetx', 'MinRTT', 'AvgRTT', 'ConnectionSerial']
|
||||
// e.g. [2, [21, 1708, 22, 14119, 309, 0, 5830, 0.076125, 0.076125, 54, -1]]
|
||||
netData := NetworkDataConnectionUpdate{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "network-connection-update",
|
||||
TimeStamp: time.Now().Unix(),
|
||||
},
|
||||
RxBytes: convert2Int64(msgValue[0]),
|
||||
RxPackets: convert2Int64(msgValue[1]),
|
||||
TxBytes: convert2Int64(msgValue[2]),
|
||||
TxPackets: convert2Int64(msgValue[3]),
|
||||
}
|
||||
if value, ok := msgValue[4].(uint64); ok {
|
||||
netData.RxDups = int64(value)
|
||||
}
|
||||
if value, ok := msgValue[5].(uint64); ok {
|
||||
netData.RxOOO = int64(value)
|
||||
}
|
||||
if value, ok := msgValue[6].(uint64); ok {
|
||||
netData.TxRetx = int64(value)
|
||||
}
|
||||
if value, ok := msgValue[7].(uint64); ok {
|
||||
netData.MinRTT = int64(value)
|
||||
}
|
||||
if value, ok := msgValue[8].(uint64); ok {
|
||||
netData.AvgRTT = int64(value)
|
||||
}
|
||||
if value, ok := msgValue[9].(uint64); ok {
|
||||
netData.ConnectionSerial = int64(value)
|
||||
}
|
||||
|
||||
netBytes, _ = json.Marshal(netData)
|
||||
}
|
||||
c.chanNetwork <- netBytes
|
||||
}
|
||||
|
||||
func parseSocketAddr(data []byte) (string, error) {
|
||||
len := data[0] // length of address
|
||||
_ = data[1] // family
|
||||
port := binary.BigEndian.Uint16(data[2:4]) // port
|
||||
|
||||
// network, data[4:4+len]
|
||||
if len == 0x10 {
|
||||
// IPv4, 4 bytes
|
||||
ip := net.IP(data[4:8])
|
||||
return fmt.Sprintf("%s:%d", ip, port), nil
|
||||
} else if len == 0x1c {
|
||||
// IPv6, 16 bytes
|
||||
ip := net.IP(data[4:20])
|
||||
return fmt.Sprintf("%s:%d", ip, port), nil
|
||||
}
|
||||
return "", fmt.Errorf("invalid socket address: %v", data)
|
||||
}
|
||||
|
||||
type PerfDataBase struct {
|
||||
Type string `json:"type"`
|
||||
TimeStamp int64 `json:"timestamp"`
|
||||
Msg string `json:"msg,omitempty"` // message for invalid data
|
||||
}
|
||||
|
||||
// network-interface-detection
|
||||
type NetworkDataInterfaceDetection struct {
|
||||
PerfDataBase
|
||||
InterfaceIndex int64 `json:"interface_index"` // 0
|
||||
Name string `json:"name"` // 1
|
||||
}
|
||||
|
||||
// network-connection-detected
|
||||
type NetworkDataConnectionDetected struct {
|
||||
PerfDataBase
|
||||
LocalAddress string `json:"local_address"` // 0
|
||||
RemoteAddress string `json:"remote_address"` // 1
|
||||
InterfaceIndex int64 `json:"interface_index"` // 2
|
||||
Pid int64 `json:"pid"` // 3
|
||||
RecvBufferSize int64 `json:"recv_buffer_size"` // 4
|
||||
RecvBufferUsed int64 `json:"recv_buffer_used"` // 5
|
||||
SerialNumber int64 `json:"serial_number"` // 6
|
||||
Kind int64 `json:"kind"` // 7
|
||||
}
|
||||
|
||||
// network-connection-update
|
||||
type NetworkDataConnectionUpdate struct {
|
||||
PerfDataBase
|
||||
RxBytes int64 `json:"rx_bytes"` // 0
|
||||
RxPackets int64 `json:"rx_packets"` // 1
|
||||
TxBytes int64 `json:"tx_bytes"` // 2
|
||||
TxPackets int64 `json:"tx_packets"` // 3
|
||||
RxDups int64 `json:"rx_dups,omitempty"` // 4
|
||||
RxOOO int64 `json:"rx_000,omitempty"` // 5
|
||||
TxRetx int64 `json:"tx_retx,omitempty"` // 6
|
||||
MinRTT int64 `json:"min_rtt,omitempty"` // 7
|
||||
AvgRTT int64 `json:"avg_rtt,omitempty"` // 8
|
||||
ConnectionSerial int64 `json:"connection_serial"` // 9
|
||||
}
|
||||
|
||||
func (d *device) newPerfdGraphicsOpengl(options *PerfOptions) (*perfdGraphicsOpengl, error) {
|
||||
instruments, err := d.newInstrumentsService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &perfdGraphicsOpengl{
|
||||
perfdClient: perfdClient{
|
||||
i: instruments,
|
||||
options: options,
|
||||
stop: make(chan struct{}),
|
||||
},
|
||||
chanGPU: make(chan []byte, 10),
|
||||
chanFPS: make(chan []byte, 10),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type perfdGraphicsOpengl struct {
|
||||
perfdClient
|
||||
chanGPU chan []byte // gpu channel
|
||||
chanFPS chan []byte // fps channel
|
||||
}
|
||||
|
||||
func (c *perfdGraphicsOpengl) Start() (data <-chan []byte, err error) {
|
||||
if _, err = c.i.call(
|
||||
instrumentsServiceGraphicsOpengl,
|
||||
"setSamplingRate:",
|
||||
float64(c.options.OutputInterval)/100, // FIXME: unable to set sampling rate, always 1.0
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = c.i.call(
|
||||
instrumentsServiceGraphicsOpengl,
|
||||
"startSamplingAtTimeInterval:",
|
||||
0,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.i.call(instrumentsServiceGraphicsOpengl, "stopSampling")
|
||||
return
|
||||
default:
|
||||
c.parseData(m.Obj)
|
||||
}
|
||||
})
|
||||
c.cancel = cancel
|
||||
|
||||
outCh := make(chan []byte, 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
c.cancel()
|
||||
return
|
||||
case gpuBytes, ok := <-c.chanGPU:
|
||||
if ok {
|
||||
outCh <- gpuBytes
|
||||
}
|
||||
case fpsBytes, ok := <-c.chanFPS:
|
||||
if ok {
|
||||
outCh <- fpsBytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return outCh, nil
|
||||
}
|
||||
|
||||
func (c *perfdGraphicsOpengl) Stop() {
|
||||
close(c.stop)
|
||||
}
|
||||
|
||||
func (c *perfdGraphicsOpengl) parseData(data interface{}) {
|
||||
// data example:
|
||||
// map[
|
||||
// Alloc system memory:50167808
|
||||
// Allocated PB Size:1179648
|
||||
// CoreAnimationFramesPerSecond:0 // fps from GPU
|
||||
// Device Utilization %:0 // device
|
||||
// IOGLBundleName:Built-In
|
||||
// In use system memory:10633216
|
||||
// Renderer Utilization %:0 // renderer
|
||||
// SplitSceneCount:0
|
||||
// TiledSceneBytes:0
|
||||
// Tiler Utilization %:0 // tiler
|
||||
// XRVideoCardRunTimeStamp:1010679
|
||||
// recoveryCount:0
|
||||
// ]
|
||||
|
||||
gpuInfo := GPUData{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "gpu",
|
||||
TimeStamp: time.Now().Unix(),
|
||||
},
|
||||
}
|
||||
fpsInfo := FPSData{
|
||||
PerfDataBase: PerfDataBase{
|
||||
Type: "fps",
|
||||
TimeStamp: time.Now().Unix(),
|
||||
},
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if c.options.gpu {
|
||||
gpuBytes, _ := json.Marshal(gpuInfo)
|
||||
c.chanGPU <- gpuBytes
|
||||
}
|
||||
if c.options.FPS {
|
||||
fpsBytes, _ := json.Marshal(fpsInfo)
|
||||
c.chanFPS <- fpsBytes
|
||||
}
|
||||
}()
|
||||
|
||||
raw, ok := data.(map[string]interface{})
|
||||
if !ok {
|
||||
gpuInfo.Msg = fmt.Sprintf("invalid graphics.opengl data: %v", data)
|
||||
return
|
||||
}
|
||||
|
||||
// gpu
|
||||
gpuInfo.DeviceUtilization = convert2Int64(raw["Device Utilization %"])
|
||||
gpuInfo.TilerUtilization = convert2Int64(raw["Tiler Utilization %"])
|
||||
gpuInfo.RendererUtilization = convert2Int64(raw["Renderer Utilization %"])
|
||||
|
||||
// fps
|
||||
fpsInfo.FPS = int(convert2Int64(raw["CoreAnimationFramesPerSecond"]))
|
||||
}
|
||||
|
||||
type GPUData struct {
|
||||
PerfDataBase // gpu
|
||||
TilerUtilization int64 `json:"tiler_utilization"` // 处理顶点的 GPU 时间占比
|
||||
DeviceUtilization int64 `json:"device_utilization"` // 设备利用率
|
||||
RendererUtilization int64 `json:"renderer_utilization"` // 渲染器利用率
|
||||
}
|
||||
|
||||
type FPSData struct {
|
||||
PerfDataBase // fps
|
||||
FPS int `json:"fps"`
|
||||
}
|
||||
|
||||
func convert2Int64(num interface{}) int64 {
|
||||
switch value := num.(type) {
|
||||
case int64:
|
||||
return value
|
||||
case uint64:
|
||||
return int64(value)
|
||||
case uint32:
|
||||
return int64(value)
|
||||
case uint16:
|
||||
return int64(value)
|
||||
case uint8:
|
||||
return int64(value)
|
||||
case uint:
|
||||
return int64(value)
|
||||
}
|
||||
fmt.Printf("convert2Int64 failed: %v, %T\n", num, num)
|
||||
return -1
|
||||
}
|
||||
|
||||
func containString(ss []string, s string) bool {
|
||||
for _, v := range ss {
|
||||
if s == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPerfSystemMonitor(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PerfStart(
|
||||
WithPerfSystemCPU(true),
|
||||
WithPerfSystemMem(true),
|
||||
WithPerfSystemDisk(true),
|
||||
WithPerfSystemNetwork(true),
|
||||
WithPerfOutputInterval(1000),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PerfStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerfProcessMonitor(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PerfStart(
|
||||
WithPerfProcessAttributes("cpuUsage", "memAnon"),
|
||||
WithPerfOutputInterval(1000),
|
||||
WithPerfPID(100),
|
||||
WithPerfBundleID("com.apple.mobilesafari"), // higher priority than pid
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PerfStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerfGPU(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PerfStart(
|
||||
WithPerfSystemCPU(false),
|
||||
WithPerfSystemMem(false),
|
||||
WithPerfGPU(true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PerfStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerfFPS(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PerfStart(
|
||||
WithPerfSystemCPU(false),
|
||||
WithPerfSystemMem(false),
|
||||
WithPerfFPS(true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PerfStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerfNetwork(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PerfStart(
|
||||
WithPerfSystemCPU(false),
|
||||
WithPerfSystemMem(false),
|
||||
WithPerfNetwork(true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PerfStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerfAll(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
data, err := dev.PerfStart(
|
||||
WithPerfSystemCPU(true),
|
||||
WithPerfSystemMem(true),
|
||||
WithPerfSystemDisk(true),
|
||||
WithPerfSystemNetwork(true),
|
||||
WithPerfNetwork(true),
|
||||
WithPerfFPS(true),
|
||||
WithPerfGPU(true),
|
||||
WithPerfBundleID("com.apple.mobilesafari"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(time.Duration(time.Second * 10))
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
dev.PerfStop()
|
||||
return
|
||||
case d := <-data:
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func Info(ipaPath string) (info map[string]interface{}, err error) {
|
||||
var reader *zip.ReadCloser
|
||||
if reader, err = zip.OpenReader(ipaPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = reader.Close()
|
||||
}()
|
||||
|
||||
for _, file := range reader.File {
|
||||
matched, _err := path.Match("Payload/*.app/Info.plist", file.Name)
|
||||
if _err != nil {
|
||||
err = _err
|
||||
continue
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
var rd io.ReadCloser
|
||||
if rd, _err = file.Open(); _err != nil {
|
||||
return nil, _err
|
||||
}
|
||||
data, _err := io.ReadAll(rd)
|
||||
if _err != nil {
|
||||
return nil, _err
|
||||
}
|
||||
|
||||
info = make(map[string]interface{})
|
||||
_, _err = plist.Unmarshal(data, &info)
|
||||
if _err != nil {
|
||||
return nil, _err
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && len(info) == 0 {
|
||||
return nil, fmt.Errorf("find Info.plist: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
name := "/Users/hero/Documents/Workspace/GitHub/taobao-iphone-device/tests/testdata/WebDriverAgentRunner.ipa"
|
||||
name = "/private/tmp/derivedDataPath/Build/Products/Release-iphoneos/WebDriverAgentRunner-Runner.ipa"
|
||||
|
||||
info, err := Info(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for k, v := range info {
|
||||
t.Logf("%-50s\t%v", k, v)
|
||||
}
|
||||
|
||||
t.Log(info["CFBundleIdentifier"])
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const AfcServiceName = "com.apple.afc"
|
||||
|
||||
func NewAfcClient(innerConn InnerConn) *AfcClient {
|
||||
return &AfcClient{
|
||||
innerConn: innerConn,
|
||||
}
|
||||
}
|
||||
|
||||
type AfcClient struct {
|
||||
innerConn InnerConn
|
||||
packetNum uint64
|
||||
}
|
||||
|
||||
func (c *AfcClient) newPacket(operation uint64, data, payload []byte) Packet {
|
||||
c.packetNum++
|
||||
pkt := &afcPacket{
|
||||
operation: operation,
|
||||
packetNum: c.packetNum,
|
||||
entireLen: 40,
|
||||
thisLen: 40,
|
||||
}
|
||||
if data != nil {
|
||||
n := uint64(len(data))
|
||||
pkt.entireLen += n
|
||||
pkt.thisLen += n
|
||||
}
|
||||
if payload != nil {
|
||||
pkt.entireLen += uint64(len(payload))
|
||||
}
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *AfcClient) Send(operation uint64, data, payload []byte) (err error) {
|
||||
pkt := c.newPacket(operation, data, payload)
|
||||
var raw []byte
|
||||
if raw, err = pkt.Pack(); err != nil {
|
||||
return fmt.Errorf("send packet (afc): %w", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(raw)
|
||||
if data != nil {
|
||||
debugLog(fmt.Sprintf("--> %s ...afc data...\n", pkt))
|
||||
buf.Write(data)
|
||||
} else {
|
||||
debugLog(fmt.Sprintf("--> %s\n", pkt))
|
||||
}
|
||||
|
||||
if err = c.innerConn.Write(buf.Bytes()); err != nil {
|
||||
return fmt.Errorf("send packet (afc): %w", err)
|
||||
}
|
||||
|
||||
if payload != nil {
|
||||
if err = c.innerConn.Write(payload); err != nil {
|
||||
return fmt.Errorf("send packet (afc): %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AfcClient) Receive() (respMsg *AfcMessage, err error) {
|
||||
var bufHeader []byte
|
||||
if bufHeader, err = c.innerConn.Read(40); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc): %w", err)
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.Write(bufHeader)
|
||||
var respPkt *afcPacket
|
||||
if respPkt, err = new(afcPacket).unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc): %w", err)
|
||||
}
|
||||
|
||||
respMsg = new(AfcMessage)
|
||||
respMsg.Operation = respPkt.operation
|
||||
|
||||
buffer.Reset()
|
||||
if respPkt.entireLen > 40 {
|
||||
length := int(respPkt.entireLen - 40)
|
||||
var bufDataAndPayload []byte
|
||||
if bufDataAndPayload, err = c.innerConn.Read(length); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc): %w", err)
|
||||
}
|
||||
buffer.Write(bufDataAndPayload)
|
||||
}
|
||||
|
||||
bufData := make([]byte, respPkt.thisLen-40)
|
||||
if _, err = buffer.Read(bufData); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc buffer): %w", err)
|
||||
}
|
||||
respMsg.Data = bufData
|
||||
respMsg.Payload = buffer.Bytes()
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n%s\n%s", respPkt, hex.Dump(respMsg.Data), hex.Dump(respMsg.Payload)))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type AfcMessage struct {
|
||||
Operation uint64
|
||||
Data []byte
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Map() map[string]string {
|
||||
ret := make(map[string]string)
|
||||
ss := m.Strings()
|
||||
if ss != nil {
|
||||
for i := 0; i < len(ss); i += 2 {
|
||||
ret[ss[i]] = ss[i+1]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Strings() []string {
|
||||
if m.Operation == AfcOperationData {
|
||||
bs := bytes.Split(m.Payload, []byte{0})
|
||||
ss := make([]string, len(bs)-1)
|
||||
for i := 0; i < len(ss); i++ {
|
||||
ss[i] = string(bs[i])
|
||||
}
|
||||
return ss
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Uint64() uint64 {
|
||||
return binary.LittleEndian.Uint64(m.Data)
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Err() error {
|
||||
if m.Operation == AfcOperationStatus {
|
||||
status := m.Uint64()
|
||||
if status != AfcErrSuccess {
|
||||
return toError(status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toError(status uint64) error {
|
||||
switch status {
|
||||
case AfcErrUnknownError:
|
||||
return errors.New("UnknownError")
|
||||
case AfcErrOperationHeaderInvalid:
|
||||
return errors.New("OperationHeaderInvalid")
|
||||
case AfcErrNoResources:
|
||||
return errors.New("NoResources")
|
||||
case AfcErrReadError:
|
||||
return errors.New("ReadError")
|
||||
case AfcErrWriteError:
|
||||
return errors.New("WriteError")
|
||||
case AfcErrUnknownPacketType:
|
||||
return errors.New("UnknownPacketType")
|
||||
case AfcErrInvalidArgument:
|
||||
return errors.New("InvalidArgument")
|
||||
case AfcErrObjectNotFound:
|
||||
return errors.New("ObjectNotFound")
|
||||
case AfcErrObjectIsDir:
|
||||
return errors.New("ObjectIsDir")
|
||||
case AfcErrPermDenied:
|
||||
return errors.New("PermDenied")
|
||||
case AfcErrServiceNotConnected:
|
||||
return errors.New("ServiceNotConnected")
|
||||
case AfcErrOperationTimeout:
|
||||
return errors.New("OperationTimeout")
|
||||
case AfcErrTooMuchData:
|
||||
return errors.New("TooMuchData")
|
||||
case AfcErrEndOfData:
|
||||
return errors.New("EndOfData")
|
||||
case AfcErrOperationNotSupported:
|
||||
return errors.New("OperationNotSupported")
|
||||
case AfcErrObjectExists:
|
||||
return errors.New("ObjectExists")
|
||||
case AfcErrObjectBusy:
|
||||
return errors.New("ObjectBusy")
|
||||
case AfcErrNoSpaceLeft:
|
||||
return errors.New("NoSpaceLeft")
|
||||
case AfcErrOperationWouldBlock:
|
||||
return errors.New("OperationWouldBlock")
|
||||
case AfcErrIoError:
|
||||
return errors.New("IoError")
|
||||
case AfcErrOperationInterrupted:
|
||||
return errors.New("OperationInterrupted")
|
||||
case AfcErrOperationInProgress:
|
||||
return errors.New("OperationInProgress")
|
||||
case AfcErrInternalError:
|
||||
return errors.New("InternalError")
|
||||
case AfcErrMuxError:
|
||||
return errors.New("MuxError")
|
||||
case AfcErrNoMemory:
|
||||
return errors.New("NoMemory")
|
||||
case AfcErrNotEnoughData:
|
||||
return errors.New("NotEnoughData")
|
||||
case AfcErrDirNotEmpty:
|
||||
return errors.New("DirNotEmpty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
AfcOperationInvalid = 0x00000000 /* Invalid */
|
||||
AfcOperationStatus = 0x00000001 /* Status */
|
||||
AfcOperationData = 0x00000002 /* Data */
|
||||
AfcOperationReadDir = 0x00000003 /* ReadDir */
|
||||
AfcOperationReadFile = 0x00000004 /* ReadFile */
|
||||
AfcOperationWriteFile = 0x00000005 /* WriteFile */
|
||||
AfcOperationWritePart = 0x00000006 /* WritePart */
|
||||
AfcOperationTruncateFile = 0x00000007 /* TruncateFile */
|
||||
AfcOperationRemovePath = 0x00000008 /* RemovePath */
|
||||
AfcOperationMakeDir = 0x00000009 /* MakeDir */
|
||||
AfcOperationGetFileInfo = 0x0000000A /* GetFileInfo */
|
||||
AfcOperationGetDeviceInfo = 0x0000000B /* GetDeviceInfo */
|
||||
AfcOperationWriteFileAtomic = 0x0000000C /* WriteFileAtomic (tmp file+rename) */
|
||||
AfcOperationFileOpen = 0x0000000D /* FileRefOpen */
|
||||
AfcOperationFileOpenResult = 0x0000000E /* FileRefOpenResult */
|
||||
AfcOperationFileRead = 0x0000000F /* FileRefRead */
|
||||
AfcOperationFileWrite = 0x00000010 /* FileRefWrite */
|
||||
AfcOperationFileSeek = 0x00000011 /* FileRefSeek */
|
||||
AfcOperationFileTell = 0x00000012 /* FileRefTell */
|
||||
AfcOperationFileTellResult = 0x00000013 /* FileRefTellResult */
|
||||
AfcOperationFileClose = 0x00000014 /* FileRefClose */
|
||||
AfcOperationFileSetSize = 0x00000015 /* FileRefSetFileSize (ftruncate) */
|
||||
AfcOperationGetConnectionInfo = 0x00000016 /* GetConnectionInfo */
|
||||
AfcOperationSetConnectionOptions = 0x00000017 /* SetConnectionOptions */
|
||||
AfcOperationRenamePath = 0x00000018 /* RenamePath */
|
||||
AfcOperationSetFSBlockSize = 0x00000019 /* SetFSBlockSize (0x800000) */
|
||||
AfcOperationSetSocketBlockSize = 0x0000001A /* SetSocketBlockSize (0x800000) */
|
||||
AfcOperationFileRefLock = 0x0000001B /* FileRefLock */
|
||||
AfcOperationMakeLink = 0x0000001C /* MakeLink */
|
||||
AfcOperationGetFileHash = 0x0000001D /* GetFileHash */
|
||||
AfcOperationSetFileModTime = 0x0000001E /* SetModTime */
|
||||
AfcOperationGetFileHashRange = 0x0000001F /* GetFileHashWithRange */
|
||||
/* iOS 6+ */
|
||||
AfcOperationFileSetImmutableHint = 0x00000020 /* FileRefSetImmutableHint */
|
||||
AfcOperationGetSizeOfPathContents = 0x00000021 /* GetSizeOfPathContents */
|
||||
AfcOperationRemovePathAndContents = 0x00000022 /* RemovePathAndContents */
|
||||
AfcOperationDirectoryEnumeratorRefOpen = 0x00000023 /* DirectoryEnumeratorRefOpen */
|
||||
AfcOperationDirectoryEnumeratorRefOpenResult = 0x00000024 /* DirectoryEnumeratorRefOpenResult */
|
||||
AfcOperationDirectoryEnumeratorRefRead = 0x00000025 /* DirectoryEnumeratorRefRead */
|
||||
AfcOperationDirectoryEnumeratorRefClose = 0x00000026 /* DirectoryEnumeratorRefClose */
|
||||
/* iOS 7+ */
|
||||
AfcOperationFileRefReadWithOffset = 0x00000027 /* FileRefReadWithOffset */
|
||||
AfcOperationFileRefWriteWithOffset = 0x00000028 /* FileRefWriteWithOffset */
|
||||
)
|
||||
|
||||
const (
|
||||
AfcErrSuccess = 0
|
||||
AfcErrUnknownError = 1
|
||||
AfcErrOperationHeaderInvalid = 2
|
||||
AfcErrNoResources = 3
|
||||
AfcErrReadError = 4
|
||||
AfcErrWriteError = 5
|
||||
AfcErrUnknownPacketType = 6
|
||||
AfcErrInvalidArgument = 7
|
||||
AfcErrObjectNotFound = 8
|
||||
AfcErrObjectIsDir = 9
|
||||
AfcErrPermDenied = 10
|
||||
AfcErrServiceNotConnected = 11
|
||||
AfcErrOperationTimeout = 12
|
||||
AfcErrTooMuchData = 13
|
||||
AfcErrEndOfData = 14
|
||||
AfcErrOperationNotSupported = 15
|
||||
AfcErrObjectExists = 16
|
||||
AfcErrObjectBusy = 17
|
||||
AfcErrNoSpaceLeft = 18
|
||||
AfcErrOperationWouldBlock = 19
|
||||
AfcErrIoError = 20
|
||||
AfcErrOperationInterrupted = 21
|
||||
AfcErrOperationInProgress = 22
|
||||
AfcErrInternalError = 23
|
||||
AfcErrMuxError = 30
|
||||
AfcErrNoMemory = 31
|
||||
AfcErrNotEnoughData = 32
|
||||
AfcErrDirNotEmpty = 33
|
||||
)
|
||||
@@ -1,138 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
type AuxBuffer struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewAuxBuffer() *AuxBuffer {
|
||||
return &AuxBuffer{
|
||||
buf: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendObject(obj interface{}) error {
|
||||
marshal, err := nskeyedarchiver.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.AppendUInt32(10)
|
||||
m.AppendUInt32(2)
|
||||
m.AppendUInt32(uint32(len(marshal)))
|
||||
m.buf.Write(marshal)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendInt64(v int64) {
|
||||
m.AppendUInt32(10)
|
||||
m.AppendUInt32(4)
|
||||
m.AppendUInt64(uint64(v))
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendInt32(v int32) {
|
||||
m.AppendUInt32(10)
|
||||
m.AppendUInt32(3)
|
||||
m.AppendUInt32(uint32(v))
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendUInt32(v uint32) {
|
||||
_ = binary.Write(m.buf, binary.LittleEndian, v)
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendUInt64(v uint64) {
|
||||
_ = binary.Write(m.buf, binary.LittleEndian, v)
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendBytes(b []byte) {
|
||||
m.buf.Write(b)
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) Len() int {
|
||||
return m.buf.Len()
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) Bytes() []byte {
|
||||
dup := m.buf.Bytes()
|
||||
b := make([]byte, 16)
|
||||
binary.LittleEndian.PutUint64(b, 0x01f0)
|
||||
binary.LittleEndian.PutUint64(b[8:], uint64(m.Len()))
|
||||
return append(b, dup...)
|
||||
}
|
||||
|
||||
func UnmarshalAuxBuffer(b []byte) ([]interface{}, error) {
|
||||
reader := bytes.NewReader(b)
|
||||
var magic, pkgLen uint64
|
||||
if err := binary.Read(reader, binary.LittleEndian, &magic); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(reader, binary.LittleEndian, &pkgLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if magic != 0x1df0 {
|
||||
// TODO magic
|
||||
// return nil, errors.New("magic not equal 0x1df0")
|
||||
// }
|
||||
|
||||
if pkgLen > uint64(len(b)-16) {
|
||||
return nil, errors.New("package length not enough")
|
||||
}
|
||||
|
||||
var ret []interface{}
|
||||
|
||||
for reader.Len() > 0 {
|
||||
var flag, typ uint32
|
||||
if err := binary.Read(reader, binary.LittleEndian, &flag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(reader, binary.LittleEndian, &typ); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch typ {
|
||||
case 2:
|
||||
var l uint32
|
||||
if err := binary.Read(reader, binary.LittleEndian, &l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plistBuf := make([]byte, l)
|
||||
if _, err := reader.Read(plistBuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
archiver := NewNSKeyedArchiver()
|
||||
d, err := archiver.Unmarshal(plistBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, d)
|
||||
case 3, 5:
|
||||
var i int32
|
||||
if err := binary.Read(reader, binary.LittleEndian, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
case 4, 6:
|
||||
var i int64
|
||||
if err := binary.Read(reader, binary.LittleEndian, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
case 10:
|
||||
// TODO Dictionary key
|
||||
// fmt.Println("Dictionary key!")
|
||||
continue
|
||||
default:
|
||||
// fmt.Printf("unknown type %d\n", typ)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
const (
|
||||
_unregistered = "_Golang-iDevice_Unregistered"
|
||||
_over = "_Golang-iDevice_Over"
|
||||
)
|
||||
|
||||
func newDtxMessageClient(innerConn InnerConn) *dtxMessageClient {
|
||||
c := &dtxMessageClient{
|
||||
innerConn: innerConn,
|
||||
msgID: 0,
|
||||
publishedChannels: make(map[string]int32),
|
||||
openedChannels: make(map[string]uint32),
|
||||
toReply: make(chan *dtxMessageHeaderPacket),
|
||||
|
||||
mu: sync.Mutex{},
|
||||
resultMap: make(map[interface{}]*DTXMessageResult),
|
||||
|
||||
callbackMap: make(map[string]func(m DTXMessageResult)),
|
||||
}
|
||||
c.RegisterCallback(_unregistered, func(m DTXMessageResult) {})
|
||||
c.RegisterCallback(_over, func(m DTXMessageResult) {})
|
||||
c.ctx, c.cancelFunc = context.WithCancel(context.Background())
|
||||
c.startReceive()
|
||||
c.startWaitingForReply()
|
||||
return c
|
||||
}
|
||||
|
||||
type dtxMessageClient struct {
|
||||
innerConn InnerConn
|
||||
msgID uint32
|
||||
|
||||
publishedChannels map[string]int32
|
||||
openedChannels map[string]uint32
|
||||
|
||||
toReply chan *dtxMessageHeaderPacket
|
||||
|
||||
mu sync.Mutex
|
||||
resultMap map[interface{}]*DTXMessageResult
|
||||
|
||||
callbackMap map[string]func(m DTXMessageResult)
|
||||
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) SendDTXMessage(selector string, aux []byte, channelCode uint32, expectsReply bool) (msgID uint32, err error) {
|
||||
payload := new(dtxMessagePayloadPacket)
|
||||
header := &dtxMessageHeaderPacket{
|
||||
ExpectsReply: 1,
|
||||
}
|
||||
|
||||
flag := 0x1000
|
||||
if !expectsReply {
|
||||
flag = 0
|
||||
header.ExpectsReply = 0
|
||||
}
|
||||
|
||||
var sel []byte
|
||||
if sel, err = nskeyedarchiver.Marshal(selector); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if aux == nil {
|
||||
aux = make([]byte, 0)
|
||||
}
|
||||
|
||||
payload.Flags = uint32(0x2 | flag)
|
||||
payload.AuxiliaryLength = uint32(len(aux))
|
||||
payload.TotalLength = uint64(len(aux)) + uint64(len(sel))
|
||||
|
||||
header.Magic = 0x1F3D5B79
|
||||
header.CB = uint32(unsafe.Sizeof(*header))
|
||||
header.FragmentId = 0
|
||||
header.FragmentCount = 1
|
||||
header.Length = uint32(unsafe.Sizeof(*payload)) + uint32(payload.TotalLength)
|
||||
c.msgID++
|
||||
header.Identifier = c.msgID
|
||||
header.ConversationIndex = 0
|
||||
header.ChannelCode = channelCode
|
||||
|
||||
msgPkt := new(dtxMessagePacket)
|
||||
msgPkt.Header = header
|
||||
msgPkt.Payload = payload
|
||||
msgPkt.Aux = aux
|
||||
msgPkt.Sel = sel
|
||||
|
||||
raw, err := msgPkt.Pack()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("--> %s\n", msgPkt))
|
||||
msgID = header.Identifier
|
||||
err = c.innerConn.Write(raw)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) ReceiveDTXMessage() (result *DTXMessageResult, err error) {
|
||||
bufPayload := new(bytes.Buffer)
|
||||
|
||||
var header *dtxMessageHeaderPacket = nil
|
||||
var needToReply *dtxMessageHeaderPacket = nil
|
||||
|
||||
for {
|
||||
header = new(dtxMessageHeaderPacket)
|
||||
|
||||
lenHeader := int(unsafe.Sizeof(*header))
|
||||
var bufHeader []byte
|
||||
if bufHeader, err = c.innerConn.Read(lenHeader); err != nil {
|
||||
return nil, fmt.Errorf("receive: length of DTXMessageHeader: %w", err)
|
||||
}
|
||||
|
||||
if header, err = header.unpack(bytes.NewBuffer(bufHeader)); err != nil {
|
||||
return nil, fmt.Errorf("receive: DTXMessageHeader unpack: %w", err)
|
||||
}
|
||||
|
||||
if header.ExpectsReply == 1 {
|
||||
needToReply = header
|
||||
}
|
||||
|
||||
if header.Magic != 0x1F3D5B79 {
|
||||
return nil, fmt.Errorf("receive: bad magic %x", header.Magic)
|
||||
}
|
||||
|
||||
if header.ConversationIndex == 1 {
|
||||
if header.Identifier != c.msgID {
|
||||
return nil, fmt.Errorf("receive: except identifier %d new identifier %d", c.msgID, header.Identifier)
|
||||
}
|
||||
} else if header.ConversationIndex == 0 {
|
||||
if header.Identifier > c.msgID {
|
||||
c.msgID = header.Identifier
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("receive: invalid conversationIndex %d", header.ConversationIndex)
|
||||
}
|
||||
|
||||
if header.FragmentId == 0 && header.FragmentCount > 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if data, err = c.innerConn.Read(int(header.Length)); err != nil {
|
||||
return nil, fmt.Errorf("receive: length of DTXMessageHeader: %w", err)
|
||||
}
|
||||
bufPayload.Write(data)
|
||||
|
||||
if header.FragmentId == header.FragmentCount-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
rawPayload := bufPayload.Bytes()
|
||||
payload := new(dtxMessagePayloadPacket)
|
||||
if payload, err = payload.unpack(bufPayload); err != nil {
|
||||
return nil, fmt.Errorf("receive: unpack DTXMessagePayload: %w", err)
|
||||
}
|
||||
|
||||
compress := (payload.Flags & 0xff000) >> 12
|
||||
if compress != 0 {
|
||||
return nil, fmt.Errorf("receive: message is compressed type %d", compress)
|
||||
}
|
||||
|
||||
payloadSize := uint32(unsafe.Sizeof(*payload))
|
||||
objOffset := uint64(payloadSize + payload.AuxiliaryLength)
|
||||
|
||||
var aux, obj []byte
|
||||
|
||||
// see https://github.com/electricbubble/gidevice/issues/28
|
||||
if r, l := payloadSize+payload.AuxiliaryLength, len(rawPayload); int(r) <= l {
|
||||
aux = rawPayload[payloadSize:r]
|
||||
} else {
|
||||
debugLog(fmt.Sprintf("<-- DTXMessage %s\n%s\n"+
|
||||
"[aux] bounds out of range [:%d] with capacity %d",
|
||||
header.String(), payload.String(),
|
||||
r, l,
|
||||
))
|
||||
}
|
||||
if r, l := objOffset+(payload.TotalLength-uint64(payload.AuxiliaryLength)), len(rawPayload); int(r) <= l {
|
||||
obj = rawPayload[objOffset:r]
|
||||
} else {
|
||||
debugLog(fmt.Sprintf("<-- DTXMessage %s\n%s\n"+
|
||||
"[obj] bounds out of range [:%d] with capacity %d",
|
||||
header.String(), payload.String(),
|
||||
r, l,
|
||||
))
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf(
|
||||
"<-- DTXMessage %s\n%s\n"+
|
||||
"%s\n%s\n",
|
||||
header.String(), payload.String(),
|
||||
hex.Dump(aux), hex.Dump(obj),
|
||||
))
|
||||
|
||||
result = new(DTXMessageResult)
|
||||
|
||||
if len(aux) > 0 {
|
||||
if aux, err := UnmarshalAuxBuffer(aux); err != nil {
|
||||
return nil, fmt.Errorf("receive: unpack AUX: %w", err)
|
||||
} else {
|
||||
result.Aux = aux
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) > 0 {
|
||||
if obj, err := NewNSKeyedArchiver().Unmarshal(obj); err != nil {
|
||||
return nil, fmt.Errorf("receive: unpack NSKeyedArchiver: %w", err)
|
||||
} else {
|
||||
result.Obj = obj
|
||||
}
|
||||
}
|
||||
|
||||
sObj, ok := result.Obj.(string)
|
||||
if fn, do := c.callbackMap[sObj]; do {
|
||||
fn(*result)
|
||||
} else {
|
||||
c.callbackMap[_unregistered](*result)
|
||||
}
|
||||
|
||||
if needToReply != nil {
|
||||
go func() { c.toReply <- needToReply }()
|
||||
} else {
|
||||
var sk interface{} = header.Identifier
|
||||
|
||||
if ok && sObj == "_notifyOfPublishedCapabilities:" {
|
||||
sk = "_notifyOfPublishedCapabilities:"
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.resultMap[sk] = result
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) Connection() (publishedChannels map[string]int32, err error) {
|
||||
args := NewAuxBuffer()
|
||||
if err = args.AppendObject(map[string]interface{}{
|
||||
"com.apple.private.DTXBlockCompression": uint64(2),
|
||||
"com.apple.private.DTXConnection": uint64(1),
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("connection DTXMessage: %w", err)
|
||||
}
|
||||
|
||||
selector := "_notifyOfPublishedCapabilities:"
|
||||
if _, err = c.SendDTXMessage(selector, args.Bytes(), 0, false); err != nil {
|
||||
return nil, fmt.Errorf("connection send: %w", err)
|
||||
}
|
||||
|
||||
var result *DTXMessageResult
|
||||
if result, err = c.GetResult(selector); err != nil {
|
||||
return nil, fmt.Errorf("connection receive: %w", err)
|
||||
}
|
||||
|
||||
if result.Obj.(string) != "_notifyOfPublishedCapabilities:" {
|
||||
return nil, fmt.Errorf("connection: response mismatch: %s", result.Obj)
|
||||
}
|
||||
|
||||
aux := result.Aux[0].(map[string]interface{})
|
||||
for k, v := range aux {
|
||||
c.publishedChannels[k] = int32(v.(uint64))
|
||||
}
|
||||
|
||||
return c.publishedChannels, nil
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) MakeChannel(channel string) (id uint32, err error) {
|
||||
var ok bool
|
||||
if id, ok = c.openedChannels[channel]; ok {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
id = uint32(len(c.openedChannels) + 1)
|
||||
args := NewAuxBuffer()
|
||||
args.AppendInt32(int32(id))
|
||||
if err = args.AppendObject(channel); err != nil {
|
||||
return 0, fmt.Errorf("make channel DTXMessage: %w", err)
|
||||
}
|
||||
|
||||
selector := "_requestChannelWithCode:identifier:"
|
||||
|
||||
var msgID uint32
|
||||
if msgID, err = c.SendDTXMessage(selector, args.Bytes(), 0, true); err != nil {
|
||||
return 0, fmt.Errorf("make channel send: %w", err)
|
||||
}
|
||||
|
||||
if _, err = c.GetResult(msgID); err != nil {
|
||||
return 0, fmt.Errorf("make channel receive: %w", err)
|
||||
}
|
||||
|
||||
c.openedChannels[channel] = id
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
|
||||
c.callbackMap[obj] = cb
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) GetResult(key interface{}) (*DTXMessageResult, error) {
|
||||
startTime := time.Now()
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
c.mu.Lock()
|
||||
if v, ok := c.resultMap[key]; ok {
|
||||
delete(c.resultMap, key)
|
||||
c.mu.Unlock()
|
||||
return v, nil
|
||||
} else {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
if elapsed := time.Since(startTime); elapsed > 30*time.Second {
|
||||
return nil, fmt.Errorf("dtx: get result: timeout after %v", elapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) Close() {
|
||||
c.cancelFunc()
|
||||
c.innerConn.Close()
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) startReceive() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
default:
|
||||
if _, err := c.ReceiveDTXMessage(); err != nil {
|
||||
debugLog(fmt.Sprintf("dtx: receive: %s", err))
|
||||
if strings.Contains(err.Error(), io.EOF.Error()) {
|
||||
c.cancelFunc()
|
||||
c.callbackMap[_over](DTXMessageResult{})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) startWaitingForReply() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case reqHeader := <-c.toReply:
|
||||
replyPayload := new(dtxMessagePayloadPacket)
|
||||
replyPayload.Flags = 0
|
||||
replyPayload.AuxiliaryLength = 0
|
||||
replyPayload.TotalLength = 0
|
||||
|
||||
replyHeader := new(dtxMessageHeaderPacket)
|
||||
replyHeader.Magic = 0x1F3D5B79
|
||||
replyHeader.CB = uint32(unsafe.Sizeof(*replyHeader))
|
||||
replyHeader.FragmentId = 0
|
||||
replyHeader.FragmentCount = 1
|
||||
replyHeader.Length = uint32(unsafe.Sizeof(*replyPayload)) + uint32(replyPayload.TotalLength)
|
||||
replyHeader.Identifier = reqHeader.Identifier
|
||||
replyHeader.ConversationIndex = reqHeader.ConversationIndex + 1
|
||||
replyHeader.ChannelCode = reqHeader.ChannelCode
|
||||
replyHeader.ExpectsReply = 0
|
||||
|
||||
replyPkt := new(dtxMessagePacket)
|
||||
replyPkt.Header = replyHeader
|
||||
replyPkt.Payload = replyPayload
|
||||
replyPkt.Aux = nil
|
||||
replyPkt.Sel = nil
|
||||
|
||||
raw, err := replyPkt.Pack()
|
||||
if err != nil {
|
||||
debugLog(fmt.Sprintf("pack: reply DTXMessage: %s", err))
|
||||
continue
|
||||
}
|
||||
|
||||
if err = c.innerConn.Write(raw); err != nil {
|
||||
debugLog(fmt.Sprintf("send: reply DTXMessage: %s", err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type DTXMessageResult struct {
|
||||
Obj interface{}
|
||||
Aux []interface{}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func newServicePacketClient(innerConn InnerConn) *servicePacketClient {
|
||||
return &servicePacketClient{
|
||||
innerConn: innerConn,
|
||||
}
|
||||
}
|
||||
|
||||
type servicePacketClient struct {
|
||||
innerConn InnerConn
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.newPacket(req, plist.XMLFormat)
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) NewBinaryPacket(req interface{}) (Packet, error) {
|
||||
return c.newPacket(req, plist.BinaryFormat)
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) newPacket(req interface{}, format int) (Packet, error) {
|
||||
pkt := new(servicePacket)
|
||||
if buf, err := plist.Marshal(req, format); err != nil {
|
||||
return nil, fmt.Errorf("plist packet marshal: %w", err)
|
||||
} else {
|
||||
pkt.body = buf
|
||||
}
|
||||
pkt.length = uint32(len(pkt.body))
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) SendPacket(pkt Packet) (err error) {
|
||||
var raw []byte
|
||||
if raw, err = pkt.Pack(); err != nil {
|
||||
return fmt.Errorf("send packet: %w", err)
|
||||
}
|
||||
debugLog(fmt.Sprintf("--> %s\n", pkt))
|
||||
return c.innerConn.Write(raw)
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
lenPkg := binary.BigEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.innerConn.Read(int(lenPkg)); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
var reply LockdownBasicResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
|
||||
if reply.Error != "" {
|
||||
return nil, fmt.Errorf("receive packet: %s", reply.Error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
CrashReportMoverServiceName = "com.apple.crashreportmover"
|
||||
CrashReportCopyMobileServiceName = "com.apple.crashreportcopymobile"
|
||||
)
|
||||
|
||||
func NewCrashReportMoverClient(innerConn InnerConn) *CrashReportMoverClient {
|
||||
return &CrashReportMoverClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type CrashReportMoverClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *CrashReportMoverClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
DiagnosticsRelayServiceName = "com.apple.mobile.diagnostics_relay"
|
||||
)
|
||||
|
||||
type DiagnosticsRelayBasicRequest struct {
|
||||
Request string `plist:"Request"`
|
||||
Label string `plist:"Label"`
|
||||
}
|
||||
|
||||
func NewDiagnosticsRelayClient(innerConn InnerConn) *DiagnosticsRelayClient {
|
||||
return &DiagnosticsRelayClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type DiagnosticsRelayClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) NewBasicRequest(relayType string) *DiagnosticsRelayBasicRequest {
|
||||
return &DiagnosticsRelayBasicRequest{
|
||||
Request: relayType,
|
||||
Label: BundleID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const HouseArrestServiceName = "com.apple.mobile.house_arrest"
|
||||
|
||||
const (
|
||||
CommandTypeVendDocuments CommandType = "VendDocuments"
|
||||
CommandTypeVendContainer CommandType = "VendContainer"
|
||||
)
|
||||
|
||||
func NewHouseArrestClient(innerConn InnerConn) *HouseArrestClient {
|
||||
return &HouseArrestClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type HouseArrestClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewBasicRequest(cmdType CommandType, bundleID string) *HouseArrestBasicRequest {
|
||||
return &HouseArrestBasicRequest{
|
||||
Command: cmdType,
|
||||
Identifier: bundleID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewDocumentsRequest(bundleID string) *HouseArrestBasicRequest {
|
||||
return c.NewBasicRequest(CommandTypeVendDocuments, bundleID)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewContainerRequest(bundleID string) *HouseArrestBasicRequest {
|
||||
return c.NewBasicRequest(CommandTypeVendContainer, bundleID)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
type (
|
||||
HouseArrestBasicRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
Identifier string `plist:"Identifier"`
|
||||
}
|
||||
)
|
||||
@@ -1,107 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const ImageMounterServiceName = "com.apple.mobile.mobile_image_mounter"
|
||||
|
||||
var ErrDeviceLocked = errors.New("device locked")
|
||||
|
||||
type CommandType string
|
||||
|
||||
const (
|
||||
CommandTypeLookupImage CommandType = "LookupImage"
|
||||
CommandTypeReceiveBytes CommandType = "ReceiveBytes"
|
||||
CommandTypeMountImage CommandType = "MountImage"
|
||||
)
|
||||
|
||||
func NewImageMounterClient(innerConn InnerConn) *ImageMounterClient {
|
||||
return &ImageMounterClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type ImageMounterClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewBasicRequest(cmdType CommandType, imgType string) *ImageMounterBasicRequest {
|
||||
return &ImageMounterBasicRequest{
|
||||
Command: cmdType,
|
||||
ImageType: imgType,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewReceiveBytesRequest(imgType string, imgSize uint32, imgSignature []byte) *ImageMounterReceiveBytesRequest {
|
||||
return &ImageMounterReceiveBytesRequest{
|
||||
ImageMounterBasicRequest: *c.NewBasicRequest(CommandTypeReceiveBytes, imgType),
|
||||
ImageSize: imgSize,
|
||||
ImageSignature: imgSignature,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewMountImageRequest(imgType, imgPath string, imgSignature []byte) *ImageMounterMountImageRequest {
|
||||
return &ImageMounterMountImageRequest{
|
||||
ImageMounterBasicRequest: *c.NewBasicRequest(CommandTypeMountImage, imgType),
|
||||
ImagePath: imgPath,
|
||||
ImageSignature: imgSignature,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
respPkt, err = c.client.ReceivePacket()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), io.EOF.Error()) {
|
||||
return nil, ErrDeviceLocked
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) SendDmg(data []byte) (err error) {
|
||||
debugLog(fmt.Sprintf("--> ...DmgData...\n"))
|
||||
return c.client.innerConn.Write(data)
|
||||
}
|
||||
|
||||
type (
|
||||
ImageMounterBasicRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
ImageType string `plist:"ImageType"`
|
||||
}
|
||||
|
||||
ImageMounterReceiveBytesRequest struct {
|
||||
ImageMounterBasicRequest
|
||||
ImageSignature []byte `plist:"ImageSignature"`
|
||||
ImageSize uint32 `plist:"ImageSize"`
|
||||
}
|
||||
|
||||
ImageMounterMountImageRequest struct {
|
||||
ImageMounterBasicRequest
|
||||
ImagePath string `plist:"ImagePath"`
|
||||
ImageSignature []byte `plist:"ImageSignature"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
ImageMounterBasicResponse struct {
|
||||
LockdownBasicResponse
|
||||
Status string `plist:"Status"`
|
||||
}
|
||||
|
||||
ImageMounterLookupImageResponse struct {
|
||||
ImageMounterBasicResponse
|
||||
ImageSignature [][]byte `plist:"ImageSignature"`
|
||||
}
|
||||
)
|
||||
@@ -1,119 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const InstallationProxyServiceName = "com.apple.mobile.installation_proxy"
|
||||
|
||||
const (
|
||||
CommandTypeBrowse CommandType = "Browse"
|
||||
CommandTypeLookup CommandType = "Lookup"
|
||||
CommandTypeInstall CommandType = "Install"
|
||||
CommandTypeUninstall CommandType = "Uninstall"
|
||||
)
|
||||
|
||||
type ApplicationType string
|
||||
|
||||
const (
|
||||
ApplicationTypeSystem ApplicationType = "System"
|
||||
ApplicationTypeUser ApplicationType = "User"
|
||||
ApplicationTypeInternal ApplicationType = "internal"
|
||||
ApplicationTypeAny ApplicationType = "Any"
|
||||
)
|
||||
|
||||
func NewInstallationProxyClient(innerConn InnerConn) *InstallationProxyClient {
|
||||
return &InstallationProxyClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type InstallationProxyClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewBasicRequest(cmdType CommandType, opt *InstallationProxyOption) *InstallationProxyBasicRequest {
|
||||
req := &InstallationProxyBasicRequest{Command: cmdType}
|
||||
if opt != nil {
|
||||
req.ClientOptions = opt
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewInstallRequest(bundleID, packagePath string) *InstallationProxyInstallRequest {
|
||||
opt := &InstallationProxyOption{
|
||||
BundleID: bundleID,
|
||||
}
|
||||
req := &InstallationProxyInstallRequest{
|
||||
Command: CommandTypeInstall,
|
||||
ClientOptions: opt,
|
||||
PackagePath: packagePath,
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewUninstallRequest(bundleID string) *InstallationProxyUninstallRequest {
|
||||
req := &InstallationProxyUninstallRequest{
|
||||
Command: CommandTypeUninstall,
|
||||
BundleID: bundleID,
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
type InstallationProxyOption struct {
|
||||
ApplicationType ApplicationType `plist:"ApplicationType,omitempty"`
|
||||
ReturnAttributes []string `plist:"ReturnAttributes,omitempty"`
|
||||
MetaData bool `plist:"com.apple.mobile_installation.metadata,omitempty"`
|
||||
BundleIDs []string `plist:"BundleIDs,omitempty"` // for Lookup
|
||||
BundleID string `plist:"CFBundleIdentifier,omitempty"` // for Install
|
||||
}
|
||||
|
||||
type (
|
||||
InstallationProxyBasicRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
ClientOptions *InstallationProxyOption `plist:"ClientOptions,omitempty"`
|
||||
}
|
||||
|
||||
InstallationProxyInstallRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
ClientOptions *InstallationProxyOption `plist:"ClientOptions"`
|
||||
PackagePath string `plist:"PackagePath"`
|
||||
}
|
||||
|
||||
InstallationProxyUninstallRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
BundleID string `plist:"ApplicationIdentifier"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
InstallationProxyBasicResponse struct {
|
||||
Status string `plist:"Status"`
|
||||
}
|
||||
|
||||
InstallationProxyLookupResponse struct {
|
||||
InstallationProxyBasicResponse
|
||||
LookupResult interface{} `plist:"LookupResult"`
|
||||
}
|
||||
|
||||
InstallationProxyBrowseResponse struct {
|
||||
InstallationProxyBasicResponse
|
||||
CurrentAmount int `plist:"CurrentAmount"`
|
||||
CurrentIndex int `plist:"CurrentIndex"`
|
||||
CurrentList []interface{} `plist:"CurrentList"`
|
||||
}
|
||||
|
||||
InstallationProxyInstallResponse struct {
|
||||
InstallationProxyBasicResponse
|
||||
Error string `plist:"Error"`
|
||||
ErrorDescription string `plist:"ErrorDescription"`
|
||||
}
|
||||
)
|
||||
@@ -1,41 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
InstrumentsServiceName = "com.apple.instruments.remoteserver"
|
||||
InstrumentsSecureProxyServiceName = "com.apple.instruments.remoteserver.DVTSecureSocketProxy"
|
||||
)
|
||||
|
||||
func NewInstrumentsClient(innerConn InnerConn) *InstrumentsClient {
|
||||
return &InstrumentsClient{
|
||||
client: newDtxMessageClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type InstrumentsClient struct {
|
||||
client *dtxMessageClient
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) NotifyOfPublishedCapabilities() (publishedChannels map[string]int32, err error) {
|
||||
return c.client.Connection()
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) RequestChannel(channel string) (id uint32, err error) {
|
||||
return c.client.MakeChannel(channel)
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) Invoke(selector string, args *AuxBuffer, channelCode uint32, expectsReply bool) (result *DTXMessageResult, err error) {
|
||||
var msgID uint32
|
||||
if msgID, err = c.client.SendDTXMessage(selector, args.Bytes(), channelCode, expectsReply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expectsReply {
|
||||
if result, err = c.client.GetResult(msgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
|
||||
c.client.RegisterCallback(obj, cb)
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
const nsNull = "$null"
|
||||
|
||||
func newKeyedArchiver() *KeyedArchiver {
|
||||
return &KeyedArchiver{
|
||||
Archiver: "NSKeyedArchiver",
|
||||
Version: 100000,
|
||||
}
|
||||
}
|
||||
|
||||
type KeyedArchiver struct {
|
||||
Archiver string `plist:"$archiver"`
|
||||
Objects []interface{} `plist:"$objects"`
|
||||
Top ArchiverRoot `plist:"$top"`
|
||||
Version int `plist:"$version"`
|
||||
}
|
||||
|
||||
func (ka *KeyedArchiver) UID() plist.UID {
|
||||
return plist.UID(len(ka.Objects))
|
||||
}
|
||||
|
||||
type ArchiverRoot struct {
|
||||
Root plist.UID `plist:"root"`
|
||||
}
|
||||
|
||||
type ArchiverClasses struct {
|
||||
Classes []string `plist:"$classes"`
|
||||
ClassName string `plist:"$classname"`
|
||||
}
|
||||
|
||||
var (
|
||||
NSMutableDictionaryClass = &ArchiverClasses{
|
||||
Classes: []string{"NSMutableDictionary", "NSDictionary", "NSObject"},
|
||||
ClassName: "NSMutableDictionary",
|
||||
}
|
||||
NSDictionaryClass = &ArchiverClasses{
|
||||
Classes: []string{"NSDictionary", "NSObject"},
|
||||
ClassName: "NSDictionary",
|
||||
}
|
||||
NSMutableArrayClass = &ArchiverClasses{
|
||||
Classes: []string{"NSMutableArray", "NSArray", "NSObject"},
|
||||
ClassName: "NSMutableArray",
|
||||
}
|
||||
NSArrayClass = &ArchiverClasses{
|
||||
Classes: []string{"NSArray", "NSObject"},
|
||||
ClassName: "NSArray",
|
||||
}
|
||||
NSMutableDataClass = &ArchiverClasses{
|
||||
Classes: []string{"NSMutableArray", "NSArray", "NSObject"},
|
||||
ClassName: "NSMutableArray",
|
||||
}
|
||||
NSDataClass = &ArchiverClasses{
|
||||
Classes: []string{"NSData", "NSObject"},
|
||||
ClassName: "NSData",
|
||||
}
|
||||
NSDateClass = &ArchiverClasses{
|
||||
Classes: []string{"NSDate", "NSObject"},
|
||||
ClassName: "NSDate",
|
||||
}
|
||||
NSErrorClass = &ArchiverClasses{
|
||||
Classes: []string{"NSError", "NSObject"},
|
||||
ClassName: "NSError",
|
||||
}
|
||||
)
|
||||
|
||||
type NSObject struct {
|
||||
Class plist.UID `plist:"$class"`
|
||||
}
|
||||
|
||||
type NSArray struct {
|
||||
NSObject
|
||||
Values []plist.UID `plist:"NS.objects"`
|
||||
}
|
||||
|
||||
type NSDictionary struct {
|
||||
NSArray
|
||||
Keys []plist.UID `plist:"NS.keys"`
|
||||
}
|
||||
|
||||
type NSData struct {
|
||||
NSObject
|
||||
Data []byte `plist:"NS.data"`
|
||||
}
|
||||
|
||||
type NSError struct {
|
||||
NSCode int
|
||||
NSDomain string
|
||||
NSUserInfo interface{}
|
||||
}
|
||||
|
||||
type NSKeyedArchiver struct {
|
||||
objRefVal []interface{}
|
||||
objRef map[interface{}]plist.UID
|
||||
}
|
||||
|
||||
func NewNSKeyedArchiver() *NSKeyedArchiver {
|
||||
return &NSKeyedArchiver{
|
||||
objRef: make(map[interface{}]plist.UID),
|
||||
}
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) id(v interface{}) plist.UID {
|
||||
var ref plist.UID
|
||||
if id, ok := ka.objRef[v]; !ok {
|
||||
ref = plist.UID(len(ka.objRef))
|
||||
ka.objRefVal = append(ka.objRefVal, v)
|
||||
ka.objRef[v] = ref
|
||||
} else {
|
||||
ref = id
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) flushToStruct(root *KeyedArchiver) {
|
||||
for i := 0; i < len(ka.objRefVal); i++ {
|
||||
val := ka.objRefVal[i]
|
||||
vt := reflect.ValueOf(val)
|
||||
if vt.Kind() == reflect.Ptr {
|
||||
val = vt.Elem().Interface()
|
||||
}
|
||||
root.Objects = append(root.Objects, val)
|
||||
}
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) clear() {
|
||||
ka.objRef = make(map[interface{}]plist.UID)
|
||||
ka.objRefVal = []interface{}{}
|
||||
}
|
||||
|
||||
type XCTestConfiguration struct {
|
||||
Contents map[string]interface{}
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) Marshal(obj interface{}) ([]byte, error) {
|
||||
val := reflect.ValueOf(obj)
|
||||
typ := val.Type()
|
||||
|
||||
root := newKeyedArchiver()
|
||||
|
||||
var tmpTop plist.UID
|
||||
|
||||
ka.id(nsNull)
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
m := &NSDictionary{}
|
||||
m.Class = ka.id(NSDictionaryClass)
|
||||
keys := val.MapKeys()
|
||||
for _, v := range keys {
|
||||
m.Keys = append(m.Keys, ka.id(v.Interface()))
|
||||
m.Values = append(m.Values, ka.id(val.MapIndex(v).Interface()))
|
||||
}
|
||||
tmpTop = ka.id(m)
|
||||
case reflect.Slice, reflect.Array:
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
d := &NSData{}
|
||||
d.Class = ka.id(NSDataClass)
|
||||
var w []byte
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
w = append(w, uint8(val.Index(i).Uint()))
|
||||
}
|
||||
d.Data = w
|
||||
}
|
||||
a := &NSArray{}
|
||||
a.Class = ka.id(NSArrayClass)
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
a.Values = append(a.Values, ka.id(val.Index(i).Interface()))
|
||||
}
|
||||
tmpTop = ka.id(a)
|
||||
case reflect.String:
|
||||
tmpTop = ka.id(obj)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
tmpTop = ka.id(obj)
|
||||
}
|
||||
|
||||
root.Top.Root = tmpTop
|
||||
|
||||
ka.flushToStruct(root)
|
||||
|
||||
ka.clear()
|
||||
|
||||
return plist.Marshal(root, plist.BinaryFormat)
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) convertValue(v interface{}) interface{} {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
className := ka.objRefVal[m["$class"].(plist.UID)].(map[string]interface{})["$classname"]
|
||||
|
||||
switch className {
|
||||
case NSMutableDictionaryClass.Classes[0], NSDictionaryClass.Classes[0]:
|
||||
ret := make(map[string]interface{})
|
||||
keys := m["NS.keys"].([]interface{})
|
||||
values := m["NS.objects"].([]interface{})
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
var keyValue string
|
||||
key := ka.objRefVal[keys[i].(plist.UID)]
|
||||
switch key.(type) {
|
||||
case uint64:
|
||||
keyValue = strconv.Itoa(int(key.(uint64)))
|
||||
break
|
||||
default:
|
||||
keyValue = key.(string)
|
||||
}
|
||||
val := ka.convertValue(ka.objRefVal[values[i].(plist.UID)])
|
||||
ret[keyValue] = val
|
||||
}
|
||||
return ret
|
||||
case NSMutableArrayClass.Classes[0], NSArrayClass.Classes[0]:
|
||||
ret := make([]interface{}, 0)
|
||||
values := m["NS.objects"].([]interface{})
|
||||
for i := 0; i < len(values); i++ {
|
||||
ret = append(ret, ka.convertValue(values[i]))
|
||||
}
|
||||
return ret
|
||||
case NSMutableDataClass.Classes[0], NSDataClass.Classes[0]:
|
||||
return m["NS.data"].([]byte)
|
||||
case NSDateClass.Classes[0]:
|
||||
return time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC).
|
||||
Add(time.Duration(m["NS.time"].(float64)) * time.Second)
|
||||
case NSErrorClass.Classes[0]:
|
||||
err := &NSError{}
|
||||
err.NSCode = int(m["NSCode"].(uint64))
|
||||
err.NSDomain = ka.objRefVal[m["NSDomain"].(plist.UID)].(string)
|
||||
err.NSUserInfo = ka.convertValue(ka.objRefVal[m["NSUserInfo"].(plist.UID)])
|
||||
return *err
|
||||
}
|
||||
} else if uid, ok := v.(plist.UID); ok {
|
||||
return ka.convertValue(ka.objRefVal[uid])
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) Unmarshal(b []byte) (interface{}, error) {
|
||||
archiver := new(KeyedArchiver)
|
||||
|
||||
if _, err := plist.Unmarshal(b, archiver); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range archiver.Objects {
|
||||
ka.objRefVal = append(ka.objRefVal, v)
|
||||
}
|
||||
|
||||
ret := ka.convertValue(ka.objRefVal[archiver.Top.Root])
|
||||
|
||||
ka.clear()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Packet interface {
|
||||
Pack() ([]byte, error)
|
||||
Unpack(buffer *bytes.Buffer) (Packet, error)
|
||||
Unmarshal(v interface{}) error
|
||||
|
||||
String() string
|
||||
}
|
||||
|
||||
var debugFlag = false
|
||||
|
||||
// SetDebug sets debug mode
|
||||
func SetDebug(debug bool) {
|
||||
debugFlag = debug
|
||||
}
|
||||
|
||||
func debugLog(msg string) {
|
||||
if !debugFlag {
|
||||
return
|
||||
}
|
||||
log.Printf("[%s-debug] %s\n", ProgramName, msg)
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const ProtocolVersion = "2"
|
||||
|
||||
const LockdownPort = 62078
|
||||
|
||||
type RequestType string
|
||||
|
||||
const (
|
||||
RequestTypeQueryType RequestType = "QueryType"
|
||||
RequestTypeSetValue RequestType = "SetValue"
|
||||
RequestTypeGetValue RequestType = "GetValue"
|
||||
RequestTypePair RequestType = "Pair"
|
||||
RequestTypeEnterRecovery RequestType = "EnterRecovery"
|
||||
RequestTypeStartSession RequestType = "StartSession"
|
||||
RequestTypeStopSession RequestType = "StopSession"
|
||||
RequestTypeStartService RequestType = "StartService"
|
||||
)
|
||||
|
||||
type LockdownType struct {
|
||||
Type string `plist:"Type"`
|
||||
}
|
||||
|
||||
func NewLockdownClient(innerConn InnerConn) *LockdownClient {
|
||||
return &LockdownClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type LockdownClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewBasicRequest(reqType RequestType) *LockdownBasicRequest {
|
||||
return &LockdownBasicRequest{
|
||||
Label: BundleID,
|
||||
ProtocolVersion: ProtocolVersion,
|
||||
Request: reqType,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewGetValueRequest(domain, key string) *LockdownValueRequest {
|
||||
return &LockdownValueRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeGetValue),
|
||||
Domain: domain,
|
||||
Key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewSetValueRequest(domain, key string, value interface{}) *LockdownValueRequest {
|
||||
return &LockdownValueRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeSetValue),
|
||||
Domain: domain,
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewEnterRecoveryRequest() *LockdownBasicRequest {
|
||||
return c.NewBasicRequest(RequestTypeEnterRecovery)
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewPairRequest(pairRecord *PairRecord) *LockdownPairRequest {
|
||||
return &LockdownPairRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypePair),
|
||||
PairRecord: pairRecord,
|
||||
PairingOptions: map[string]interface{}{
|
||||
"ExtendedPairingErrors": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewStartSessionRequest(buid, hostID string) *LockdownStartSessionRequest {
|
||||
return &LockdownStartSessionRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStartSession),
|
||||
SystemBUID: buid,
|
||||
HostID: hostID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewStopSessionRequest(sessionID string) *LockdownStopSessionRequest {
|
||||
return &LockdownStopSessionRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStopSession),
|
||||
SessionID: sessionID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewStartServiceRequest(service string) *LockdownStartServiceRequest {
|
||||
return &LockdownStartServiceRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStartService),
|
||||
Service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *LockdownClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *LockdownClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
func (c *LockdownClient) EnableSSL(version []int, pairRecord *PairRecord) (err error) {
|
||||
return c.client.innerConn.Handshake(version, pairRecord)
|
||||
}
|
||||
|
||||
type (
|
||||
LockdownBasicRequest struct {
|
||||
Label string `plist:"Label"`
|
||||
ProtocolVersion string `plist:"ProtocolVersion"`
|
||||
Request RequestType `plist:"Request"`
|
||||
}
|
||||
|
||||
LockdownValueRequest struct {
|
||||
LockdownBasicRequest
|
||||
Domain string `plist:"Domain,omitempty"`
|
||||
Key string `plist:"Key,omitempty"`
|
||||
Value interface{} `plist:"Value,omitempty"`
|
||||
}
|
||||
|
||||
LockdownPairRequest struct {
|
||||
LockdownBasicRequest
|
||||
PairRecord *PairRecord `plist:"PairRecord"`
|
||||
PairingOptions map[string]interface{} `plist:"PairingOptions"`
|
||||
}
|
||||
|
||||
LockdownStartSessionRequest struct {
|
||||
LockdownBasicRequest
|
||||
SystemBUID string `plist:"SystemBUID"`
|
||||
HostID string `plist:"HostID"`
|
||||
}
|
||||
|
||||
LockdownStopSessionRequest struct {
|
||||
LockdownBasicRequest
|
||||
SessionID string `plist:"SessionID"`
|
||||
}
|
||||
|
||||
LockdownStartServiceRequest struct {
|
||||
LockdownBasicRequest
|
||||
Service string `plist:"Service"`
|
||||
EscrowBag []byte `plist:"EscrowBag,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
LockdownBasicResponse struct {
|
||||
Request string `plist:"Request"`
|
||||
Error string `plist:"Error"`
|
||||
}
|
||||
|
||||
LockdownTypeResponse struct {
|
||||
LockdownBasicResponse
|
||||
Type string `plist:"Type"`
|
||||
}
|
||||
|
||||
LockdownValueResponse struct {
|
||||
LockdownBasicResponse
|
||||
Key string `plist:"Key"`
|
||||
Value interface{} `plist:"Value"`
|
||||
}
|
||||
|
||||
LockdownPairResponse struct {
|
||||
LockdownBasicResponse
|
||||
EscrowBag []byte `plist:"EscrowBag"`
|
||||
}
|
||||
|
||||
LockdownStartSessionResponse struct {
|
||||
LockdownBasicResponse
|
||||
EnableSessionSSL bool `plist:"EnableSessionSSL"`
|
||||
SessionID string `plist:"SessionID"`
|
||||
}
|
||||
|
||||
LockdownStartServiceResponse struct {
|
||||
LockdownBasicResponse
|
||||
EnableServiceSSL bool `plist:"EnableServiceSSL"`
|
||||
Port int `plist:"Port"`
|
||||
Service string `plist:"Service"`
|
||||
}
|
||||
)
|
||||
@@ -1,86 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var afcHeader = []byte{0x43, 0x46, 0x41, 0x36, 0x4C, 0x50, 0x41, 0x41}
|
||||
|
||||
var _ Packet = (*afcPacket)(nil)
|
||||
|
||||
type afcPacket struct {
|
||||
entireLen uint64
|
||||
thisLen uint64
|
||||
packetNum uint64
|
||||
operation uint64
|
||||
}
|
||||
|
||||
func (p *afcPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(afcHeader)
|
||||
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, p.entireLen)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, p.thisLen)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, p.packetNum)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, p.operation)
|
||||
buf.Write(b)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *afcPacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
|
||||
return p.unpack(buffer)
|
||||
}
|
||||
|
||||
func (p *afcPacket) unpack(buffer *bytes.Buffer) (*afcPacket, error) {
|
||||
magic := make([]byte, 8)
|
||||
if _, err := buffer.Read(magic); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if bytes.Compare(magic, afcHeader) != 0 {
|
||||
return nil, errors.New("afc packet unpack: header not match")
|
||||
}
|
||||
|
||||
respPkt := new(afcPacket)
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.entireLen); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.thisLen); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.packetNum); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.operation); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *afcPacket) Unmarshal(v interface{}) error {
|
||||
// switch msg := v.(type) {
|
||||
// case *AfcMessage:
|
||||
// // msg.EntireLen = p.entireLen
|
||||
// // msg.ThisLen = p.thisLen
|
||||
// // msg.PacketNum = p.packetNum
|
||||
// msg.Operation = p.operation
|
||||
// default:
|
||||
// return errors.New("the type of the method parameter must be '*AfcMessage'")
|
||||
// }
|
||||
// return nil
|
||||
panic("never use (afcPacket)")
|
||||
}
|
||||
|
||||
func (p *afcPacket) String() string {
|
||||
return fmt.Sprintf(
|
||||
"EntireLen: %d, ThisLen: %d, PacketNum: %d, Operation: %X\n",
|
||||
p.entireLen, p.thisLen, p.packetNum, p.operation,
|
||||
)
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Packet = (*dtxMessagePayloadPacket)(nil)
|
||||
_ Packet = (*dtxMessageHeaderPacket)(nil)
|
||||
_ Packet = (*dtxMessagePacket)(nil)
|
||||
)
|
||||
|
||||
type dtxMessagePayloadPacket struct {
|
||||
Flags uint32
|
||||
AuxiliaryLength uint32
|
||||
TotalLength uint64
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.Flags)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.AuxiliaryLength)
|
||||
buf.Write(b)
|
||||
|
||||
b = make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, p.TotalLength)
|
||||
buf.Write(b)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
return p.unpack(buffer)
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) unpack(buffer *bytes.Buffer) (pkt *dtxMessagePayloadPacket, err error) {
|
||||
respPkt := new(dtxMessagePayloadPacket)
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Flags); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.AuxiliaryLength); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.TotalLength); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
|
||||
}
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (dtxMessagePayloadHeader)")
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) String() string {
|
||||
return fmt.Sprintf("DTXMessagePayloadHeader Flags: %d, AuxiliaryLength: %d, TotalLength: %d\n",
|
||||
p.Flags, p.AuxiliaryLength, p.TotalLength,
|
||||
)
|
||||
}
|
||||
|
||||
type dtxMessageHeaderPacket struct {
|
||||
Magic uint32
|
||||
CB uint32
|
||||
FragmentId uint16
|
||||
FragmentCount uint16
|
||||
Length uint32
|
||||
Identifier uint32
|
||||
ConversationIndex uint32
|
||||
ChannelCode uint32
|
||||
ExpectsReply uint32
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.Magic)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.CB)
|
||||
buf.Write(b)
|
||||
|
||||
b = make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, p.FragmentId)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint16(b, p.FragmentCount)
|
||||
buf.Write(b)
|
||||
|
||||
b = make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.Length)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.Identifier)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.ConversationIndex)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.ChannelCode)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.ExpectsReply)
|
||||
buf.Write(b)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
return p.unpack(buffer)
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) unpack(buffer *bytes.Buffer) (pkt *dtxMessageHeaderPacket, err error) {
|
||||
respPkt := new(dtxMessageHeaderPacket)
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Magic); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.CB); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.FragmentId); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.FragmentCount); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Length); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Identifier); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ConversationIndex); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ChannelCode); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ExpectsReply); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (DTXMessageHeader)")
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) String() string {
|
||||
return fmt.Sprintf("DTXMessageHeader Magic: %d, CB: %d, FragmentId: %d, FragmentCount: %d\n"+
|
||||
"Length: %d, Identifier: %d, ConversationIndex: %d, ChannelCode: %d, ExpectsReply: %d\n",
|
||||
p.Magic, p.CB, p.FragmentId, p.FragmentCount,
|
||||
p.Length, p.Identifier, p.ConversationIndex, p.ChannelCode, p.ExpectsReply,
|
||||
)
|
||||
}
|
||||
|
||||
type dtxMessagePacket struct {
|
||||
Header *dtxMessageHeaderPacket
|
||||
Payload *dtxMessagePayloadPacket
|
||||
Aux []byte
|
||||
Sel []byte
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
raw, err := p.Header.Pack()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePacket) pack: %w", err)
|
||||
}
|
||||
buf.Write(raw)
|
||||
|
||||
if raw, err = p.Payload.Pack(); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePacket) pack: %w", err)
|
||||
}
|
||||
buf.Write(raw)
|
||||
|
||||
if p.Aux != nil || len(p.Aux) != 0 {
|
||||
buf.Write(p.Aux)
|
||||
}
|
||||
if p.Sel != nil || len(p.Sel) != 0 {
|
||||
buf.Write(p.Sel)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (DTXMessagePacket)")
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) String() string {
|
||||
return fmt.Sprintf(
|
||||
"DTXMessagePacket %s\n%s\n"+
|
||||
"%s\n%s\n",
|
||||
p.Header.String(), p.Payload.String(),
|
||||
// p.Aux, p.Sel,
|
||||
hex.Dump(p.Aux), hex.Dump(p.Sel),
|
||||
)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var _ Packet = (*locationPacket)(nil)
|
||||
|
||||
type locationPacket struct {
|
||||
lon float64
|
||||
lat float64
|
||||
}
|
||||
|
||||
func (l *locationPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(0)); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
|
||||
latS := []byte(strconv.FormatFloat(l.lat, 'E', -1, 64))
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(len(latS))); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
if err := binary.Write(buf, binary.BigEndian, latS); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
|
||||
lonS := []byte(strconv.FormatFloat(l.lon, 'E', -1, 64))
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(len(lonS))); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
if err := binary.Write(buf, binary.BigEndian, lonS); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (l *locationPacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
|
||||
panic("never use (location)")
|
||||
}
|
||||
|
||||
func (l *locationPacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (location)")
|
||||
}
|
||||
|
||||
func (l *locationPacket) String() string {
|
||||
return fmt.Sprintf("lon: %v, lat: %v\n", l.lon, l.lat)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var _ Packet = (*servicePacket)(nil)
|
||||
|
||||
type servicePacket struct {
|
||||
length uint32
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (p *servicePacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, p.length)
|
||||
buf.Write(b)
|
||||
|
||||
buf.Write(p.body)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *servicePacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
respPkt := new(servicePacket)
|
||||
if err = binary.Read(buffer, binary.BigEndian, &respPkt.length); err != nil {
|
||||
return nil, fmt.Errorf("packet (service) unpack: %w", err)
|
||||
}
|
||||
respPkt.body = buffer.Bytes()
|
||||
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *servicePacket) Unmarshal(v interface{}) (err error) {
|
||||
_, err = plist.Unmarshal(p.body, v)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *servicePacket) String() string {
|
||||
return fmt.Sprintf("Length: %d\n%s", p.length, p.body)
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var _ Packet = (*packet)(nil)
|
||||
|
||||
type packet struct {
|
||||
length uint32
|
||||
version ProtoVersion
|
||||
msgType ProtoMessageType
|
||||
tag uint32
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (p *packet) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.length)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, uint32(p.version))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, uint32(p.msgType))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.tag)
|
||||
buf.Write(b)
|
||||
|
||||
buf.Write(p.body)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *packet) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
respPkt := new(packet)
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.length); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.version); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.msgType); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.tag); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
respPkt.body = buffer.Bytes()
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *packet) Unmarshal(v interface{}) (err error) {
|
||||
_, err = plist.Unmarshal(p.body, v)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *packet) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Length: %d, Version: %d, Type: %d, Tag: %d\n%s",
|
||||
p.length, p.version, p.msgType, p.tag, p.body,
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
BasicRequest struct {
|
||||
MessageType MessageType `plist:"MessageType"`
|
||||
BundleID string `plist:"BundleID,omitempty"`
|
||||
ProgramName string `plist:"ProgName,omitempty"`
|
||||
ClientVersionString string `plist:"ClientVersionString"`
|
||||
LibUSBMuxVersion uint `plist:"kLibUSBMuxVersion"`
|
||||
}
|
||||
|
||||
ConnectRequest struct {
|
||||
BasicRequest
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
PortNumber int `plist:"PortNumber"`
|
||||
}
|
||||
|
||||
ReadPairRecordRequest struct {
|
||||
BasicRequest
|
||||
PairRecordID string `plist:"PairRecordID"`
|
||||
}
|
||||
|
||||
SavePairRecordRequest struct {
|
||||
BasicRequest
|
||||
PairRecordID string `plist:"PairRecordID"`
|
||||
PairRecordData []byte `plist:"PairRecordData"`
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
}
|
||||
|
||||
DeletePairRecordRequest struct {
|
||||
BasicRequest
|
||||
PairRecordID string `plist:"PairRecordID"`
|
||||
}
|
||||
)
|
||||
|
||||
type PairRecord struct {
|
||||
DeviceCertificate []byte `plist:"DeviceCertificate"`
|
||||
EscrowBag []byte `plist:"EscrowBag,omitempty"`
|
||||
HostCertificate []byte `plist:"HostCertificate"`
|
||||
HostPrivateKey []byte `plist:"HostPrivateKey,omitempty"`
|
||||
HostID string `plist:"HostID"`
|
||||
RootCertificate []byte `plist:"RootCertificate"`
|
||||
RootPrivateKey []byte `plist:"RootPrivateKey,omitempty"`
|
||||
SystemBUID string `plist:"SystemBUID"`
|
||||
WiFiMACAddress string `plist:"WiFiMACAddress,omitempty"`
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunixbochs/struc"
|
||||
)
|
||||
|
||||
const PcapdServiceName = "com.apple.pcapd"
|
||||
|
||||
func filterPacket(pid int, procName string) func(*IOSPacketHeader) bool {
|
||||
return func(iph *IOSPacketHeader) bool {
|
||||
if pid > 0 {
|
||||
return iph.Pid == int32(pid) ||
|
||||
iph.Pid2 == int32(pid)
|
||||
}
|
||||
if procName != "" {
|
||||
return strings.HasPrefix(iph.ProcName, procName) ||
|
||||
strings.HasPrefix(iph.ProcName2, procName)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func NewPcapdClient(innerConn InnerConn, targetPID int, targetProcName string) *PcapdClient {
|
||||
return &PcapdClient{
|
||||
filter: filterPacket(targetPID, targetProcName),
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type PcapdClient struct {
|
||||
filter func(*IOSPacketHeader) bool
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *PcapdClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.client.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
|
||||
}
|
||||
lenPkg := binary.BigEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.client.innerConn.Read(int(lenPkg)); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
PacketHeaderSize = uint32(95)
|
||||
)
|
||||
|
||||
// ref: https://github.com/danielpaulus/go-ios/blob/fc943b9d236571f9775f5c593e3d49bb5bd67afd/ios/pcap/pcap.go#L27
|
||||
type IOSPacketHeader struct {
|
||||
HdrSize uint32 `struc:"uint32,big"`
|
||||
Version uint8 `struc:"uint8,big"`
|
||||
PacketSize uint32 `struc:"uint32,big"`
|
||||
Type uint8 `struc:"uint8,big"`
|
||||
Unit uint16 `struc:"uint16,big"`
|
||||
IO uint8 `struc:"uint8,big"`
|
||||
ProtocolFamily uint32 `struc:"uint32,big"`
|
||||
FramePreLength uint32 `struc:"uint32,big"`
|
||||
FramePstLength uint32 `struc:"uint32,big"`
|
||||
IFName string `struc:"[16]byte"`
|
||||
Pid int32 `struc:"int32,little"`
|
||||
ProcName string `struc:"[17]byte"`
|
||||
Unknown uint32 `struc:"uint32,little"`
|
||||
Pid2 int32 `struc:"int32,little"`
|
||||
ProcName2 string `struc:"[17]byte"`
|
||||
Unknown2 [8]byte `struc:"[8]byte"`
|
||||
}
|
||||
|
||||
func (c *PcapdClient) GetPacket(buf []byte) ([]byte, error) {
|
||||
iph := IOSPacketHeader{}
|
||||
preader := bytes.NewReader(buf)
|
||||
_ = struc.Unpack(preader, &iph)
|
||||
|
||||
if c.filter != nil {
|
||||
if !c.filter(&iph) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// support ios 15 beta4
|
||||
if iph.HdrSize > PacketHeaderSize {
|
||||
buf := make([]byte, iph.HdrSize-PacketHeaderSize)
|
||||
_, err := io.ReadFull(preader, buf)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
}
|
||||
|
||||
packet, err := io.ReadAll(preader)
|
||||
if err != nil {
|
||||
return packet, err
|
||||
}
|
||||
if iph.FramePreLength == 0 {
|
||||
ext := []byte{
|
||||
0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe,
|
||||
0xbe, 0xfe, 0xbe, 0xfe, 0x08, 0x00,
|
||||
}
|
||||
return append(ext, packet...), nil
|
||||
}
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
type PcaprecHdrS struct {
|
||||
TsSec int `struc:"uint32,little"` /* timestamp seconds */
|
||||
TsUsec int `struc:"uint32,little"` /* timestamp microseconds */
|
||||
InclLen int `struc:"uint32,little"` /* number of octets of packet saved in file */
|
||||
OrigLen int `struc:"uint32,little"` /* actual length of packet */
|
||||
}
|
||||
|
||||
func (c *PcapdClient) CreatePacket(packet []byte) ([]byte, error) {
|
||||
now := time.Now()
|
||||
phs := &PcaprecHdrS{
|
||||
int(now.Unix()),
|
||||
int(now.UnixNano()/1e3 - now.Unix()*1e6),
|
||||
len(packet),
|
||||
len(packet),
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err := struc.Pack(&buf, phs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf.Write(packet)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *PcapdClient) Close() {
|
||||
c.client.innerConn.Close()
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const ScreenshotServiceName = "com.apple.mobile.screenshotr"
|
||||
|
||||
func NewScreenshotClient(innerConn InnerConn) *ScreenshotClient {
|
||||
return &ScreenshotClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type ScreenshotClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *ScreenshotClient) NewBinaryPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewBinaryPacket(req)
|
||||
}
|
||||
|
||||
func (c *ScreenshotClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *ScreenshotClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.client.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
|
||||
}
|
||||
lenPkg := binary.BigEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.client.innerConn.Read(int(lenPkg)); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const SimulateLocationServiceName = "com.apple.dt.simulatelocation"
|
||||
|
||||
type CoordinateSystem string
|
||||
|
||||
const (
|
||||
CoordinateSystemWGS84 CoordinateSystem = "WGS84"
|
||||
CoordinateSystemBD09 CoordinateSystem = "BD09"
|
||||
CoordinateSystemGCJ02 CoordinateSystem = "GCJ02"
|
||||
)
|
||||
|
||||
func NewSimulateLocationClient(innerConn InnerConn) *SimulateLocationClient {
|
||||
return &SimulateLocationClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type SimulateLocationClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *SimulateLocationClient) NewLocationPacket(lon, lat float64, coordinateSystem CoordinateSystem) Packet {
|
||||
switch CoordinateSystem(strings.ToUpper(string(coordinateSystem))) {
|
||||
case CoordinateSystemGCJ02:
|
||||
lon, lat = gcj02ToWGS84(lon, lat)
|
||||
case CoordinateSystemBD09:
|
||||
lon, lat = bd09ToWGS84(lon, lat)
|
||||
case CoordinateSystemWGS84:
|
||||
_, _ = lon, lat
|
||||
default:
|
||||
_, _ = lon, lat
|
||||
}
|
||||
|
||||
pkt := new(locationPacket)
|
||||
pkt.lon = lon
|
||||
pkt.lat = lat
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *SimulateLocationClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
// Recover try to revert back
|
||||
func (c *SimulateLocationClient) Recover() error {
|
||||
data := []byte{0x00, 0x00, 0x00, 0x01}
|
||||
debugLog(fmt.Sprintf("--> %+v\n", data))
|
||||
return c.client.innerConn.Write(data)
|
||||
}
|
||||
|
||||
const (
|
||||
xPi = math.Pi * 3000.0 / 180.0
|
||||
offset = 0.00669342162296594323
|
||||
axis = 6378245.0
|
||||
)
|
||||
|
||||
func isOutOfChina(lon, lat float64) bool {
|
||||
return !(lon > 73.66 && lon < 135.05 && lat > 3.86 && lat < 53.55)
|
||||
}
|
||||
|
||||
func delta(lon, lat float64) (float64, float64) {
|
||||
dLat := transformLat(lon-105.0, lat-35.0)
|
||||
dLon := transformLng(lon-105.0, lat-35.0)
|
||||
|
||||
radLat := lat / 180.0 * math.Pi
|
||||
magic := math.Sin(radLat)
|
||||
magic = 1 - offset*magic*magic
|
||||
sqrtMagic := math.Sqrt(magic)
|
||||
|
||||
dLat = (dLat * 180.0) / ((axis * (1 - offset)) / (magic * sqrtMagic) * math.Pi)
|
||||
dLon = (dLon * 180.0) / (axis / sqrtMagic * math.Cos(radLat) * math.Pi)
|
||||
|
||||
mgLat := lat + dLat
|
||||
mgLon := lon + dLon
|
||||
|
||||
return mgLon, mgLat
|
||||
}
|
||||
|
||||
func transformLat(lon, lat float64) float64 {
|
||||
ret := -100.0 + 2.0*lon + 3.0*lat + 0.2*lat*lat + 0.1*lon*lat + 0.2*math.Sqrt(math.Abs(lon))
|
||||
ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0
|
||||
ret += (20.0*math.Sin(lat*math.Pi) + 40.0*math.Sin(lat/3.0*math.Pi)) * 2.0 / 3.0
|
||||
ret += (160.0*math.Sin(lat/12.0*math.Pi) + 320*math.Sin(lat*math.Pi/30.0)) * 2.0 / 3.0
|
||||
return ret
|
||||
}
|
||||
|
||||
func transformLng(lon, lat float64) float64 {
|
||||
ret := 300.0 + lon + 2.0*lat + 0.1*lon*lon + 0.1*lon*lat + 0.1*math.Sqrt(math.Abs(lon))
|
||||
ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0
|
||||
ret += (20.0*math.Sin(lon*math.Pi) + 40.0*math.Sin(lon/3.0*math.Pi)) * 2.0 / 3.0
|
||||
ret += (150.0*math.Sin(lon/12.0*math.Pi) + 300.0*math.Sin(lon/30.0*math.Pi)) * 2.0 / 3.0
|
||||
return ret
|
||||
}
|
||||
|
||||
func gcj02ToWGS84(lon, lat float64) (float64, float64) {
|
||||
if isOutOfChina(lon, lat) {
|
||||
return lon, lat
|
||||
}
|
||||
|
||||
mgLon, mgLat := delta(lon, lat)
|
||||
|
||||
return lon*2 - mgLon, lat*2 - mgLat
|
||||
}
|
||||
|
||||
func bd09ToGCJ02(lon, lat float64) (float64, float64) {
|
||||
x := lon - 0.0065
|
||||
y := lat - 0.006
|
||||
|
||||
z := math.Sqrt(x*x+y*y) - 0.00002*math.Sin(y*xPi)
|
||||
theta := math.Atan2(y, x) - 0.000003*math.Cos(x*xPi)
|
||||
|
||||
gLon := z * math.Cos(theta)
|
||||
gLat := z * math.Sin(theta)
|
||||
|
||||
return gLon, gLat
|
||||
}
|
||||
|
||||
func bd09ToWGS84(lon, lat float64) (float64, float64) {
|
||||
lon, lat = bd09ToGCJ02(lon, lat)
|
||||
return gcj02ToWGS84(lon, lat)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
type IconPNGDataResponse struct {
|
||||
PNGData []byte `plist:"pngData"`
|
||||
}
|
||||
|
||||
type InterfaceOrientationResponse struct {
|
||||
Orientation OrientationState `plist:"interfaceOrientation"`
|
||||
}
|
||||
|
||||
type OrientationState int64
|
||||
|
||||
const (
|
||||
Unknown OrientationState = iota
|
||||
Portrait
|
||||
PortraitUpsideDown
|
||||
LandscapeRight
|
||||
LandscapeLeft
|
||||
)
|
||||
|
||||
const (
|
||||
SpringBoardServiceName = "com.apple.springboardservices"
|
||||
)
|
||||
|
||||
func NewSpringBoardClient(innerConn InnerConn) *SpringBoardClient {
|
||||
return &SpringBoardClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type SpringBoardClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) NewBinaryPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewBinaryPacket(req)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const SyslogRelayServiceName = "com.apple.syslog_relay"
|
||||
|
||||
func NewSyslogRelayClient(innerConn InnerConn) *SyslogRelayClient {
|
||||
return &SyslogRelayClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type SyslogRelayClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *SyslogRelayClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
func (c *SyslogRelayClient) Close() {
|
||||
c.client.innerConn.Close()
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
TestmanagerdSecureServiceName = "com.apple.testmanagerd.lockdown.secure"
|
||||
TestmanagerdServiceName = "com.apple.testmanagerd.lockdown"
|
||||
)
|
||||
|
||||
func NewTestmanagerdClient(innerConn InnerConn) *TestmanagerdClient {
|
||||
return &TestmanagerdClient{
|
||||
client: newDtxMessageClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type TestmanagerdClient struct {
|
||||
client *dtxMessageClient
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) Connection() (publishedChannels map[string]int32, err error) {
|
||||
return t.client.Connection()
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) MakeChannel(channel string) (id uint32, err error) {
|
||||
return t.client.MakeChannel(channel)
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) Invoke(selector string, args *AuxBuffer, channelCode uint32, expectsReply bool) (result *DTXMessageResult, err error) {
|
||||
var msgID uint32
|
||||
if msgID, err = t.client.SendDTXMessage(selector, args.Bytes(), channelCode, expectsReply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expectsReply {
|
||||
if result, err = t.client.GetResult(msgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
|
||||
t.client.RegisterCallback(obj, cb)
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) Close() {
|
||||
t.client.Close()
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var DefaultDeadlineTimeout = 30 * time.Second
|
||||
|
||||
const (
|
||||
BundleID = "electricbubble.libimobiledevice"
|
||||
ProgramName = "libimobiledevice"
|
||||
ClientVersion = "libimobiledevice-beta"
|
||||
LibUSBMuxVersion = 3
|
||||
)
|
||||
|
||||
type ReplyCode uint64
|
||||
|
||||
const (
|
||||
ReplyCodeOK ReplyCode = iota
|
||||
ReplyCodeBadCommand
|
||||
ReplyCodeBadDevice
|
||||
ReplyCodeConnectionRefused
|
||||
_ // ignore `4`
|
||||
_ // ignore `5`
|
||||
ReplyCodeBadVersion
|
||||
)
|
||||
|
||||
func (rc ReplyCode) String() string {
|
||||
switch rc {
|
||||
case ReplyCodeOK:
|
||||
return "ok"
|
||||
case ReplyCodeBadCommand:
|
||||
return "bad command"
|
||||
case ReplyCodeBadDevice:
|
||||
return "bad device"
|
||||
case ReplyCodeConnectionRefused:
|
||||
return "connection refused"
|
||||
case ReplyCodeBadVersion:
|
||||
return "bad version"
|
||||
default:
|
||||
return "unknown reply code: " + strconv.Itoa(int(rc))
|
||||
}
|
||||
}
|
||||
|
||||
type ProtoVersion uint32
|
||||
|
||||
// proto_version == 1
|
||||
// construct message plist
|
||||
// else `0`? res == `RESULT_BADVERSION`
|
||||
// binary packet
|
||||
|
||||
const (
|
||||
ProtoVersionBinary ProtoVersion = iota
|
||||
ProtoVersionPlist
|
||||
)
|
||||
|
||||
type ProtoMessageType uint32
|
||||
|
||||
const (
|
||||
_ ProtoMessageType = iota
|
||||
ProtoMessageTypeResult
|
||||
ProtoMessageTypeConnect
|
||||
ProtoMessageTypeListen
|
||||
ProtoMessageTypeDeviceAdd
|
||||
ProtoMessageTypeDeviceRemove
|
||||
ProtoMessageTypeDevicePaired
|
||||
_ // `7`
|
||||
ProtoMessageTypePlist
|
||||
)
|
||||
|
||||
type MessageType string
|
||||
|
||||
const (
|
||||
MessageTypeResult MessageType = "Result"
|
||||
MessageTypeConnect MessageType = "Connect"
|
||||
MessageTypeListen MessageType = "Listen"
|
||||
MessageTypeDeviceAdd MessageType = "Attached"
|
||||
MessageTypeDeviceRemove MessageType = "Detached"
|
||||
MessageTypeReadBUID MessageType = "ReadBUID"
|
||||
MessageTypeReadPairRecord MessageType = "ReadPairRecord"
|
||||
MessageTypeSavePairRecord MessageType = "SavePairRecord"
|
||||
MessageTypeDeletePairRecord MessageType = "DeletePairRecord"
|
||||
MessageTypeDeviceList MessageType = "ListDevices"
|
||||
)
|
||||
|
||||
type BaseDevice struct {
|
||||
MessageType MessageType `plist:"MessageType"`
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
Properties DeviceProperties `plist:"Properties"`
|
||||
}
|
||||
|
||||
type DeviceProperties struct {
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
ConnectionType string `plist:"ConnectionType"`
|
||||
ConnectionSpeed int `plist:"ConnectionSpeed"`
|
||||
ProductID int `plist:"ProductID"`
|
||||
LocationID int `plist:"LocationID"`
|
||||
SerialNumber string `plist:"SerialNumber"`
|
||||
UDID string `plist:"UDID"`
|
||||
USBSerialNumber string `plist:"USBSerialNumber"`
|
||||
|
||||
EscapedFullServiceName string `plist:"EscapedFullServiceName"`
|
||||
InterfaceIndex int `plist:"InterfaceIndex"`
|
||||
NetworkAddress []byte `plist:"NetworkAddress"`
|
||||
}
|
||||
|
||||
func NewUsbmuxClient(timeout ...time.Duration) (c *UsbmuxClient, err error) {
|
||||
if len(timeout) == 0 {
|
||||
timeout = []time.Duration{DefaultDeadlineTimeout}
|
||||
}
|
||||
c = &UsbmuxClient{version: ProtoVersionPlist}
|
||||
var conn net.Conn
|
||||
if conn, err = rawDial(timeout[0]); err != nil {
|
||||
return nil, fmt.Errorf("usbmux connect: %w", err)
|
||||
}
|
||||
|
||||
c.innerConn = newInnerConn(conn, timeout[0])
|
||||
return
|
||||
}
|
||||
|
||||
type UsbmuxClient struct {
|
||||
innerConn InnerConn
|
||||
version ProtoVersion
|
||||
tag uint32
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewBasicRequest(msgType MessageType) *BasicRequest {
|
||||
return &BasicRequest{
|
||||
MessageType: msgType,
|
||||
BundleID: BundleID,
|
||||
ProgramName: ProgramName,
|
||||
ClientVersionString: ClientVersion,
|
||||
LibUSBMuxVersion: LibUSBMuxVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewConnectRequest(deviceID, port int) *ConnectRequest {
|
||||
return &ConnectRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeConnect),
|
||||
DeviceID: deviceID,
|
||||
PortNumber: ((port << 8) & 0xFF00) | (port >> 8),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewReadPairRecordRequest(udid string) *ReadPairRecordRequest {
|
||||
return &ReadPairRecordRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeReadPairRecord),
|
||||
PairRecordID: udid,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewSavePairRecordRequest(udid string, deviceID int, data []byte) *SavePairRecordRequest {
|
||||
return &SavePairRecordRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeSavePairRecord),
|
||||
PairRecordID: udid,
|
||||
PairRecordData: data,
|
||||
DeviceID: deviceID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewDeletePairRecordRequest(udid string) *DeletePairRecordRequest {
|
||||
return &DeletePairRecordRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeDeletePairRecord),
|
||||
PairRecordID: udid,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewPacket(protoMsgType ProtoMessageType) Packet {
|
||||
return c.newPacket(protoMsgType)
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) newPacket(protoMsgType ProtoMessageType) *packet {
|
||||
c.tag++
|
||||
pkt := &packet{
|
||||
version: c.version,
|
||||
msgType: protoMsgType,
|
||||
tag: c.tag,
|
||||
}
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewPlistPacket(req interface{}) (Packet, error) {
|
||||
pkt := c.newPacket(ProtoMessageTypePlist)
|
||||
if buf, err := plist.Marshal(req, plist.XMLFormat); err != nil {
|
||||
return nil, fmt.Errorf("plist packet marshal: %w", err)
|
||||
} else {
|
||||
pkt.body = buf
|
||||
}
|
||||
pkt.length = uint32(len(pkt.body) + 4*4)
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) SendPacket(pkt Packet) (err error) {
|
||||
var raw []byte
|
||||
if raw, err = pkt.Pack(); err != nil {
|
||||
return fmt.Errorf("usbmux send: %w", err)
|
||||
}
|
||||
// debugLog(fmt.Sprintf("--> Length: %d, Version: %d, Type: %d, Tag: %d\n%s\n", pkt.Length(), pkt.Version(), pkt.Type(), pkt.Tag(), pkt.Body()))
|
||||
debugLog(fmt.Sprintf("--> %s\n", pkt))
|
||||
return c.innerConn.Write(raw)
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
lenPkg := binary.LittleEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.innerConn.Read(int(lenPkg - 4)); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(packet).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
|
||||
// debugLog(fmt.Sprintf("<-- Length: %d, Version: %d, Type: %d, Tag: %d\n%s\n", respPkt.Length(), respPkt.Version(), respPkt.Type(), respPkt.Tag(), respPkt.Body()))
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
reply := struct {
|
||||
MessageType string `plist:"MessageType"`
|
||||
Number ReplyCode `plist:"Number"`
|
||||
}{}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
|
||||
if reply.Number != ReplyCodeOK {
|
||||
return nil, fmt.Errorf("usbmux receive: %s", reply.Number.String())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) Close() {
|
||||
c.innerConn.Close()
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) RawConn() net.Conn {
|
||||
return c.innerConn.RawConn()
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) InnerConn() InnerConn {
|
||||
return c.innerConn
|
||||
}
|
||||
|
||||
func rawDial(timeout time.Duration) (net.Conn, error) {
|
||||
dialer := net.Dialer{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
var network, address string
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "android", "linux":
|
||||
network, address = "unix", "/var/run/usbmuxd"
|
||||
case "windows":
|
||||
network, address = "tcp", "127.0.0.1:27015"
|
||||
default:
|
||||
return nil, fmt.Errorf("raw dial: unsupported system: %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
return dialer.Dial(network, address)
|
||||
}
|
||||
|
||||
type InnerConn interface {
|
||||
Write(data []byte) (err error)
|
||||
Read(length int) (data []byte, err error)
|
||||
Handshake(version []int, pairRecord *PairRecord) (err error)
|
||||
DismissSSL() (err error)
|
||||
Close()
|
||||
RawConn() net.Conn
|
||||
Timeout(time.Duration)
|
||||
}
|
||||
|
||||
func newInnerConn(conn net.Conn, timeout time.Duration) InnerConn {
|
||||
return &safeConn{
|
||||
conn: conn,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
type safeConn struct {
|
||||
conn net.Conn
|
||||
sslConn *tls.Conn
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (c *safeConn) Write(data []byte) (err error) {
|
||||
conn := c.RawConn()
|
||||
if c.timeout <= 0 {
|
||||
err = conn.SetWriteDeadline(time.Time{})
|
||||
} else {
|
||||
err = conn.SetWriteDeadline(time.Now().Add(c.timeout))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for totalSent := 0; totalSent < len(data); {
|
||||
var sent int
|
||||
if sent, err = conn.Write(data[totalSent:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if sent == 0 {
|
||||
return err
|
||||
}
|
||||
totalSent += sent
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) Read(length int) (data []byte, err error) {
|
||||
conn := c.RawConn()
|
||||
if c.timeout <= 0 {
|
||||
err = conn.SetReadDeadline(time.Time{})
|
||||
} else {
|
||||
err = conn.SetReadDeadline(time.Now().Add(c.timeout))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data = make([]byte, 0, length)
|
||||
for len(data) < length {
|
||||
buf := make([]byte, length-len(data))
|
||||
_n, _err := 0, error(nil)
|
||||
if _n, _err = conn.Read(buf); _err != nil && _n == 0 {
|
||||
return nil, _err
|
||||
}
|
||||
data = append(data, buf[:_n]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) Handshake(version []int, pairRecord *PairRecord) (err error) {
|
||||
minVersion := uint16(tls.VersionTLS11)
|
||||
maxVersion := uint16(tls.VersionTLS11)
|
||||
|
||||
if version[0] > 10 {
|
||||
minVersion = tls.VersionTLS11
|
||||
maxVersion = tls.VersionTLS13
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(pairRecord.RootCertificate, pairRecord.RootPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: minVersion,
|
||||
MaxVersion: maxVersion,
|
||||
}
|
||||
|
||||
c.sslConn = tls.Client(c.conn, config)
|
||||
|
||||
if err = c.sslConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) DismissSSL() (err error) {
|
||||
if c.sslConn != nil {
|
||||
// err = c.sslConn.CloseWrite()
|
||||
// if err = c.sslConn.CloseWrite(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
c.sslConn = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) Close() {
|
||||
if c.sslConn != nil {
|
||||
if err := c.sslConn.Close(); err != nil {
|
||||
debugLog(fmt.Sprintf("close: %s", err))
|
||||
}
|
||||
}
|
||||
if c.conn != nil {
|
||||
if err := c.conn.Close(); err != nil {
|
||||
debugLog(fmt.Sprintf("close: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RawConn `sslConn` first
|
||||
func (c *safeConn) RawConn() net.Conn {
|
||||
if c.sslConn != nil {
|
||||
return c.sslConn
|
||||
}
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func (c *safeConn) Timeout(duration time.Duration) {
|
||||
c.timeout = duration
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import "howett.net/plist"
|
||||
|
||||
type NSArray struct {
|
||||
internal []interface{}
|
||||
}
|
||||
|
||||
func NewNSArray(value []interface{}) *NSArray {
|
||||
return &NSArray{
|
||||
internal: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSArray) archive(objects []interface{}) []interface{} {
|
||||
objs := make([]interface{}, 0, len(ns.internal))
|
||||
|
||||
info := map[string]interface{}{}
|
||||
objects = append(objects, info)
|
||||
|
||||
for _, v := range ns.internal {
|
||||
var uid plist.UID
|
||||
objects, uid = archive(objects, v)
|
||||
objs = append(objs, uid)
|
||||
}
|
||||
|
||||
info["NS.objects"] = objs
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSArray",
|
||||
"$classes": []interface{}{"NSArray", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNSArray_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
value := []interface{}{
|
||||
"a", 1,
|
||||
"b", "2",
|
||||
"c", false,
|
||||
}
|
||||
array := NewNSArray(value)
|
||||
objects := array.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSDictionary struct {
|
||||
internal map[string]interface{}
|
||||
}
|
||||
|
||||
func NewNSDictionary(value map[string]interface{}) *NSDictionary {
|
||||
return &NSDictionary{
|
||||
internal: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSDictionary) archive(objects []interface{}) []interface{} {
|
||||
keys := make([]interface{}, 0, len(ns.internal))
|
||||
objs := make([]interface{}, 0, len(ns.internal))
|
||||
|
||||
info := map[string]interface{}{}
|
||||
objects = append(objects, info)
|
||||
|
||||
for k, v := range ns.internal {
|
||||
uid := plist.UID(len(objects))
|
||||
keys = append(keys, uid)
|
||||
objects = append(objects, k)
|
||||
|
||||
objects, uid = archive(objects, v)
|
||||
objs = append(objs, uid)
|
||||
}
|
||||
|
||||
info["NS.keys"] = keys
|
||||
info["NS.objects"] = objs
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSDictionary",
|
||||
"$classes": []interface{}{"NSDictionary", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNSDictionary_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
value := map[string]interface{}{
|
||||
"a": 1,
|
||||
"b": "2",
|
||||
"c": true,
|
||||
}
|
||||
dict := NewNSDictionary(value)
|
||||
objects := dict.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func Marshal(obj interface{}) (raw []byte, err error) {
|
||||
objects := []interface{}{"$null"}
|
||||
objects, _ = archive(objects, obj)
|
||||
archiver := map[string]interface{}{
|
||||
"$version": 100000,
|
||||
"$archiver": "NSKeyedArchiver",
|
||||
"$top": map[string]interface{}{"root": plist.UID(1)},
|
||||
"$objects": objects,
|
||||
}
|
||||
// if len(format) == 0 {
|
||||
// format = []int{plist.BinaryFormat}
|
||||
// }
|
||||
// return plist.Marshal(archiver, format[0])
|
||||
return plist.Marshal(archiver, plist.BinaryFormat)
|
||||
}
|
||||
|
||||
func archive(_objects []interface{}, _value interface{}) (objects []interface{}, uid plist.UID) {
|
||||
val := reflect.ValueOf(_value)
|
||||
typ := val.Type()
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String,
|
||||
reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Uintptr:
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = append(_objects, _value)
|
||||
return
|
||||
case reflect.Map:
|
||||
uid = plist.UID(len(_objects))
|
||||
vv := make(map[string]interface{})
|
||||
keys := val.MapKeys()
|
||||
for _, k := range keys {
|
||||
vv[k.String()] = val.MapIndex(k).Interface()
|
||||
}
|
||||
objects = NewNSDictionary(vv).archive(_objects)
|
||||
return
|
||||
case reflect.Slice, reflect.Array:
|
||||
uid = plist.UID(len(_objects))
|
||||
vv := make([]interface{}, val.Len())
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
vv[i] = val.Index(i).Interface()
|
||||
}
|
||||
objects = NewNSArray(vv).archive(_objects)
|
||||
return
|
||||
case reflect.Struct, reflect.Ptr:
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
switch typ.Name() {
|
||||
case "NSUUID":
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = NewNSUUID(val.Field(0).Bytes()).archive(_objects)
|
||||
return
|
||||
case "NSURL":
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = NewNSURL(val.Field(0).String()).archive(_objects)
|
||||
return
|
||||
case "XCTestConfiguration":
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = newXCTestConfiguration(_value).archive(_objects)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO unarchive
|
||||
@@ -1,41 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
// value := map[string]interface{}{
|
||||
// "a": 1,
|
||||
// "b": "2",
|
||||
// "c": true,
|
||||
// }
|
||||
|
||||
// value := []interface{}{
|
||||
// "a", 1,
|
||||
// "b", "2",
|
||||
// "c", false,
|
||||
// }
|
||||
|
||||
// value := NewNSUUID(uuid.NewV4().Bytes())
|
||||
|
||||
// value := NewNSURL("/tmp")
|
||||
|
||||
value := NewXCTestConfiguration(NewNSUUID(uuid.NewV4().Bytes()), NewNSURL("/tmp"), "", "")
|
||||
|
||||
raw, err := Marshal(value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, v := range raw {
|
||||
fmt.Printf("%x", v)
|
||||
}
|
||||
fmt.Println()
|
||||
// fmt.Println(raw)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSNull struct{}
|
||||
|
||||
func NewNSNull() *NSNull {
|
||||
return &NSNull{}
|
||||
}
|
||||
|
||||
func (ns *NSNull) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{}
|
||||
|
||||
objects = append(objects, info)
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSNull",
|
||||
"$classes": []interface{}{"NSNull", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSURL struct {
|
||||
internal string
|
||||
}
|
||||
|
||||
func NewNSURL(path string) *NSURL {
|
||||
return &NSURL{
|
||||
internal: path,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSURL) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{}
|
||||
|
||||
objects = append(objects, info)
|
||||
|
||||
uid := plist.UID(0)
|
||||
info["NS.base"] = uid
|
||||
objects, uid = archive(objects, fmt.Sprintf("file://%s", ns.internal))
|
||||
info["NS.relative"] = uid
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSURL",
|
||||
"$classes": []interface{}{"NSURL", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNSURL_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
nsurl := NewNSURL("/tmp")
|
||||
objects := nsurl.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSUUID struct {
|
||||
internal []byte
|
||||
}
|
||||
|
||||
func NewNSUUID(uuid []byte) *NSUUID {
|
||||
return &NSUUID{
|
||||
internal: uuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSUUID) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{
|
||||
"NS.uuidbytes": ns.internal,
|
||||
}
|
||||
|
||||
objects = append(objects, info)
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSUUID",
|
||||
"$classes": []interface{}{"NSUUID", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
|
||||
func (ns *NSUUID) String() string {
|
||||
buf := make([]byte, 36)
|
||||
|
||||
hex.Encode(buf[0:8], ns.internal[0:4])
|
||||
buf[8] = '-'
|
||||
hex.Encode(buf[9:13], ns.internal[4:6])
|
||||
buf[13] = '-'
|
||||
hex.Encode(buf[14:18], ns.internal[6:8])
|
||||
buf[18] = '-'
|
||||
hex.Encode(buf[19:23], ns.internal[8:10])
|
||||
buf[23] = '-'
|
||||
hex.Encode(buf[24:], ns.internal[10:])
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestNSUUID_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
nsuuid := NewNSUUID(uuid.NewV4().Bytes())
|
||||
objects := nsuuid.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
type XCTCapabilities struct {
|
||||
internal map[string]interface{}
|
||||
}
|
||||
|
||||
func (caps *XCTCapabilities) archive(objects []interface{}) []interface{} {
|
||||
// TODO caps
|
||||
return nil
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type XCTestConfiguration struct {
|
||||
internal map[string]interface{}
|
||||
}
|
||||
|
||||
func newXCTestConfiguration(cfg interface{}) *XCTestConfiguration {
|
||||
return cfg.(*XCTestConfiguration)
|
||||
}
|
||||
|
||||
func NewXCTestConfiguration(nsuuid *NSUUID, nsurl *NSURL, targetBundleID, targetAppPath string) *XCTestConfiguration {
|
||||
contents := map[string]interface{}{
|
||||
"aggregateStatisticsBeforeCrash": map[string]interface{}{
|
||||
"XCSuiteRecordsKey": map[string]interface{}{},
|
||||
},
|
||||
"automationFrameworkPath": "/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
|
||||
"baselineFileRelativePath": nil,
|
||||
"baselineFileURL": nil,
|
||||
"defaultTestExecutionTimeAllowance": nil,
|
||||
"disablePerformanceMetrics": false,
|
||||
"emitOSLogs": false,
|
||||
"formatVersion": 2,
|
||||
"gatherLocalizableStringsData": false,
|
||||
"initializeForUITesting": true,
|
||||
"maximumTestExecutionTimeAllowance": nil,
|
||||
"productModuleName": "WebDriverAgentRunner", // set to other value is also OK
|
||||
"randomExecutionOrderingSeed": nil,
|
||||
"reportActivities": true,
|
||||
"reportResultsToIDE": true,
|
||||
"systemAttachmentLifetime": 2,
|
||||
"targetApplicationArguments": []interface{}{}, // maybe useless
|
||||
"targetApplicationEnvironment": nil,
|
||||
"targetApplicationPath": targetAppPath,
|
||||
"testApplicationDependencies": map[string]interface{}{},
|
||||
"testApplicationUserOverrides": nil,
|
||||
"testBundleRelativePath": nil,
|
||||
"testExecutionOrdering": 0,
|
||||
"testTimeoutsEnabled": false,
|
||||
"testsDrivenByIDE": false,
|
||||
"testsMustRunOnMainThread": true,
|
||||
"testsToRun": nil,
|
||||
"testsToSkip": nil,
|
||||
"treatMissingBaselinesAsFailures": false,
|
||||
"userAttachmentLifetime": 1,
|
||||
"testBundleURL": nsurl,
|
||||
"sessionIdentifier": nsuuid,
|
||||
"targetApplicationBundleID": targetBundleID,
|
||||
// "targetApplicationBundleID": "",
|
||||
}
|
||||
return &XCTestConfiguration{internal: contents}
|
||||
}
|
||||
|
||||
func (cfg *XCTestConfiguration) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{}
|
||||
objects = append(objects, info)
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "XCTestConfiguration",
|
||||
"$classes": []interface{}{"XCTestConfiguration", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
for k, v := range cfg.internal {
|
||||
val := reflect.ValueOf(v)
|
||||
if !val.IsValid() {
|
||||
info[k] = plist.UID(0)
|
||||
continue
|
||||
}
|
||||
|
||||
typ := val.Type()
|
||||
|
||||
if k != "formatVersion" && (typ.Kind() == reflect.Bool || typ.Kind() == reflect.Uintptr || typ.Kind() == reflect.Int) {
|
||||
info[k] = v
|
||||
} else {
|
||||
var uid plist.UID
|
||||
objects, uid = archive(objects, v)
|
||||
info[k] = uid
|
||||
}
|
||||
}
|
||||
return objects
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestXCTestConfiguration_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
xcTestConfiguration := NewXCTestConfiguration(NewNSUUID(uuid.NewV4().Bytes()), NewNSURL("/tmp"), "", "")
|
||||
objects := xcTestConfiguration.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ Screenshot = (*screenshot)(nil)
|
||||
|
||||
func newScreenshot(client *libimobiledevice.ScreenshotClient) *screenshot {
|
||||
return &screenshot{
|
||||
client: client,
|
||||
exchanged: false,
|
||||
}
|
||||
}
|
||||
|
||||
type screenshot struct {
|
||||
client *libimobiledevice.ScreenshotClient
|
||||
exchanged bool
|
||||
}
|
||||
|
||||
func (s *screenshot) Take() (raw *bytes.Buffer, err error) {
|
||||
if err = s.exchange(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// link service
|
||||
req := []interface{}{
|
||||
"DLMessageProcessMessage",
|
||||
map[string]interface{}{
|
||||
"MessageType": "ScreenShotRequest",
|
||||
},
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = s.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = s.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []interface{}
|
||||
if err = respPkt.Unmarshal(&resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp[0].(string) != "DLMessageProcessMessage" {
|
||||
return nil, fmt.Errorf("message device not ready %s %s", resp[3], resp[4])
|
||||
}
|
||||
|
||||
raw = new(bytes.Buffer)
|
||||
|
||||
screen := resp[1].(map[string]interface{})
|
||||
var data []byte
|
||||
ok := false
|
||||
if data, ok = screen["ScreenShotData"].([]byte); !ok {
|
||||
return nil, errors.New("`ScreenShotData` not ready")
|
||||
}
|
||||
if _, err = raw.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *screenshot) exchange() (err error) {
|
||||
if s.exchanged {
|
||||
return
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = s.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp []interface{}
|
||||
if err = respPkt.Unmarshal(&resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := []interface{}{
|
||||
"DLMessageVersionExchange",
|
||||
"DLVersionsOk",
|
||||
resp[1],
|
||||
}
|
||||
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.client.SendPacket(pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if respPkt, err = s.client.ReceivePacket(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = respPkt.Unmarshal(&resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp[3].(string) != "DLMessageDeviceReady" {
|
||||
return fmt.Errorf("message device not ready %s", resp[3])
|
||||
}
|
||||
|
||||
s.exchanged = true
|
||||
return
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var screenshotSrv Screenshot
|
||||
|
||||
func setupScreenshotSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if screenshotSrv, err = lockdownSrv.ScreenshotService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_screenshot_Take(t *testing.T) {
|
||||
setupScreenshotSrv(t)
|
||||
|
||||
// raw, err := dev.Screenshot()
|
||||
raw, err := screenshotSrv.Take()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = raw
|
||||
|
||||
img, format, err := image.Decode(raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
userHomeDir, _ := os.UserHomeDir()
|
||||
file, err := os.Create(userHomeDir + "/Desktop/s1." + format)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
switch format {
|
||||
case "png":
|
||||
err = png.Encode(file, img)
|
||||
case "jpeg":
|
||||
err = jpeg.Encode(file, img, nil)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(file.Name())
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import "github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
|
||||
var _ SimulateLocation = (*simulateLocation)(nil)
|
||||
|
||||
func newSimulateLocation(client *libimobiledevice.SimulateLocationClient) *simulateLocation {
|
||||
return &simulateLocation{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type simulateLocation struct {
|
||||
client *libimobiledevice.SimulateLocationClient
|
||||
}
|
||||
|
||||
func (s *simulateLocation) Update(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error) {
|
||||
if len(coordinateSystem) == 0 {
|
||||
coordinateSystem = []CoordinateSystem{CoordinateSystemWGS84}
|
||||
}
|
||||
pkt := s.client.NewLocationPacket(longitude, latitude, coordinateSystem[0])
|
||||
return s.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (s *simulateLocation) Recover() (err error) {
|
||||
return s.client.Recover()
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import "testing"
|
||||
|
||||
var simulateLocationSrv SimulateLocation
|
||||
|
||||
func setupSimulateLocationSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if simulateLocationSrv, err = lockdownSrv.SimulateLocationService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_simulateLocation_Update(t *testing.T) {
|
||||
setupSimulateLocationSrv(t)
|
||||
|
||||
// https://api.map.baidu.com/lbsapi/getpoint/index.html
|
||||
// if err := dev.SimulateLocationUpdate(116.024067, 40.362639, CoordinateSystemBD09); err != nil {
|
||||
if err := simulateLocationSrv.Update(116.024067, 40.362639, CoordinateSystemBD09); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// https://developer.amap.com/tools/picker
|
||||
// https://lbs.qq.com/tool/getpoint/index.html
|
||||
// if err := simulateLocationSrv.Update(120.116979,30.252876, CoordinateSystemGCJ02); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if err := simulateLocationSrv.Update(121.499763, 31.239580); err != nil {
|
||||
// if err := simulateLocationSrv.Update(121.499763, 31.239580, CoordinateSystemWGS84); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_simulateLocation_Recover(t *testing.T) {
|
||||
setupSimulateLocationSrv(t)
|
||||
|
||||
// if err := dev.SimulateLocationRecover(); err != nil {
|
||||
if err := simulateLocationSrv.Recover(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
func newSpringBoard(client *libimobiledevice.SpringBoardClient) *springboard {
|
||||
return &springboard{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type springboard struct {
|
||||
client *libimobiledevice.SpringBoardClient
|
||||
}
|
||||
|
||||
func (s springboard) GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
req := map[string]interface{}{
|
||||
"command": "getIconPNGData",
|
||||
"bundleId": bundleId,
|
||||
}
|
||||
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = s.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var reply libimobiledevice.IconPNGDataResponse
|
||||
raw = new(bytes.Buffer)
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
if _, err = raw.Write(reply.PNGData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s springboard) GetInterfaceOrientation() (orientation libimobiledevice.OrientationState, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
req := map[string]interface{}{
|
||||
"command": "getInterfaceOrientation",
|
||||
}
|
||||
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.client.SendPacket(pkt); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = s.client.ReceivePacket(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var reply libimobiledevice.InterfaceOrientationResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return 0, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
orientation = reply.Orientation
|
||||
return
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var springBoardSrv SpringBoard
|
||||
|
||||
func setupSpringBoardSrv(t *testing.T) {
|
||||
setupLockdownSrv(t)
|
||||
|
||||
var err error
|
||||
if lockdownSrv, err = dev.lockdownService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if springBoardSrv, err = lockdownSrv.SpringBoardService(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_springBoard_GetIcon(t *testing.T) {
|
||||
setupSpringBoardSrv(t)
|
||||
raw, _ := springBoardSrv.GetIconPNGData("com.ss.iphone.ugc.Aweme")
|
||||
img, format, err := image.Decode(raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file, err := os.Create("./abc." + format)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
switch format {
|
||||
case "png":
|
||||
err = png.Encode(file, img)
|
||||
case "jpeg":
|
||||
err = jpeg.Encode(file, img, nil)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_springBoard_GetOrient(t *testing.T) {
|
||||
setupSpringBoardSrv(t)
|
||||
fmt.Println(springBoardSrv.GetInterfaceOrientation())
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ SyslogRelay = (*syslogRelay)(nil)
|
||||
|
||||
func newSyslogRelay(client *libimobiledevice.SyslogRelayClient) *syslogRelay {
|
||||
r := &syslogRelay{
|
||||
client: client,
|
||||
stop: make(chan bool),
|
||||
isReading: false,
|
||||
}
|
||||
r.reader = bufio.NewReader(r.client.InnerConn().RawConn())
|
||||
return r
|
||||
}
|
||||
|
||||
type syslogRelay struct {
|
||||
client *libimobiledevice.SyslogRelayClient
|
||||
|
||||
reader *bufio.Reader
|
||||
stop chan bool
|
||||
isReading bool
|
||||
}
|
||||
|
||||
func (r *syslogRelay) Lines() <-chan string {
|
||||
out := make(chan string)
|
||||
r.isReading = true
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
close(out)
|
||||
r.isReading = false
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-r.stop:
|
||||
return
|
||||
default:
|
||||
bs, err := r.readLine()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), io.EOF.Error()) {
|
||||
return
|
||||
}
|
||||
debugLog(fmt.Sprintf("syslog: %s", err))
|
||||
}
|
||||
if len(bs) > 1 && bs[0] == 0 {
|
||||
bs = bs[1:]
|
||||
}
|
||||
out <- string(bs)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *syslogRelay) Stop() {
|
||||
if r.isReading {
|
||||
r.stop <- true
|
||||
}
|
||||
}
|
||||
|
||||
func (r *syslogRelay) readLine() ([]byte, error) {
|
||||
var line []byte
|
||||
for {
|
||||
l, more, err := r.reader.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if line == nil && !more {
|
||||
return l, nil
|
||||
}
|
||||
line = append(line, l...)
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ Testmanagerd = (*testmanagerd)(nil)
|
||||
|
||||
func newTestmanagerd(client *libimobiledevice.TestmanagerdClient, iOSVersion []int) *testmanagerd {
|
||||
return &testmanagerd{
|
||||
client: client,
|
||||
iOSVersion: iOSVersion,
|
||||
}
|
||||
}
|
||||
|
||||
type testmanagerd struct {
|
||||
client *libimobiledevice.TestmanagerdClient
|
||||
iOSVersion []int
|
||||
}
|
||||
|
||||
func (t *testmanagerd) notifyOfPublishedCapabilities() (err error) {
|
||||
_, err = t.client.Connection()
|
||||
return
|
||||
}
|
||||
|
||||
func (t *testmanagerd) requestChannel(channel string) (id uint32, err error) {
|
||||
return t.client.MakeChannel(channel)
|
||||
}
|
||||
|
||||
func (t *testmanagerd) newXCTestManagerDaemon() (xcTestManager XCTestManagerDaemon, err error) {
|
||||
var channelCode uint32
|
||||
if channelCode, err = t.requestChannel("dtxproxy:XCTestManager_IDEInterface:XCTestManager_DaemonConnectionInterface"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xcTestManager = newXcTestManagerDaemon(t, channelCode)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *testmanagerd) invoke(selector string, args *libimobiledevice.AuxBuffer, channelCode uint32, expectsReply bool) (
|
||||
result *libimobiledevice.DTXMessageResult, err error) {
|
||||
return t.client.Invoke(selector, args, channelCode, expectsReply)
|
||||
}
|
||||
|
||||
func (t *testmanagerd) registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult)) {
|
||||
t.client.RegisterCallback(obj, cb)
|
||||
}
|
||||
|
||||
func (t *testmanagerd) close() {
|
||||
t.client.Close()
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var _ Usbmux = (*usbmux)(nil)
|
||||
|
||||
func NewUsbmux() (Usbmux, error) {
|
||||
umClient, err := libimobiledevice.NewUsbmuxClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &usbmux{client: umClient}, nil
|
||||
}
|
||||
|
||||
func newUsbmux(client *libimobiledevice.UsbmuxClient) *usbmux {
|
||||
return &usbmux{client: client}
|
||||
}
|
||||
|
||||
type usbmux struct {
|
||||
client *libimobiledevice.UsbmuxClient
|
||||
}
|
||||
|
||||
func (um *usbmux) Devices() (devices []Device, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = um.client.NewPlistPacket(
|
||||
um.client.NewBasicRequest(libimobiledevice.MessageTypeDeviceList),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = um.client.SendPacket(pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = um.client.ReceivePacket(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply := struct {
|
||||
DeviceList []libimobiledevice.BaseDevice `plist:"DeviceList"`
|
||||
}{}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devices = make([]Device, len(reply.DeviceList))
|
||||
for i := range reply.DeviceList {
|
||||
dev := reply.DeviceList[i]
|
||||
devices[i] = newDevice(um.client, dev.Properties)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (um *usbmux) ReadBUID() (buid string, err error) {
|
||||
var pktReadBUID libimobiledevice.Packet
|
||||
if pktReadBUID, err = um.client.NewPlistPacket(
|
||||
um.client.NewBasicRequest(libimobiledevice.MessageTypeReadBUID),
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = um.client.SendPacket(pktReadBUID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
respPkt, err := um.client.ReceivePacket()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
reply := struct {
|
||||
BUID string `plist:"BUID"`
|
||||
}{}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buid = reply.BUID
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (um *usbmux) Listen(devNotifier chan Device) (context.CancelFunc, error) {
|
||||
baseDevNotifier := make(chan libimobiledevice.BaseDevice)
|
||||
ctx, cancelFunc, err := um.listen(baseDevNotifier)
|
||||
go func(ctx context.Context) {
|
||||
defer close(devNotifier)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case baseDev := <-baseDevNotifier:
|
||||
if baseDev.MessageType != libimobiledevice.MessageTypeDeviceAdd {
|
||||
baseDev.Properties.DeviceID = baseDev.DeviceID
|
||||
}
|
||||
client, err := libimobiledevice.NewUsbmuxClient()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
devNotifier <- newDevice(client, baseDev.Properties)
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
return cancelFunc, err
|
||||
}
|
||||
|
||||
func (um *usbmux) listen(devNotifier chan libimobiledevice.BaseDevice) (ctx context.Context, cancelFunc context.CancelFunc, err error) {
|
||||
var pkt libimobiledevice.Packet
|
||||
if pkt, err = um.client.NewPlistPacket(
|
||||
um.client.NewBasicRequest(libimobiledevice.MessageTypeListen),
|
||||
); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err = um.client.SendPacket(pkt); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc = context.WithCancel(context.Background())
|
||||
|
||||
go func(ctx context.Context) {
|
||||
defer close(devNotifier)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
var respPkt libimobiledevice.Packet
|
||||
if respPkt, err = um.client.ReceivePacket(); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var replyDevice libimobiledevice.BaseDevice
|
||||
if err = respPkt.Unmarshal(&replyDevice); err != nil {
|
||||
break
|
||||
}
|
||||
if replyDevice.MessageType == libimobiledevice.MessageTypeResult {
|
||||
break
|
||||
}
|
||||
|
||||
devNotifier <- replyDevice
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
|
||||
return ctx, cancelFunc, nil
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
)
|
||||
|
||||
var um Usbmux
|
||||
|
||||
func setupUsbmux(t *testing.T) {
|
||||
var err error
|
||||
um, err = NewUsbmux()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_usbmux_Devices(t *testing.T) {
|
||||
setupUsbmux(t)
|
||||
|
||||
devices, err := um.Devices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, dev := range devices {
|
||||
t.Log(dev.Properties().SerialNumber, dev.Properties().ProductID, dev.Properties().DeviceID)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_usbmux_ReadBUID(t *testing.T) {
|
||||
setupUsbmux(t)
|
||||
|
||||
buid, err := um.ReadBUID()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(buid)
|
||||
}
|
||||
|
||||
func Test_usbmux_Listen(t *testing.T) {
|
||||
setupUsbmux(t)
|
||||
|
||||
devNotifier := make(chan Device)
|
||||
cancelFunc, err := um.Listen(devNotifier)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(20 * time.Second)
|
||||
cancelFunc()
|
||||
}()
|
||||
|
||||
for dev := range devNotifier {
|
||||
if dev.Properties().ConnectionType != "" {
|
||||
t.Log(dev.Properties().SerialNumber, dev.Properties().ProductID, dev.Properties().DeviceID)
|
||||
} else {
|
||||
t.Log(libimobiledevice.MessageTypeDeviceRemove, dev.Properties().DeviceID)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
t.Log("Done")
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package gidevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
var _ XCTestManagerDaemon = (*xcTestManagerDaemon)(nil)
|
||||
|
||||
func newXcTestManagerDaemon(testmanagerd Testmanagerd, channelCode uint32) *xcTestManagerDaemon {
|
||||
return &xcTestManagerDaemon{
|
||||
testmanagerd: testmanagerd,
|
||||
channelCode: channelCode,
|
||||
}
|
||||
}
|
||||
|
||||
type xcTestManagerDaemon struct {
|
||||
testmanagerd Testmanagerd
|
||||
channelCode uint32
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) initiateControlSession(XcodeVersion uint64) (err error) {
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(XcodeVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "_IDE_initiateControlSessionWithProtocolVersion:"
|
||||
|
||||
var ret *libimobiledevice.DTXMessageResult
|
||||
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
|
||||
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) startExecutingTestPlan(XcodeVersion uint64) (err error) {
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(XcodeVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "_IDE_startExecutingTestPlanWithProtocolVersion:"
|
||||
|
||||
if _, err = d.testmanagerd.invoke(selector, args, 0xFFFFFFFF, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) initiateSession(XcodeVersion uint64, nsUUID *nskeyedarchiver.NSUUID) (err error) {
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(nsUUID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = args.AppendObject(nsUUID.String() + "-Go-iDevice"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = args.AppendObject("/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = args.AppendObject(XcodeVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "_IDE_initiateSessionWithIdentifier:forClient:atPath:protocolVersion:"
|
||||
|
||||
var ret *libimobiledevice.DTXMessageResult
|
||||
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
|
||||
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) authorizeTestSession(pid int) (err error) {
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "_IDE_authorizeTestSessionWithProcessID:"
|
||||
|
||||
var ret *libimobiledevice.DTXMessageResult
|
||||
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
|
||||
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) initiateControlSessionForTestProcessID(pid int) (err error) {
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "_IDE_initiateControlSessionForTestProcessID:"
|
||||
|
||||
var ret *libimobiledevice.DTXMessageResult
|
||||
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
|
||||
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) initiateControlSessionForTestProcessIDProtocolVersion(pid int, XcodeVersion uint64) (err error) {
|
||||
args := libimobiledevice.NewAuxBuffer()
|
||||
if err = args.AppendObject(pid); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = args.AppendObject(XcodeVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := "_IDE_initiateControlSessionForTestProcessID:protocolVersion:"
|
||||
|
||||
var ret *libimobiledevice.DTXMessageResult
|
||||
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
|
||||
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult)) {
|
||||
d.testmanagerd.registerCallback(obj, cb)
|
||||
}
|
||||
|
||||
func (d *xcTestManagerDaemon) close() {
|
||||
d.testmanagerd.close()
|
||||
}
|
||||
@@ -54,6 +54,7 @@ type LoginRequest struct {
|
||||
PackageName string `json:"packageName"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
Captcha string `json:"captcha"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type LogoutRequest struct {
|
||||
|
||||
@@ -105,7 +105,7 @@ func loginHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = dExt.Driver.LoginNoneUI(loginReq.PackageName, loginReq.PhoneNumber, loginReq.Captcha)
|
||||
info, err := dExt.Driver.LoginNoneUI(loginReq.PackageName, loginReq.PhoneNumber, loginReq.Captcha, loginReq.Password)
|
||||
if err != nil {
|
||||
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to login", c.HandlerName()))
|
||||
c.JSON(http.StatusInternalServerError,
|
||||
@@ -117,7 +117,7 @@ func loginHandler(c *gin.Context) {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, HttpResponse{Code: 0, Message: "success"})
|
||||
c.JSON(http.StatusOK, HttpResponse{Code: 0, Message: "success", Result: info})
|
||||
}
|
||||
|
||||
func logoutHandler(c *gin.Context) {
|
||||
|
||||
@@ -15,24 +15,25 @@ import (
|
||||
type ActionMethod string
|
||||
|
||||
const (
|
||||
ACTION_LOG ActionMethod = "log"
|
||||
ACTION_AppInstall ActionMethod = "install"
|
||||
ACTION_AppUninstall ActionMethod = "uninstall"
|
||||
ACTION_AppClear ActionMethod = "app_clear"
|
||||
ACTION_AppStart ActionMethod = "app_start"
|
||||
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
|
||||
ACTION_AppTerminate ActionMethod = "app_terminate"
|
||||
ACTION_AppStop ActionMethod = "app_stop"
|
||||
ACTION_ScreenShot ActionMethod = "screenshot"
|
||||
ACTION_Sleep ActionMethod = "sleep"
|
||||
ACTION_SleepMS ActionMethod = "sleep_ms"
|
||||
ACTION_SleepRandom ActionMethod = "sleep_random"
|
||||
ACTION_StartCamera ActionMethod = "camera_start" // alias for app_launch camera
|
||||
ACTION_StopCamera ActionMethod = "camera_stop" // alias for app_terminate camera
|
||||
ACTION_SetClipboard ActionMethod = "set_clipboard"
|
||||
ACTION_GetClipboard ActionMethod = "get_clipboard"
|
||||
ACTION_SetIme ActionMethod = "set_ime"
|
||||
ACTION_GetSource ActionMethod = "get_source"
|
||||
ACTION_LOG ActionMethod = "log"
|
||||
ACTION_AppInstall ActionMethod = "install"
|
||||
ACTION_AppUninstall ActionMethod = "uninstall"
|
||||
ACTION_AppClear ActionMethod = "app_clear"
|
||||
ACTION_AppStart ActionMethod = "app_start"
|
||||
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
|
||||
ACTION_AppTerminate ActionMethod = "app_terminate"
|
||||
ACTION_AppStop ActionMethod = "app_stop"
|
||||
ACTION_ScreenShot ActionMethod = "screenshot"
|
||||
ACTION_Sleep ActionMethod = "sleep"
|
||||
ACTION_SleepMS ActionMethod = "sleep_ms"
|
||||
ACTION_SleepRandom ActionMethod = "sleep_random"
|
||||
ACTION_StartCamera ActionMethod = "camera_start" // alias for app_launch camera
|
||||
ACTION_StopCamera ActionMethod = "camera_stop" // alias for app_terminate camera
|
||||
ACTION_SetClipboard ActionMethod = "set_clipboard"
|
||||
ACTION_GetClipboard ActionMethod = "get_clipboard"
|
||||
ACTION_SetIme ActionMethod = "set_ime"
|
||||
ACTION_GetSource ActionMethod = "get_source"
|
||||
ACTION_GetForegroundApp ActionMethod = "get_foreground_app"
|
||||
|
||||
// UI handling
|
||||
ACTION_Home ActionMethod = "home"
|
||||
@@ -46,6 +47,7 @@ const (
|
||||
ACTION_Swipe ActionMethod = "swipe"
|
||||
ACTION_Input ActionMethod = "input"
|
||||
ACTION_Back ActionMethod = "back"
|
||||
ACTION_KeyCode ActionMethod = "keycode"
|
||||
|
||||
// custom actions
|
||||
ACTION_SwipeToTapApp ActionMethod = "swipe_to_tap_app" // swipe left & right to find app and tap
|
||||
@@ -109,7 +111,8 @@ type ActionOptions struct {
|
||||
MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times
|
||||
IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found
|
||||
Interval float64 `json:"interval,omitempty" yaml:"interval,omitempty"` // interval between retries in seconds
|
||||
PressDuration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action
|
||||
Duration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action
|
||||
PressDuration float64 `json:"press_duration,omitempty" yaml:"press_duration,omitempty"` // used to set duration of ios swipe action
|
||||
Steps int `json:"steps,omitempty" yaml:"steps,omitempty"` // used to set steps of android swipe action
|
||||
Direction interface{} `json:"direction,omitempty" yaml:"direction,omitempty"` // used by swipe to tap text or app
|
||||
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action
|
||||
@@ -159,6 +162,9 @@ func (o *ActionOptions) Options() []ActionOption {
|
||||
if o.Interval != 0 {
|
||||
options = append(options, WithInterval(o.Interval))
|
||||
}
|
||||
if o.Duration != 0 {
|
||||
options = append(options, WithDuration(o.Duration))
|
||||
}
|
||||
if o.PressDuration != 0 {
|
||||
options = append(options, WithPressDuration(o.PressDuration))
|
||||
}
|
||||
@@ -309,8 +315,8 @@ func (o *ActionOptions) updateData(data map[string]interface{}) {
|
||||
data["steps"] = 12 // default steps
|
||||
}
|
||||
|
||||
if o.PressDuration > 0 {
|
||||
data["duration"] = o.PressDuration
|
||||
if o.Duration > 0 {
|
||||
data["duration"] = o.Duration
|
||||
}
|
||||
if _, ok := data["duration"]; !ok {
|
||||
data["duration"] = 0 // default duration
|
||||
@@ -380,9 +386,15 @@ func WithInterval(sec float64) ActionOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithPressDuration(duration float64) ActionOption {
|
||||
func WithDuration(duration float64) ActionOption {
|
||||
return func(o *ActionOptions) {
|
||||
o.PressDuration = duration
|
||||
o.Duration = duration
|
||||
}
|
||||
}
|
||||
|
||||
func WithPressDuration(pressDuration float64) ActionOption {
|
||||
return func(o *ActionOptions) {
|
||||
o.PressDuration = pressDuration
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,15 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/funplugin/myexec"
|
||||
@@ -245,6 +248,34 @@ func (ad *adbDriver) Unlock() (err error) {
|
||||
return ad.PressKeyCodes(KCMenu, KMEmpty)
|
||||
}
|
||||
|
||||
func (ad *adbDriver) Backspace(count int, options ...ActionOption) (err error) {
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
if count == 1 {
|
||||
return ad.PressKeyCode(67)
|
||||
}
|
||||
keyArray := make([]KeyCode, count)
|
||||
|
||||
for i := range keyArray {
|
||||
keyArray[i] = KeyCode(67)
|
||||
}
|
||||
return ad.combinationKey(keyArray)
|
||||
}
|
||||
|
||||
func (ad *adbDriver) combinationKey(keyCodes []KeyCode) (err error) {
|
||||
if len(keyCodes) == 1 {
|
||||
return ad.PressKeyCode(keyCodes[0])
|
||||
}
|
||||
strKeyCodes := make([]string, len(keyCodes))
|
||||
for i, keycode := range keyCodes {
|
||||
strKeyCodes[i] = fmt.Sprintf("%d", keycode)
|
||||
}
|
||||
_, err = ad.adbClient.RunShellCommand(
|
||||
"input", append([]string{"keycombination"}, strKeyCodes...)...)
|
||||
return
|
||||
}
|
||||
|
||||
func (ad *adbDriver) PressKeyCode(keyCode KeyCode) (err error) {
|
||||
return ad.PressKeyCodes(keyCode, KMEmpty)
|
||||
}
|
||||
@@ -309,15 +340,11 @@ func (ad *adbDriver) TapFloat(x, y float64, options ...ActionOption) (err error)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *adbDriver) DoubleTap(x, y int, options ...ActionOption) error {
|
||||
return ad.DoubleTapFloat(float64(x), float64(y), options...)
|
||||
}
|
||||
|
||||
func (ad *adbDriver) DoubleTapFloat(x, y float64, options ...ActionOption) (err error) {
|
||||
func (ad *adbDriver) DoubleTap(x, y float64, options ...ActionOption) error {
|
||||
// adb shell input tap x y
|
||||
xStr := fmt.Sprintf("%.1f", x)
|
||||
yStr := fmt.Sprintf("%.1f", y)
|
||||
_, err = ad.adbClient.RunShellCommand(
|
||||
_, err := ad.adbClient.RunShellCommand(
|
||||
"input", "tap", xStr, yStr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("tap <%s, %s> failed", xStr, yStr))
|
||||
@@ -331,22 +358,64 @@ func (ad *adbDriver) DoubleTapFloat(x, y float64, options ...ActionOption) (err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *adbDriver) TouchAndHold(x, y int, second ...float64) (err error) {
|
||||
return ad.TouchAndHoldFloat(float64(x), float64(y), second...)
|
||||
func (ad *adbDriver) TouchAndHold(x, y float64, options ...ActionOption) (err error) {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
|
||||
if len(actionOptions.Offset) == 2 {
|
||||
x += float64(actionOptions.Offset[0])
|
||||
y += float64(actionOptions.Offset[1])
|
||||
}
|
||||
x += actionOptions.getRandomOffset()
|
||||
y += actionOptions.getRandomOffset()
|
||||
duration := 1000.0
|
||||
if actionOptions.Duration > 0 {
|
||||
duration = actionOptions.Duration * 1000
|
||||
}
|
||||
// adb shell input swipe fromX fromY toX toY
|
||||
_, err = ad.adbClient.RunShellCommand(
|
||||
"input", "swipe",
|
||||
fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y),
|
||||
fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y),
|
||||
fmt.Sprintf("%d", int(duration)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "long press failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *adbDriver) TouchAndHoldFloat(x, y float64, second ...float64) (err error) {
|
||||
err = errDriverNotImplemented
|
||||
return
|
||||
}
|
||||
func (ad *adbDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
|
||||
func (ad *adbDriver) Drag(fromX, fromY, toX, toY int, options ...ActionOption) error {
|
||||
return ad.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...)
|
||||
}
|
||||
|
||||
func (ad *adbDriver) DragFloat(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
|
||||
err = errDriverNotImplemented
|
||||
return
|
||||
if len(actionOptions.Offset) == 4 {
|
||||
fromX += float64(actionOptions.Offset[0])
|
||||
fromY += float64(actionOptions.Offset[1])
|
||||
toX += float64(actionOptions.Offset[2])
|
||||
toY += float64(actionOptions.Offset[3])
|
||||
}
|
||||
fromX += actionOptions.getRandomOffset()
|
||||
fromY += actionOptions.getRandomOffset()
|
||||
toX += actionOptions.getRandomOffset()
|
||||
toY += actionOptions.getRandomOffset()
|
||||
duration := 200.0
|
||||
if actionOptions.Duration > 0 {
|
||||
duration = actionOptions.Duration * 1000
|
||||
}
|
||||
command := "swipe"
|
||||
if actionOptions.PressDuration > 0 {
|
||||
command = "draganddrop"
|
||||
}
|
||||
// adb shell input swipe fromX fromY toX toY
|
||||
_, err = ad.adbClient.RunShellCommand(
|
||||
"input", command,
|
||||
fmt.Sprintf("%.1f", fromX), fmt.Sprintf("%.1f", fromY),
|
||||
fmt.Sprintf("%.1f", toX), fmt.Sprintf("%.1f", toY),
|
||||
fmt.Sprintf("%d", int(duration)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "drag failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *adbDriver) Swipe(fromX, fromY, toX, toY int, options ...ActionOption) error {
|
||||
@@ -557,8 +626,8 @@ func (ad *adbDriver) Source(srcOpt ...SourceOption) (source string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ad *adbDriver) LoginNoneUI(packageName, phoneNumber string, captcha string) error {
|
||||
return errDriverNotImplemented
|
||||
func (ad *adbDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
|
||||
return info, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ad *adbDriver) LogoutNoneUI(packageName string) error {
|
||||
@@ -750,6 +819,10 @@ func (ad *adbDriver) GetSession() *DriverSession {
|
||||
return &ad.Driver.session
|
||||
}
|
||||
|
||||
func (ad *adbDriver) GetDriverResults() []*DriverResult {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *adbDriver) GetForegroundApp() (app AppInfo, err error) {
|
||||
packageInfo, err := ad.adbClient.RunShellCommand(
|
||||
"CLASSPATH=/data/local/tmp/evalite", "app_process", "/",
|
||||
@@ -916,3 +989,63 @@ var androidActivities = map[string]map[string][]string{
|
||||
},
|
||||
// TODO: SPH, XHS
|
||||
}
|
||||
|
||||
func (ad *adbDriver) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) {
|
||||
// 获取当前时间戳
|
||||
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
|
||||
// 创建文件名
|
||||
fileName := fmt.Sprintf("%s/%s.mp4", folderPath, timestamp)
|
||||
err = os.MkdirAll(folderPath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Error creating directory")
|
||||
}
|
||||
|
||||
// 创建一个文件
|
||||
file, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
log.Error().Err(err)
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
// scrcpy -s 7d21bb91 --record=file.mp4 -N
|
||||
cmd := exec.Command(
|
||||
"scrcpy",
|
||||
"-s", ad.adbClient.Serial(),
|
||||
fmt.Sprintf("--record=%s", fileName),
|
||||
"-N",
|
||||
)
|
||||
cmd.Stdout = io.Discard
|
||||
cmd.Stderr = io.Discard
|
||||
// 启动命令
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Error().Err(err)
|
||||
return "", err
|
||||
}
|
||||
timer := time.After(duration)
|
||||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
// 等待 ffmpeg 命令执行完毕
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
select {
|
||||
case <-timer:
|
||||
// 超时,停止 scrcpy 进程
|
||||
if err := cmd.Process.Signal(syscall.SIGINT); err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
case err := <-done:
|
||||
// ffmpeg 正常结束
|
||||
if err != nil {
|
||||
log.Error().Err(err)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return filepath.Abs(fileName)
|
||||
}
|
||||
|
||||
func (ad *adbDriver) TearDown() {
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user