android-mock-location-mcp 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -25
- package/dist/adb.d.ts +9 -25
- package/dist/adb.js +15 -67
- package/dist/adb.js.map +1 -1
- package/dist/emulator.d.ts +52 -0
- package/dist/emulator.js +175 -0
- package/dist/emulator.js.map +1 -0
- package/dist/geo-math.d.ts +0 -2
- package/dist/geo-math.js +0 -8
- package/dist/geo-math.js.map +1 -1
- package/dist/gpx-kml.d.ts +23 -0
- package/dist/gpx-kml.js +200 -0
- package/dist/gpx-kml.js.map +1 -0
- package/dist/index.js +612 -211
- package/dist/index.js.map +1 -1
- package/dist/routing.d.ts +0 -4
- package/dist/routing.js +7 -23
- package/dist/routing.js.map +1 -1
- package/package.json +9 -5
- package/dist/adb-tracker.d.ts +0 -15
- package/dist/adb-tracker.js +0 -176
- package/dist/adb-tracker.js.map +0 -1
- package/dist/device.d.ts +0 -15
- package/dist/device.js +0 -313
- package/dist/device.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
# MCP Server — android-mock-location-mcp
|
|
2
2
|
|
|
3
|
-
MCP server that exposes
|
|
3
|
+
MCP server that exposes 11 tools for controlling Android emulator GPS location. Sets mock locations via `adb emu geo fix` and supports geocoding and street-level routing through configurable providers.
|
|
4
4
|
|
|
5
5
|
See the [root README](../README.md) for project overview and quick start.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
npx android-mock-location-mcp
|
|
12
|
-
```
|
|
9
|
+
The server is launched automatically by your MCP client — you don't run it directly. The installation method determines how the client finds and starts it.
|
|
10
|
+
|
|
11
|
+
**npx (no install needed):** Use `npx -y android-mock-location-mcp` as the command in your MCP client config. The client will download and run it automatically.
|
|
13
12
|
|
|
14
|
-
**Global install:**
|
|
13
|
+
**Global install:** Pre-install the package so the client can launch it without download delay:
|
|
15
14
|
```bash
|
|
16
15
|
npm install -g android-mock-location-mcp
|
|
17
|
-
android-mock-location-mcp
|
|
18
16
|
```
|
|
19
17
|
|
|
20
18
|
**Build from source:**
|
|
@@ -22,9 +20,12 @@ android-mock-location-mcp
|
|
|
22
20
|
cd server
|
|
23
21
|
npm install
|
|
24
22
|
npm run build
|
|
25
|
-
npm start
|
|
26
23
|
```
|
|
27
24
|
|
|
25
|
+
> **Updating:** `npx` caches packages after the first download. To force an update, temporarily change the command in your MCP client config to `npx -y android-mock-location-mcp@latest` and restart the client.
|
|
26
|
+
|
|
27
|
+
See the [Configuration](#configuration) section below for how to set up your MCP client to launch the server.
|
|
28
|
+
|
|
28
29
|
## Configuration
|
|
29
30
|
|
|
30
31
|
### Environment Variables
|
|
@@ -35,6 +36,8 @@ npm start
|
|
|
35
36
|
| `GOOGLE_API_KEY` | Google Places + Routes API key | When `PROVIDER=google` |
|
|
36
37
|
| `MAPBOX_ACCESS_TOKEN` | Mapbox Geocoding + Directions access token | When `PROVIDER=mapbox` |
|
|
37
38
|
|
|
39
|
+
Provider selection is resolved once at server startup. Changing `PROVIDER` or API keys requires restarting the MCP client (which restarts the server).
|
|
40
|
+
|
|
38
41
|
Set environment variables in your MCP client configuration:
|
|
39
42
|
|
|
40
43
|
<details>
|
|
@@ -100,7 +103,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
100
103
|
```
|
|
101
104
|
</details>
|
|
102
105
|
|
|
103
|
-
After editing the config, restart
|
|
106
|
+
After editing the config, restart Claude Desktop to apply the changes (this restarts the MCP server automatically).
|
|
104
107
|
|
|
105
108
|
<details>
|
|
106
109
|
<summary><b>Claude Code (default OSM provider)</b></summary>
|
|
@@ -141,7 +144,7 @@ claude mcp add android-mock-location-mcp \
|
|
|
141
144
|
<details>
|
|
142
145
|
<summary><b>Claude Code — switching providers</b></summary>
|
|
143
146
|
|
|
144
|
-
To switch from one provider to another (e.g. `osm` → `google`), remove and re-add the server with new env vars, then restart
|
|
147
|
+
To switch from one provider to another (e.g. `osm` → `google`), remove and re-add the server with new env vars, then restart Claude Code:
|
|
145
148
|
|
|
146
149
|
```bash
|
|
147
150
|
# 1. Remove existing server
|
|
@@ -154,10 +157,10 @@ claude mcp add android-mock-location-mcp \
|
|
|
154
157
|
-e GOOGLE_API_KEY=$GOOGLE_API_KEY \
|
|
155
158
|
-- npx -y android-mock-location-mcp
|
|
156
159
|
|
|
157
|
-
# 3. Restart the MCP server
|
|
160
|
+
# 3. Restart Claude Code (this restarts the MCP server automatically)
|
|
158
161
|
```
|
|
159
162
|
|
|
160
|
-
|
|
163
|
+
Provider selection is resolved once at server startup, so restarting is required for changes to take effect.
|
|
161
164
|
</details>
|
|
162
165
|
|
|
163
166
|
### Providers
|
|
@@ -178,7 +181,7 @@ Google and Mapbox providers produce better results than the default OSM provider
|
|
|
178
181
|
|
|
179
182
|
### `geo_list_devices`
|
|
180
183
|
|
|
181
|
-
List connected Android
|
|
184
|
+
List connected Android emulators via ADB.
|
|
182
185
|
|
|
183
186
|
No parameters.
|
|
184
187
|
|
|
@@ -186,24 +189,23 @@ No parameters.
|
|
|
186
189
|
|
|
187
190
|
### `geo_connect_device`
|
|
188
191
|
|
|
189
|
-
Connect to an Android
|
|
192
|
+
Connect to an Android emulator for mock location control. Only emulators are supported (e.g. `emulator-5554`). Locations are set via the emulator's `geo fix` command.
|
|
190
193
|
|
|
191
194
|
| Parameter | Type | Required | Description |
|
|
192
195
|
|-----------|------|----------|-------------|
|
|
193
|
-
| `deviceId` | string | yes |
|
|
196
|
+
| `deviceId` | string | yes | Emulator serial from `geo_list_devices`, e.g. `emulator-5554` |
|
|
194
197
|
|
|
195
198
|
---
|
|
196
199
|
|
|
197
200
|
### `geo_set_location`
|
|
198
201
|
|
|
199
|
-
Set
|
|
202
|
+
Set emulator GPS to coordinates or any place name/address. Geocodes place names via the configured provider.
|
|
200
203
|
|
|
201
204
|
| Parameter | Type | Required | Default | Description |
|
|
202
205
|
|-----------|------|----------|---------|-------------|
|
|
203
206
|
| `lat` | number | no | — | Latitude (-90 to 90) |
|
|
204
207
|
| `lng` | number | no | — | Longitude (-180 to 180) |
|
|
205
208
|
| `place` | string | no | — | Place name or address, e.g. `'Times Square'`, `'Tokyo Station'` |
|
|
206
|
-
| `accuracy` | number | no | `3` | GPS accuracy in meters |
|
|
207
209
|
|
|
208
210
|
Provide either `place` or both `lat`/`lng`.
|
|
209
211
|
|
|
@@ -227,7 +229,7 @@ Simulate movement along a route between two points at a given speed. Routes foll
|
|
|
227
229
|
|
|
228
230
|
Provide either `from`/`to` (place names) or `fromLat`/`fromLng`/`toLat`/`toLng` (coordinates) for each endpoint.
|
|
229
231
|
|
|
230
|
-
**Starting location auto-resolve:** If no `from`/`fromLat`/`fromLng` is provided, the tool automatically tries (in order): the last mock location, the
|
|
232
|
+
**Starting location auto-resolve:** If no `from`/`fromLat`/`fromLng` is provided, the tool automatically tries (in order): the last mock location, the emulator's current GPS position via `geo_get_location`, or returns an error asking the user for their starting location.
|
|
231
233
|
|
|
232
234
|
#### Routing Profiles
|
|
233
235
|
|
|
@@ -241,6 +243,30 @@ The AI should select `profile` based on user intent (e.g. "walk to" → `foot`,
|
|
|
241
243
|
|
|
242
244
|
---
|
|
243
245
|
|
|
246
|
+
### `geo_simulate_multi_stop`
|
|
247
|
+
|
|
248
|
+
Simulate movement along a multi-stop route (e.g. delivery route, rideshare pickups). Routes between consecutive waypoints follow real streets. Each waypoint can have a dwell time (time spent stationary at the stop before continuing). Runs as one continuous 1 Hz simulation with no gaps between legs.
|
|
249
|
+
|
|
250
|
+
| Parameter | Type | Required | Default | Description |
|
|
251
|
+
|-----------|------|----------|---------|-------------|
|
|
252
|
+
| `waypoints` | array | yes | — | Ordered list of waypoints (min 2). Each: `{ lat?, lng?, place?, dwellSeconds? }` |
|
|
253
|
+
| `speedKmh` | number | no | `60` | Travel speed between waypoints (km/h) |
|
|
254
|
+
| `trafficMultiplier` | number | no | `1.0` | Traffic slowdown factor (e.g. `1.5` = 50% slower) |
|
|
255
|
+
| `profile` | enum | no | `car` | Routing profile: `car`, `foot`, or `bike` |
|
|
256
|
+
|
|
257
|
+
Each waypoint object:
|
|
258
|
+
|
|
259
|
+
| Field | Type | Required | Default | Description |
|
|
260
|
+
|-------|------|----------|---------|-------------|
|
|
261
|
+
| `lat` | number | no | — | Waypoint latitude |
|
|
262
|
+
| `lng` | number | no | — | Waypoint longitude |
|
|
263
|
+
| `place` | string | no | — | Waypoint place name or address |
|
|
264
|
+
| `dwellSeconds` | number | no | `0` | Time to stay at this waypoint before continuing (seconds) |
|
|
265
|
+
|
|
266
|
+
Provide either `place` or both `lat`/`lng` per waypoint. If the first waypoint has no coordinates or place, the tool auto-resolves from the current mock location or emulator GPS position.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
244
270
|
### `geo_simulate_jitter`
|
|
245
271
|
|
|
246
272
|
Simulate GPS noise/jitter at a location for testing accuracy handling.
|
|
@@ -291,6 +317,32 @@ Provide either `place` or both `lat`/`lng`.
|
|
|
291
317
|
|
|
292
318
|
---
|
|
293
319
|
|
|
320
|
+
### `geo_replay_gpx_kml`
|
|
321
|
+
|
|
322
|
+
Replay a GPX or KML track file on the emulator. Auto-detects format from the XML content.
|
|
323
|
+
|
|
324
|
+
| Parameter | Type | Required | Default | Description |
|
|
325
|
+
|-----------|------|----------|---------|-------------|
|
|
326
|
+
| `fileContent` | string | no* | — | Raw GPX or KML file content (XML string) |
|
|
327
|
+
| `filePath` | string | no* | — | Absolute path to a GPX or KML file on the host |
|
|
328
|
+
| `speedMultiplier` | number | no | `1.0` | Playback speed multiplier for time-based replay (e.g. `2.0` = 2x faster) |
|
|
329
|
+
| `speedKmh` | number | no | `60` | Travel speed for distance-based replay (km/h) |
|
|
330
|
+
|
|
331
|
+
*Provide either `fileContent` or `filePath`.
|
|
332
|
+
|
|
333
|
+
#### Replay Modes
|
|
334
|
+
|
|
335
|
+
| Mode | When used | Speed control |
|
|
336
|
+
|------|-----------|---------------|
|
|
337
|
+
| Time-based | GPX files with timestamps on ≥80% of trackpoints | `speedMultiplier` — preserves original speed profile |
|
|
338
|
+
| Distance-based | KML files, or GPX without timestamps | `speedKmh` — constant speed along the track |
|
|
339
|
+
|
|
340
|
+
**GPX support:** Extracts `<trkpt>` elements from `<trk>/<trkseg>` with optional `<ele>` (elevation) and `<time>` (timestamp).
|
|
341
|
+
|
|
342
|
+
**KML support:** Extracts `<coordinates>` from `<LineString>` elements. Coordinate format is `lng,lat,ele` tuples separated by whitespace.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
294
346
|
### `geo_stop`
|
|
295
347
|
|
|
296
348
|
Stop any active location simulation.
|
|
@@ -309,22 +361,35 @@ No parameters.
|
|
|
309
361
|
|
|
310
362
|
### `geo_get_location`
|
|
311
363
|
|
|
312
|
-
Get the
|
|
364
|
+
Get the emulator's current GPS location (last known position from the emulator's location providers). Reads the location via `adb shell dumpsys location`. Use this to determine where the emulator is before simulating a route.
|
|
313
365
|
|
|
314
366
|
No parameters.
|
|
315
367
|
|
|
316
|
-
Returns the
|
|
368
|
+
Returns the emulator's latitude and longitude if a recent GPS fix is available, along with `accuracy` (horizontal accuracy in meters) when provided. If the emulator has no location fix, the tool returns an error message — in that case, ask the user for their current location.
|
|
369
|
+
|
|
370
|
+
## How It Works
|
|
371
|
+
|
|
372
|
+
The server sets emulator GPS location using `adb emu geo fix`, which updates the emulator's stored GPS state. The command format is:
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
geo fix <longitude> <latitude> [<altitude> [<satellites> [<velocity>]]]
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Unlike `geo nmea` (which injects one-shot NMEA sentences), `geo fix` updates the emulator's internal state so the position persists across the emulator's 1 Hz GPS update loop.
|
|
379
|
+
|
|
380
|
+
The `geo fix` command is processed directly by the emulator's GPS simulation layer.
|
|
317
381
|
|
|
318
382
|
## Source Structure
|
|
319
383
|
|
|
320
384
|
| File | Purpose |
|
|
321
385
|
|------|---------|
|
|
322
|
-
| `src/index.ts` | MCP server setup, all
|
|
323
|
-
| `src/
|
|
324
|
-
| `src/
|
|
386
|
+
| `src/index.ts` | MCP server setup, all 11 tool definitions with Zod schemas |
|
|
387
|
+
| `src/emulator.ts` | Emulator connection management, location setting via `geo fix` |
|
|
388
|
+
| `src/adb.ts` | ADB command execution with timeouts, emulator validation |
|
|
325
389
|
| `src/geocode.ts` | Geocoding providers: Nominatim, Google, Mapbox |
|
|
326
390
|
| `src/routing.ts` | Routing providers: OSRM, Google Routes API, Mapbox Directions |
|
|
327
|
-
| `src/
|
|
391
|
+
| `src/gpx-kml.ts` | GPX/KML file parsing for track replay |
|
|
392
|
+
| `src/geo-math.ts` | Haversine distance calculation |
|
|
328
393
|
| `src/fetch-utils.ts` | Shared `fetchWithTimeout` helper |
|
|
329
394
|
|
|
330
395
|
## Development
|
|
@@ -340,7 +405,8 @@ The server communicates via stdio (MCP protocol). To test interactively, use the
|
|
|
340
405
|
|
|
341
406
|
## Known Limitations
|
|
342
407
|
|
|
343
|
-
- **
|
|
408
|
+
- **Emulators only.** The server uses `adb emu geo fix` which only works with Android emulators. Physical devices are not supported.
|
|
409
|
+
- **Single emulator.** The server connects to one emulator at a time. Calling `geo_connect_device` with a different serial disconnects the previous one and stops any active simulation.
|
|
344
410
|
- **Single simulation.** Only one simulation (route, jitter, or geofence) runs at a time. Starting a new one stops the previous.
|
|
345
411
|
|
|
346
412
|
See also: provider-specific limitations in the [Providers](#providers) table above.
|
package/dist/adb.d.ts
CHANGED
|
@@ -1,30 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
interface ExecError extends Error {
|
|
2
|
+
stderr?: string;
|
|
3
|
+
status?: number | null;
|
|
4
|
+
}
|
|
5
|
+
export declare function isExecError(err: unknown): err is ExecError;
|
|
6
|
+
/** Check if a device ID matches the emulator serial pattern. */
|
|
7
|
+
export declare function isEmulator(deviceId: string): boolean;
|
|
8
|
+
/** Emulator-scoped synchronous ADB command. Validates deviceId and applies timeout. */
|
|
4
9
|
export declare function adbDevice(deviceId: string, args: string[], timeoutMs?: number): string;
|
|
5
10
|
/** Global asynchronous ADB command (no -s flag). */
|
|
6
11
|
export declare function adb(args: string[], timeoutMs?: number): Promise<string>;
|
|
7
12
|
/** List connected ADB devices. Returns raw `adb devices -l` output. */
|
|
8
13
|
export declare function listDevices(): Promise<string>;
|
|
9
|
-
|
|
10
|
-
* Check whether the agent APK is installed on the device.
|
|
11
|
-
* Returns true if installed, false if not installed.
|
|
12
|
-
* Throws if the ADB command fails for other reasons (device offline, unauthorized, etc.).
|
|
13
|
-
*/
|
|
14
|
-
export declare function isAgentInstalled(deviceId: string): boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Ensure the device is set up for mock location: grant location permissions
|
|
17
|
-
* and set this app as the mock location provider via AppOps.
|
|
18
|
-
* All commands are idempotent — safe to call on every connection attempt.
|
|
19
|
-
*/
|
|
20
|
-
export declare function ensureDeviceSetup(deviceId: string): void;
|
|
21
|
-
/**
|
|
22
|
-
* Start the MockLocationService directly via ADB.
|
|
23
|
-
* The service is exported, so it can be started without launching the Activity.
|
|
24
|
-
*/
|
|
25
|
-
export declare function startAgentService(deviceId: string): void;
|
|
26
|
-
/**
|
|
27
|
-
* Set up ADB port forwarding for the agent TCP socket.
|
|
28
|
-
* Synchronous — throws on failure with stderr context.
|
|
29
|
-
*/
|
|
30
|
-
export declare function adbForward(deviceId: string): void;
|
|
14
|
+
export {};
|
package/dist/adb.js
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
// ── ADB command execution
|
|
1
|
+
// ── ADB command execution (emulator-only) ────────────────────────────────────
|
|
2
2
|
import { execFileSync, execFile } from "node:child_process";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
const execFileAsync = promisify(execFile);
|
|
5
5
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
6
|
-
export const AGENT_PACKAGE = "com.ms.square.geomcpagent";
|
|
7
|
-
export const AGENT_PORT = 5005;
|
|
8
6
|
const ADB_TIMEOUT_MS = 10_000;
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
function isExecError(err) {
|
|
12
|
-
|
|
7
|
+
/** Only emulator serials are accepted (e.g. emulator-5554). */
|
|
8
|
+
const EMULATOR_ID_PATTERN = /^emulator-\d+$/;
|
|
9
|
+
export function isExecError(err) {
|
|
10
|
+
if (!(err instanceof Error) || !("stderr" in err))
|
|
11
|
+
return false;
|
|
12
|
+
const { stderr } = err;
|
|
13
|
+
return stderr === undefined || typeof stderr === "string";
|
|
13
14
|
}
|
|
14
15
|
// ── Validation ───────────────────────────────────────────────────────────────
|
|
15
16
|
function validateDeviceId(deviceId) {
|
|
16
|
-
if (!
|
|
17
|
-
throw new Error(`Invalid
|
|
17
|
+
if (!EMULATOR_ID_PATTERN.test(deviceId)) {
|
|
18
|
+
throw new Error(`Invalid emulator ID: ${deviceId}. Only Android emulators are supported (e.g. emulator-5554).`);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
21
|
+
/** Check if a device ID matches the emulator serial pattern. */
|
|
22
|
+
export function isEmulator(deviceId) {
|
|
23
|
+
return EMULATOR_ID_PATTERN.test(deviceId);
|
|
24
|
+
}
|
|
20
25
|
// ── Primitives ───────────────────────────────────────────────────────────────
|
|
21
|
-
/**
|
|
26
|
+
/** Emulator-scoped synchronous ADB command. Validates deviceId and applies timeout. */
|
|
22
27
|
export function adbDevice(deviceId, args, timeoutMs = ADB_TIMEOUT_MS) {
|
|
23
28
|
validateDeviceId(deviceId);
|
|
24
29
|
return execFileSync("adb", ["-s", deviceId, ...args], {
|
|
@@ -39,61 +44,4 @@ export async function adb(args, timeoutMs = ADB_TIMEOUT_MS) {
|
|
|
39
44
|
export async function listDevices() {
|
|
40
45
|
return adb(["devices", "-l"]);
|
|
41
46
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Check whether the agent APK is installed on the device.
|
|
44
|
-
* Returns true if installed, false if not installed.
|
|
45
|
-
* Throws if the ADB command fails for other reasons (device offline, unauthorized, etc.).
|
|
46
|
-
*/
|
|
47
|
-
export function isAgentInstalled(deviceId) {
|
|
48
|
-
try {
|
|
49
|
-
const output = adbDevice(deviceId, ["shell", "pm", "path", AGENT_PACKAGE]);
|
|
50
|
-
return output.includes("package:");
|
|
51
|
-
}
|
|
52
|
-
catch (err) {
|
|
53
|
-
// `pm path` exits with code 1 and empty stderr when the package is not found.
|
|
54
|
-
// ADB-level failures (device offline, unauthorized) also exit with code 1 but
|
|
55
|
-
// include an error message on stderr — those should propagate.
|
|
56
|
-
if (isExecError(err) && err.status === 1 && !err.stderr?.trim()) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
throw err;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Ensure the device is set up for mock location: grant location permissions
|
|
64
|
-
* and set this app as the mock location provider via AppOps.
|
|
65
|
-
* All commands are idempotent — safe to call on every connection attempt.
|
|
66
|
-
*/
|
|
67
|
-
export function ensureDeviceSetup(deviceId) {
|
|
68
|
-
adbDevice(deviceId, ["shell", "pm", "grant", AGENT_PACKAGE, "android.permission.ACCESS_FINE_LOCATION"]);
|
|
69
|
-
adbDevice(deviceId, ["shell", "pm", "grant", AGENT_PACKAGE, "android.permission.ACCESS_COARSE_LOCATION"]);
|
|
70
|
-
adbDevice(deviceId, ["shell", "appops", "set", AGENT_PACKAGE, "android:mock_location", "allow"]);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Start the MockLocationService directly via ADB.
|
|
74
|
-
* The service is exported, so it can be started without launching the Activity.
|
|
75
|
-
*/
|
|
76
|
-
export function startAgentService(deviceId) {
|
|
77
|
-
adbDevice(deviceId, [
|
|
78
|
-
"shell",
|
|
79
|
-
"am",
|
|
80
|
-
"start-foreground-service",
|
|
81
|
-
"-n",
|
|
82
|
-
`${AGENT_PACKAGE}/.MockLocationService`,
|
|
83
|
-
]);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Set up ADB port forwarding for the agent TCP socket.
|
|
87
|
-
* Synchronous — throws on failure with stderr context.
|
|
88
|
-
*/
|
|
89
|
-
export function adbForward(deviceId) {
|
|
90
|
-
try {
|
|
91
|
-
adbDevice(deviceId, ["forward", `tcp:${AGENT_PORT}`, `tcp:${AGENT_PORT}`]);
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
95
|
-
const stderr = isExecError(err) ? err.stderr?.trim() : undefined;
|
|
96
|
-
throw new Error(`Failed to set up adb port forwarding for ${deviceId}: ${msg}${stderr ? ` (${stderr})` : ""}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
47
|
//# sourceMappingURL=adb.js.map
|
package/dist/adb.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb.js","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,gFAAgF;AAEhF,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"adb.js","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,gFAAgF;AAEhF,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,+DAA+D;AAC/D,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAS7C,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAgB,CAAC;IACpC,OAAO,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC;AAC5D,CAAC;AAED,gFAAgF;AAEhF,SAAS,gBAAgB,CAAC,QAAgB;IACxC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,wBAAwB,QAAQ,8DAA8D,CAC/F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED,gFAAgF;AAEhF,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAc,EAAE,SAAS,GAAG,cAAc;IACpF,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,OAAO,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,EAAE;QACpD,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,SAAS;KACnB,CAAC,CAAC;AACL,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,SAAS,GAAG,cAAc;IAClE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE;QAClD,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,SAAS;KACnB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gFAAgF;AAEhF,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,OAAO,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Register a callback invoked when the emulator disconnects. */
|
|
2
|
+
export declare function onDisconnect(cb: () => void): void;
|
|
3
|
+
export declare function getConnectedDeviceId(): string | null;
|
|
4
|
+
export declare function isConnected(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Connect to an emulator by device ID.
|
|
7
|
+
* Validates the emulator is reachable via ADB.
|
|
8
|
+
*/
|
|
9
|
+
export declare function connectEmulator(deviceId: string): void;
|
|
10
|
+
export interface LocationParams {
|
|
11
|
+
lat: number;
|
|
12
|
+
lng: number;
|
|
13
|
+
altitude?: number;
|
|
14
|
+
/** Speed in meters per second. Converted to knots for `geo fix`. */
|
|
15
|
+
speed?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Set mock location on the connected emulator via `geo fix`.
|
|
19
|
+
*
|
|
20
|
+
* `geo fix` updates the emulator's stored GPS state, so the 1 Hz
|
|
21
|
+
* PassiveGpsUpdater loop keeps broadcasting the new position.
|
|
22
|
+
*/
|
|
23
|
+
export declare function setLocation(params: LocationParams): void;
|
|
24
|
+
export interface EmulatorLocation {
|
|
25
|
+
lat: number;
|
|
26
|
+
lng: number;
|
|
27
|
+
accuracy?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse a Location line from `dumpsys location` output.
|
|
31
|
+
*
|
|
32
|
+
* Android's Location.toString() format (API 26+):
|
|
33
|
+
* Location[gps 37.421998,-122.084000 hAcc=20.0 ...]
|
|
34
|
+
* Location[fused 37.421998,-122.084000 acc=5.0 ...]
|
|
35
|
+
*
|
|
36
|
+
* We extract lat, lng and optional accuracy (hAcc= or acc=).
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseLocationLine(line: string): EmulatorLocation | null;
|
|
39
|
+
/**
|
|
40
|
+
* Get the emulator's current GPS location via `adb shell dumpsys location`.
|
|
41
|
+
*
|
|
42
|
+
* Parses the "Last Known Locations" section from the location service dump
|
|
43
|
+
* and returns the GPS provider's last fix. Falls back to the fused provider
|
|
44
|
+
* if GPS is not available.
|
|
45
|
+
*
|
|
46
|
+
* Returns null if no location is available.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getLocation(): EmulatorLocation | null;
|
|
49
|
+
/** Initialize emulator module. No-op — no background connections needed. */
|
|
50
|
+
export declare function initEmulator(): void;
|
|
51
|
+
/** Shut down emulator module — clean up state. */
|
|
52
|
+
export declare function shutdownEmulator(): void;
|
package/dist/emulator.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// ── Emulator location control via `geo fix` ──────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Sets mock locations on the emulator using `adb emu geo fix` which updates
|
|
4
|
+
// the emulator's stored GPS state. Unlike `geo nmea`, `geo fix` survives the
|
|
5
|
+
// emulator's 1 Hz PassiveGpsUpdater loop that re-sends from stored state.
|
|
6
|
+
//
|
|
7
|
+
// Format: geo fix <longitude> <latitude> [<altitude> [<satellites> [<velocity>]]]
|
|
8
|
+
// - longitude/latitude in decimal degrees (longitude FIRST)
|
|
9
|
+
// - altitude in meters
|
|
10
|
+
// - satellites count (integer)
|
|
11
|
+
// - velocity in knots
|
|
12
|
+
import { adbDevice, isExecError } from "./adb.js";
|
|
13
|
+
// ── State ────────────────────────────────────────────────────────────────────
|
|
14
|
+
let connectedDeviceId = null;
|
|
15
|
+
let disconnectCallback = null;
|
|
16
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
17
|
+
/** Register a callback invoked when the emulator disconnects. */
|
|
18
|
+
export function onDisconnect(cb) {
|
|
19
|
+
disconnectCallback = cb;
|
|
20
|
+
}
|
|
21
|
+
export function getConnectedDeviceId() {
|
|
22
|
+
return connectedDeviceId;
|
|
23
|
+
}
|
|
24
|
+
export function isConnected() {
|
|
25
|
+
return connectedDeviceId !== null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Connect to an emulator by device ID.
|
|
29
|
+
* Validates the emulator is reachable via ADB.
|
|
30
|
+
*/
|
|
31
|
+
export function connectEmulator(deviceId) {
|
|
32
|
+
// Disconnect any previously connected emulator first
|
|
33
|
+
if (connectedDeviceId !== null && connectedDeviceId !== deviceId) {
|
|
34
|
+
disconnectEmulator();
|
|
35
|
+
}
|
|
36
|
+
// Verify the emulator is reachable without side-effects (no location change).
|
|
37
|
+
// ro.kernel.qemu is "1" on emulators — this confirms ADB connectivity.
|
|
38
|
+
try {
|
|
39
|
+
adbDevice(deviceId, ["shell", "getprop", "ro.kernel.qemu"]);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
+
const stderr = isExecError(err) ? err.stderr?.trim() : undefined;
|
|
44
|
+
throw new Error(`Failed to reach emulator ${deviceId}: ${msg}${stderr ? ` (${stderr})` : ""}\n` +
|
|
45
|
+
"Ensure the emulator is running and ADB is connected.");
|
|
46
|
+
}
|
|
47
|
+
connectedDeviceId = deviceId;
|
|
48
|
+
}
|
|
49
|
+
/** Disconnect from the current emulator. */
|
|
50
|
+
function disconnectEmulator() {
|
|
51
|
+
const wasConnected = connectedDeviceId !== null;
|
|
52
|
+
connectedDeviceId = null;
|
|
53
|
+
if (wasConnected) {
|
|
54
|
+
try {
|
|
55
|
+
disconnectCallback?.();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Ignore callback errors
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const MS_TO_KNOTS = 1.94384;
|
|
63
|
+
/**
|
|
64
|
+
* Set mock location on the connected emulator via `geo fix`.
|
|
65
|
+
*
|
|
66
|
+
* `geo fix` updates the emulator's stored GPS state, so the 1 Hz
|
|
67
|
+
* PassiveGpsUpdater loop keeps broadcasting the new position.
|
|
68
|
+
*/
|
|
69
|
+
export function setLocation(params) {
|
|
70
|
+
if (!connectedDeviceId) {
|
|
71
|
+
throw new Error("Not connected to an emulator. Call geo_connect_device first with an emulator serial (e.g. emulator-5554).");
|
|
72
|
+
}
|
|
73
|
+
// geo fix <longitude> <latitude> [<altitude> [<satellites> [<velocity>]]]
|
|
74
|
+
const args = [
|
|
75
|
+
"emu", "geo", "fix",
|
|
76
|
+
params.lng.toFixed(6),
|
|
77
|
+
params.lat.toFixed(6),
|
|
78
|
+
];
|
|
79
|
+
const altitude = params.altitude ?? 0;
|
|
80
|
+
const satellites = 8;
|
|
81
|
+
args.push(altitude.toFixed(1));
|
|
82
|
+
args.push(satellites.toString());
|
|
83
|
+
if (params.speed != null && params.speed > 0) {
|
|
84
|
+
args.push((params.speed * MS_TO_KNOTS).toFixed(1));
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
adbDevice(connectedDeviceId, args);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
91
|
+
// If ADB fails, the emulator may have been closed
|
|
92
|
+
disconnectEmulator();
|
|
93
|
+
throw new Error(`Failed to set location: ${msg}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Parse a Location line from `dumpsys location` output.
|
|
98
|
+
*
|
|
99
|
+
* Android's Location.toString() format (API 26+):
|
|
100
|
+
* Location[gps 37.421998,-122.084000 hAcc=20.0 ...]
|
|
101
|
+
* Location[fused 37.421998,-122.084000 acc=5.0 ...]
|
|
102
|
+
*
|
|
103
|
+
* We extract lat, lng and optional accuracy (hAcc= or acc=).
|
|
104
|
+
*/
|
|
105
|
+
export function parseLocationLine(line) {
|
|
106
|
+
// Match "Location[<provider> <lat>,<lng>" pattern
|
|
107
|
+
const coordMatch = line.match(/Location\[\S+\s+(-?[\d.]+),(-?[\d.]+)/);
|
|
108
|
+
if (!coordMatch)
|
|
109
|
+
return null;
|
|
110
|
+
const lat = parseFloat(coordMatch[1]);
|
|
111
|
+
const lng = parseFloat(coordMatch[2]);
|
|
112
|
+
if (Number.isNaN(lat) || Number.isNaN(lng))
|
|
113
|
+
return null;
|
|
114
|
+
// Try to extract accuracy (hAcc= in newer Android, acc= in older)
|
|
115
|
+
const accMatch = line.match(/(?:hAcc|acc)=([\d.]+)/);
|
|
116
|
+
const accuracy = accMatch ? parseFloat(accMatch[1]) : undefined;
|
|
117
|
+
return { lat, lng, accuracy };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the emulator's current GPS location via `adb shell dumpsys location`.
|
|
121
|
+
*
|
|
122
|
+
* Parses the "Last Known Locations" section from the location service dump
|
|
123
|
+
* and returns the GPS provider's last fix. Falls back to the fused provider
|
|
124
|
+
* if GPS is not available.
|
|
125
|
+
*
|
|
126
|
+
* Returns null if no location is available.
|
|
127
|
+
*/
|
|
128
|
+
export function getLocation() {
|
|
129
|
+
if (!connectedDeviceId) {
|
|
130
|
+
throw new Error("Not connected to an emulator. Call geo_connect_device first.");
|
|
131
|
+
}
|
|
132
|
+
let output;
|
|
133
|
+
try {
|
|
134
|
+
output = adbDevice(connectedDeviceId, ["shell", "dumpsys", "location"]);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
138
|
+
throw new Error(`Failed to query emulator location: ${msg}`);
|
|
139
|
+
}
|
|
140
|
+
// Look for "Last Known Locations:" section and parse GPS/fused provider lines
|
|
141
|
+
const lines = output.split("\n");
|
|
142
|
+
let inLastKnown = false;
|
|
143
|
+
let gpsLocation = null;
|
|
144
|
+
let fusedLocation = null;
|
|
145
|
+
for (const line of lines) {
|
|
146
|
+
const trimmed = line.trim();
|
|
147
|
+
if (trimmed.startsWith("Last Known Locations:")) {
|
|
148
|
+
inLastKnown = true;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
// End of section: blank line or new non-indented section header
|
|
152
|
+
if (inLastKnown && (trimmed === "" || (trimmed !== "" && !line.startsWith(" ") && !line.startsWith("\t")))) {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
if (inLastKnown) {
|
|
156
|
+
if (trimmed.startsWith("gps:") || trimmed.includes("Location[gps")) {
|
|
157
|
+
gpsLocation = parseLocationLine(trimmed);
|
|
158
|
+
}
|
|
159
|
+
else if (trimmed.startsWith("fused:") || trimmed.includes("Location[fused")) {
|
|
160
|
+
fusedLocation = parseLocationLine(trimmed);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Prefer GPS, fall back to fused
|
|
165
|
+
return gpsLocation ?? fusedLocation ?? null;
|
|
166
|
+
}
|
|
167
|
+
/** Initialize emulator module. No-op — no background connections needed. */
|
|
168
|
+
export function initEmulator() {
|
|
169
|
+
// Nothing to do
|
|
170
|
+
}
|
|
171
|
+
/** Shut down emulator module — clean up state. */
|
|
172
|
+
export function shutdownEmulator() {
|
|
173
|
+
disconnectEmulator();
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=emulator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emulator.js","sourceRoot":"","sources":["../src/emulator.ts"],"names":[],"mappings":"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"}
|
package/dist/geo-math.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
1
|
/** Haversine distance between two points in meters. */
|
|
2
2
|
export declare function haversineDistance(lat1: number, lng1: number, lat2: number, lng2: number): number;
|
|
3
|
-
/** Forward azimuth (bearing) in degrees 0-360 from point 1 to point 2. */
|
|
4
|
-
export declare function computeBearing(lat1: number, lng1: number, lat2: number, lng2: number): number;
|
package/dist/geo-math.js
CHANGED
|
@@ -9,12 +9,4 @@ export function haversineDistance(lat1, lng1, lat2, lng2) {
|
|
|
9
9
|
const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
|
|
10
10
|
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
11
11
|
}
|
|
12
|
-
/** Forward azimuth (bearing) in degrees 0-360 from point 1 to point 2. */
|
|
13
|
-
export function computeBearing(lat1, lng1, lat2, lng2) {
|
|
14
|
-
const dLng = toRad(lng2 - lng1);
|
|
15
|
-
const y = Math.sin(dLng) * Math.cos(toRad(lat2));
|
|
16
|
-
const x = Math.cos(toRad(lat1)) * Math.sin(toRad(lat2)) -
|
|
17
|
-
Math.sin(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.cos(dLng);
|
|
18
|
-
return (toDeg(Math.atan2(y, x)) + 360) % 360;
|
|
19
|
-
}
|
|
20
12
|
//# sourceMappingURL=geo-math.js.map
|