Skip to content

Commit fe5e60b

Browse files
Victorfish-sauce
Victor
andauthored
feat: Simulate location for iOS17 (danielpaulus#403)
The current setlocation only works for iOS below 17, the iOS 17 introduces a new instrumentation service com.apple.instruments.dtservicehub with the channel id com.apple.instruments.server.services.LocationSimulation for the simulated location. Co-authored-by: fish-sauce <victor.kachalov@saucelabs.com>
1 parent ac4e40b commit fe5e60b

File tree

5 files changed

+98
-3
lines changed

5 files changed

+98
-3
lines changed

ios/debugproxy/binforward.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var serviceConfigurations = map[string]serviceConfig{
2222
"com.apple.accessibility.axAuditDaemon.remoteserver": {NewDtxDecoder, true},
2323
"com.apple.testmanagerd.lockdown": {NewDtxDecoder, true},
2424
"com.apple.debugserver": {NewBinDumpOnly, true},
25+
"com.apple.instruments.dtservicehub": {NewDtxDecoder, false},
2526
"com.apple.instruments.remoteserver.DVTSecureSocketProxy": {NewDtxDecoder, false},
2627
"com.apple.testmanagerd.lockdown.secure": {NewDtxDecoder, false},
2728
"bindumper": {NewBinDumpOnly, false},

ios/instruments/helper.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
const (
1313
serviceName string = "com.apple.instruments.remoteserver"
1414
serviceNameiOS14 string = "com.apple.instruments.remoteserver.DVTSecureSocketProxy"
15+
serviceNameRsd string = "com.apple.instruments.dtservicehub"
1516
)
1617

1718
type loggingDispatcher struct {
@@ -24,6 +25,10 @@ func (p loggingDispatcher) Dispatch(m dtx.Message) {
2425
}
2526

2627
func connectInstruments(device ios.DeviceEntry) (*dtx.Connection, error) {
28+
if device.SupportsRsd() {
29+
log.Debugf("Connecting to %s", serviceNameRsd)
30+
return dtx.NewTunnelConnection(device, serviceNameRsd)
31+
}
2732
dtxConn, err := dtx.NewUsbmuxdConnection(device, serviceName)
2833
if err != nil {
2934
log.Debugf("Failed connecting to %s, trying %s", serviceName, serviceNameiOS14)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package instruments
2+
3+
import (
4+
"github.com/danielpaulus/go-ios/ios"
5+
dtx "github.com/danielpaulus/go-ios/ios/dtx_codec"
6+
)
7+
8+
const locationSimulationIdentifier = "com.apple.instruments.server.services.LocationSimulation"
9+
10+
// LocationSimulationService gives us access to simulate device geo location
11+
type LocationSimulationService struct {
12+
channel *dtx.Channel
13+
conn *dtx.Connection
14+
}
15+
16+
// StartSimulateLocation sets geolocation with provided params
17+
func (d *LocationSimulationService) StartSimulateLocation(lat, lon float64) error {
18+
_, err := d.channel.MethodCall("simulateLocationWithLatitude:longitude:", lat, lon)
19+
if err != nil {
20+
return err
21+
}
22+
23+
return nil
24+
}
25+
26+
// StopSimulateLocation clears simulated location
27+
func (d *LocationSimulationService) StopSimulateLocation() error {
28+
_, err := d.channel.MethodCall("stopLocationSimulation")
29+
if err != nil {
30+
return err
31+
}
32+
defer d.Close()
33+
34+
return nil
35+
}
36+
37+
// NewLocationSimulationService creates a new LocationSimulationService for a given device
38+
func NewLocationSimulationService(device ios.DeviceEntry) (*LocationSimulationService, error) {
39+
dtxConn, err := connectInstruments(device)
40+
if err != nil {
41+
return nil, err
42+
}
43+
processControlChannel := dtxConn.RequestChannelIdentifier(locationSimulationIdentifier, loggingDispatcher{dtxConn})
44+
return &LocationSimulationService{channel: processControlChannel, conn: dtxConn}, nil
45+
}
46+
47+
// Close closes up the DTX connection
48+
func (d *LocationSimulationService) Close() {
49+
d.conn.Close()
50+
}

ios/listdevices.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ func NewReadDevices() ReadDevicesType {
8282
return data
8383
}
8484

85+
// SupportsRsd checks if the device supports RSD (Remote Service Discovery).
86+
// It returns true if the device has RSD capability, otherwise false.
87+
func (device *DeviceEntry) SupportsRsd() bool {
88+
return device.Rsd != nil
89+
}
90+
8591
// ListDevices returns a DeviceList containing data about all
8692
// currently connected iOS devices
8793
func (muxConn *UsbMuxConnection) ListDevices() (DeviceList, error) {

main.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"path/filepath"
1515
"runtime/debug"
1616
"sort"
17+
"strconv"
1718
"strings"
1819
"syscall"
1920
"time"
@@ -156,7 +157,7 @@ The commands work as following:
156157
ios info [display | lockdown] [options] Prints a dump of device information from the given source.
157158
ios image list [options] List currently mounted developers images' signatures
158159
ios image mount [--path=<imagepath>] [options] Mount a image from <imagepath>
159-
> For iOS 17+ (personalized developer disk images) <imagepath> must point to the "Restore" directory inside the developer disk
160+
> For iOS 17+ (personalized developer disk images) <imagepath> must point to the "Restore" directory inside the developer disk
160161
ios image auto [--basedir=<where_dev_images_are_stored>] [options] Automatically download correct dev image from the internets and mount it.
161162
> You can specify a dir where images should be cached.
162163
> The default is the current dir.
@@ -216,7 +217,7 @@ The commands work as following:
216217
ios apps [--system] [--all] [--list] [--filesharing] Retrieves a list of installed applications. --system prints out preinstalled system apps. --all prints all apps, including system, user, and hidden apps. --list only prints bundle ID, bundle name and version number. --filesharing only prints apps which enable documents sharing.
217218
ios launch <bundleID> [--wait] Launch app with the bundleID on the device. Get your bundle ID from the apps command. --wait keeps the connection open if you want logs.
218219
ios kill (<bundleID> | --pid=<processID> | --process=<processName>) [options] Kill app with the specified bundleID, process id, or process name on the device.
219-
ios runtest [--bundle-id=<bundleid>] [--test-runner-bundle-id=<testbundleid>] [--xctest-config=<xctestconfig>] [--log-output=<file>] [--test-to-run=<tests>]... [--test-to-skip=<tests>]... [--env=<e>]... [options] Run a XCUITest. If you provide only bundle-id go-ios will try to dynamically create test-runner-bundle-id and xctest-config.
220+
ios runtest [--bundle-id=<bundleid>] [--test-runner-bundle-id=<testbundleid>] [--xctest-config=<xctestconfig>] [--log-output=<file>] [--test-to-run=<tests>]... [--test-to-skip=<tests>]... [--env=<e>]... [options] Run a XCUITest. If you provide only bundle-id go-ios will try to dynamically create test-runner-bundle-id and xctest-config.
220221
> If you provide '-' as log output, it prints resuts to stdout.
221222
> To be able to filter for tests to run or skip, use one argument per test selector. Example: runtest --test-to-run=(TestTarget.)TestClass/testMethod --test-to-run=(TestTarget.)TestClass/testMethod (the value for 'TestTarget' is optional)
222223
> The method name can also be omitted and in this case all tests of the specified class are run
@@ -242,7 +243,7 @@ The commands work as following:
242243
> On systems with System Integrity Protection enabled the argument '--pair-record-path' is required as we can not access the default path for the pair record
243244
> This command needs to be executed with admin privileges.
244245
> (On MacOS the process 'remoted' must be paused before starting a tunnel is possible 'sudo pkill -SIGSTOP remoted', and 'sudo pkill -SIGCONT remoted' to resume)
245-
ios tunnel ls List currently started tunnels
246+
ios tunnel ls List currently started tunnels
246247
ios devmode (enable | get) [--enable-post-restart] [options] Enable developer mode on the device or check if it is enabled. Can also completely finalize developer mode setup after device is restarted.
247248
248249
`, version)
@@ -605,6 +606,15 @@ The commands work as following:
605606
if b {
606607
lat, _ := arguments.String("--lat")
607608
lon, _ := arguments.String("--lon")
609+
610+
if device.SupportsRsd() {
611+
server, err := instruments.NewLocationSimulationService(device)
612+
exitIfError("failed to create location simulation service:", err)
613+
614+
startLocationSimulation(server, lat, lon)
615+
return
616+
}
617+
608618
setLocation(device, lat, lon)
609619
return
610620
}
@@ -1730,6 +1740,29 @@ func setLocationGPX(device ios.DeviceEntry, gpxFilePath string) {
17301740
exitIfError("Setting location failed with", err)
17311741
}
17321742

1743+
func startLocationSimulation(service *instruments.LocationSimulationService, lat string, lon string) {
1744+
latitude, err := strconv.ParseFloat(lat, 64)
1745+
exitIfError("location simulation failed to parse lat", err)
1746+
1747+
longitude, err := strconv.ParseFloat(lon, 64)
1748+
exitIfError("location simulation failed to parse lon", err)
1749+
1750+
err = service.StartSimulateLocation(latitude, longitude)
1751+
exitIfError("location simulation failed to start with", err)
1752+
1753+
defer stopLocationSimulation(service)
1754+
c := make(chan os.Signal, 1)
1755+
signal.Notify(c, os.Interrupt)
1756+
<-c
1757+
}
1758+
1759+
func stopLocationSimulation(service *instruments.LocationSimulationService) {
1760+
err := service.StopSimulateLocation()
1761+
if err != nil {
1762+
exitIfError("location simulation failed to stop with", err)
1763+
}
1764+
}
1765+
17331766
func resetLocation(device ios.DeviceEntry) {
17341767
err := simlocation.ResetLocation(device)
17351768
exitIfError("Resetting location failed with", err)

0 commit comments

Comments
 (0)