Schlaubischlump / LocationSimulator

MacOS application to spoof / fake / mock your iOS / iPadOS or iPhoneSimulator device location. WatchOS and TvOS are partially supported.
https://schlaubischlump.github.io/LocationSimulator/
GNU General Public License v3.0
2.53k stars 196 forks source link

[Feature] Location heading on Simulator #175

Closed Wouter01 closed 1 year ago

Wouter01 commented 1 year ago

Is your feature request related to a problem? Please describe. Currently, spoofing a path works with LocationSimulator on an iOS simulator. However, location heading doesn't seem to update, and is always pointing to north.

Describe the solution you'd like I did some research myself and turns out it's possible to get an accurate (simulated) heading with the simctl tool (for example, using xcrun simctl location <deviceid> start --distance=10 --speed=20 37.629538,-122.395733 40.628083,-80,768254). This tool also uses CoreSimulator, so I went to have a look there and there are some additional useful methods there:

- (BOOL)_startLocationSimulationWithDistance:(double)arg1 interval:(double)arg2 speed:(double)arg3 waypoints:(id)arg4 error:(id*)arg5;
- (id)availableLocationScenarios;
- (BOOL)clearSimulatedLocationWithError:(id*)arg1;
- (BOOL)setLocationScenario:(id)arg1 error:(id*)arg2;
- (BOOL)setLocationScenarioWithPath:(id)arg1 error:(id*)arg2;
- (BOOL)setLocationWithLatitude:(double)arg1 andLongitude:(double)arg2 error:(id*)arg3;
- (BOOL)startLocationSimulationWithDistance:(double)arg1 speed:(double)arg2 waypoints:(id)arg3 error:(id*)arg4;
- (BOOL)startLocationSimulationWithInterval:(double)arg1 speed:(double)arg2 waypoints:(id)arg3 error:(id*)arg4;

I did some digging in hopper and got this working with the following arguments:

public func simulatePath(waypoints: [CLLocationCoordinate2D]) throws {
    let waypoints = waypoints.map { [$0.latitude, $0.longitude] }.flatMap { $0 }

    try self.wrapper.device().startLocationSimulation(withDistance: 10, speed: 20, waypoints: waypoints)
}

This achieves the same functionality as the LocationSpoofer class, tracking speed and other things itself. I wonder if such a thing could live in the app? The main issue I'm thinking of is that you don't know when a simulation has ended, and that it isn't available on physical devices afaik.

Schlaubischlump commented 1 year ago

Hi @Wouter01,

As you have figured out, there are a couple of more CoreSimulator methods one can use. I decided against using them primarily for the reason you pointed out as well (feature parity between iOS and iPhoneSimulator).

Just for my clarification: Are we talking about the heading not being displayed in LocationSimulator or are we talking about the speed and course properties of a CLLocation object ?

Since I got all the information about previous and next locations I should be able to just calculate the direction you are currently heading to and update the UI or do I miss something here ? Regarding the missing information of a CLLocation object, there is probably not much I can do...

Edit: Is this maybe the same feature request as #172 ?

Wouter01 commented 1 year ago

Apologies if the issue was not clear. The issue I'm having is that the heading isn't updated in the iOS simulator. This seems to happen because the heading is updated internally in CoreSimulator by comparing with the previous location, but it doesn't seem to work if you set the location point by point. It indeed seems to be related to #172, but that issue was not completely clear to me

Schlaubischlump commented 1 year ago

Alright got you.

Looking at the disassembled source code of CoreSimulator reveals the following:

The following is called internally when calling setLocationWithLatitude:

float ___69-[SimDevice(SimLocation) setLocationWithLatitude:andLongitude:error:]_block_invoke(int arg0, int arg1, int arg2, int arg3) {
    xmm0 = *(arg0 + 0x20);
    _host_support_mig_set_location_lat_lon(arg1, arg2, arg3, arg3, r8, r9);
    return xmm0;
}

This is called internally when using startLocationSimulationWithDistance:

int ___94-[SimDevice(SimLocation) _startLocationSimulationWithDistance:interval:speed:waypoints:error:]_block_invoke(int arg0, int arg1, int arg2, int arg3) {
    _obj_to_wire(*(arg0 + 0x20), &var_40, &var_38, 0x0);
    rbx = _host_support_mig_set_location_waypoints(arg1, var_40, var_38, arg2, arg3, r9);
    free(var_40);
    rax = rbx;
    return rax;
}

I'll try to look deeper into it. Maybe this _host_support_mig_set_location_waypoints somehow sets the heading, while _host_support_mig_set_location_lat_lon does not.

Edit: Hard to say what _host_support_mig_set_location_waypoints is actually doing. It is just sending some messages to the XPC service and it is not clear what any of them are doing. But I still think somewhere inside of this method the heading must be updated...

Wouter01 commented 1 year ago

Thanks for looking into this. I stumbled on this too, as most of the things are hard to discover. I had the xpc service crash multiple times during my testings. If I discover something new I’ll let you know

Wouter01 commented 1 year ago

I have no clue what happened, but it seems to be working fine now with LocationSimulator. I have no clue why it's working now, as it wasn't for the past few days. (I have updated to 13.5 this morning, and Xcode did reconfigure afterwards, but I doubt that has anything to do with it)

Schlaubischlump commented 1 year ago

Interesting... This is really strange. Let me know if you figure out why it suddenly started working.