Skip to content

Commit

Permalink
feat: Simulate location for iOS17 (danielpaulus#403)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
kvs-coder and fish-sauce authored May 2, 2024
1 parent ac4e40b commit fe5e60b
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 3 deletions.
1 change: 1 addition & 0 deletions ios/debugproxy/binforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var serviceConfigurations = map[string]serviceConfig{
"com.apple.accessibility.axAuditDaemon.remoteserver": {NewDtxDecoder, true},
"com.apple.testmanagerd.lockdown": {NewDtxDecoder, true},
"com.apple.debugserver": {NewBinDumpOnly, true},
"com.apple.instruments.dtservicehub": {NewDtxDecoder, false},
"com.apple.instruments.remoteserver.DVTSecureSocketProxy": {NewDtxDecoder, false},
"com.apple.testmanagerd.lockdown.secure": {NewDtxDecoder, false},
"bindumper": {NewBinDumpOnly, false},
Expand Down
5 changes: 5 additions & 0 deletions ios/instruments/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
const (
serviceName string = "com.apple.instruments.remoteserver"
serviceNameiOS14 string = "com.apple.instruments.remoteserver.DVTSecureSocketProxy"
serviceNameRsd string = "com.apple.instruments.dtservicehub"
)

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

func connectInstruments(device ios.DeviceEntry) (*dtx.Connection, error) {
if device.SupportsRsd() {
log.Debugf("Connecting to %s", serviceNameRsd)
return dtx.NewTunnelConnection(device, serviceNameRsd)
}
dtxConn, err := dtx.NewUsbmuxdConnection(device, serviceName)
if err != nil {
log.Debugf("Failed connecting to %s, trying %s", serviceName, serviceNameiOS14)
Expand Down
50 changes: 50 additions & 0 deletions ios/instruments/location_simulation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package instruments

import (
"github.com/danielpaulus/go-ios/ios"
dtx "github.com/danielpaulus/go-ios/ios/dtx_codec"
)

const locationSimulationIdentifier = "com.apple.instruments.server.services.LocationSimulation"

// LocationSimulationService gives us access to simulate device geo location
type LocationSimulationService struct {
channel *dtx.Channel
conn *dtx.Connection
}

// StartSimulateLocation sets geolocation with provided params
func (d *LocationSimulationService) StartSimulateLocation(lat, lon float64) error {
_, err := d.channel.MethodCall("simulateLocationWithLatitude:longitude:", lat, lon)
if err != nil {
return err
}

return nil
}

// StopSimulateLocation clears simulated location
func (d *LocationSimulationService) StopSimulateLocation() error {
_, err := d.channel.MethodCall("stopLocationSimulation")
if err != nil {
return err
}
defer d.Close()

return nil
}

// NewLocationSimulationService creates a new LocationSimulationService for a given device
func NewLocationSimulationService(device ios.DeviceEntry) (*LocationSimulationService, error) {
dtxConn, err := connectInstruments(device)
if err != nil {
return nil, err
}
processControlChannel := dtxConn.RequestChannelIdentifier(locationSimulationIdentifier, loggingDispatcher{dtxConn})
return &LocationSimulationService{channel: processControlChannel, conn: dtxConn}, nil
}

// Close closes up the DTX connection
func (d *LocationSimulationService) Close() {
d.conn.Close()
}
6 changes: 6 additions & 0 deletions ios/listdevices.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func NewReadDevices() ReadDevicesType {
return data
}

// SupportsRsd checks if the device supports RSD (Remote Service Discovery).
// It returns true if the device has RSD capability, otherwise false.
func (device *DeviceEntry) SupportsRsd() bool {
return device.Rsd != nil
}

// ListDevices returns a DeviceList containing data about all
// currently connected iOS devices
func (muxConn *UsbMuxConnection) ListDevices() (DeviceList, error) {
Expand Down
39 changes: 36 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"path/filepath"
"runtime/debug"
"sort"
"strconv"
"strings"
"syscall"
"time"
Expand Down Expand Up @@ -156,7 +157,7 @@ The commands work as following:
ios info [display | lockdown] [options] Prints a dump of device information from the given source.
ios image list [options] List currently mounted developers images' signatures
ios image mount [--path=<imagepath>] [options] Mount a image from <imagepath>
> For iOS 17+ (personalized developer disk images) <imagepath> must point to the "Restore" directory inside the developer disk
> For iOS 17+ (personalized developer disk images) <imagepath> must point to the "Restore" directory inside the developer disk
ios image auto [--basedir=<where_dev_images_are_stored>] [options] Automatically download correct dev image from the internets and mount it.
> You can specify a dir where images should be cached.
> The default is the current dir.
Expand Down Expand Up @@ -216,7 +217,7 @@ The commands work as following:
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.
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.
ios kill (<bundleID> | --pid=<processID> | --process=<processName>) [options] Kill app with the specified bundleID, process id, or process name on the device.
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.
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.
> If you provide '-' as log output, it prints resuts to stdout.
> 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)
> The method name can also be omitted and in this case all tests of the specified class are run
Expand All @@ -242,7 +243,7 @@ The commands work as following:
> 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
> This command needs to be executed with admin privileges.
> (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)
ios tunnel ls List currently started tunnels
ios tunnel ls List currently started tunnels
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.
`, version)
Expand Down Expand Up @@ -605,6 +606,15 @@ The commands work as following:
if b {
lat, _ := arguments.String("--lat")
lon, _ := arguments.String("--lon")

if device.SupportsRsd() {
server, err := instruments.NewLocationSimulationService(device)
exitIfError("failed to create location simulation service:", err)

startLocationSimulation(server, lat, lon)
return
}

setLocation(device, lat, lon)
return
}
Expand Down Expand Up @@ -1730,6 +1740,29 @@ func setLocationGPX(device ios.DeviceEntry, gpxFilePath string) {
exitIfError("Setting location failed with", err)
}

func startLocationSimulation(service *instruments.LocationSimulationService, lat string, lon string) {
latitude, err := strconv.ParseFloat(lat, 64)
exitIfError("location simulation failed to parse lat", err)

longitude, err := strconv.ParseFloat(lon, 64)
exitIfError("location simulation failed to parse lon", err)

err = service.StartSimulateLocation(latitude, longitude)
exitIfError("location simulation failed to start with", err)

defer stopLocationSimulation(service)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
}

func stopLocationSimulation(service *instruments.LocationSimulationService) {
err := service.StopSimulateLocation()
if err != nil {
exitIfError("location simulation failed to stop with", err)
}
}

func resetLocation(device ios.DeviceEntry) {
err := simlocation.ResetLocation(device)
exitIfError("Resetting location failed with", err)
Expand Down

0 comments on commit fe5e60b

Please sign in to comment.