{"id":48911689,"url":"https://github.com/asdoos/sonygpsapp","last_synced_at":"2026-04-17T00:01:56.009Z","repository":{"id":351886253,"uuid":"1212926179","full_name":"Asdoos/SonyGpsApp","owner":"Asdoos","description":"Transfer GPS coordinates from Android to Sony cameras via BLE — without the Sony Creators App","archived":false,"fork":false,"pushed_at":"2026-04-16T22:11:11.000Z","size":74,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-16T23:29:38.576Z","etag":null,"topics":["android","bluetooth-le","gps","kotlin","reverse-engineering","sony"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Asdoos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-16T21:46:21.000Z","updated_at":"2026-04-16T22:11:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Asdoos/SonyGpsApp","commit_stats":null,"previous_names":["asdoos/sonygpsapp"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Asdoos/SonyGpsApp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asdoos%2FSonyGpsApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asdoos%2FSonyGpsApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asdoos%2FSonyGpsApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asdoos%2FSonyGpsApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Asdoos","download_url":"https://codeload.github.com/Asdoos/SonyGpsApp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asdoos%2FSonyGpsApp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31909235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"ssl_error","status_checked_at":"2026-04-16T18:21:47.142Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","bluetooth-le","gps","kotlin","reverse-engineering","sony"],"created_at":"2026-04-17T00:00:43.514Z","updated_at":"2026-04-17T00:01:55.988Z","avatar_url":"https://github.com/Asdoos.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sony GPS Link\n\n[![Release](https://img.shields.io/github/v/release/Asdoos/SonyGpsApp?style=flat-square)](https://github.com/Asdoos/SonyGpsApp/releases/latest)\n[![Build](https://img.shields.io/github/actions/workflow/status/Asdoos/SonyGpsApp/release.yml?style=flat-square\u0026label=build)](https://github.com/Asdoos/SonyGpsApp/actions)\n[![Android](https://img.shields.io/badge/Android-8.0%2B-green?style=flat-square\u0026logo=android)](https://developer.android.com)\n[![Kotlin](https://img.shields.io/badge/Kotlin-1.9-purple?style=flat-square\u0026logo=kotlin)](https://kotlinlang.org)\n[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)\n\nAndroid app that transfers GPS coordinates from a smartphone to Sony cameras via Bluetooth Low Energy (BLE) — without the original Sony Creators App.\n\n\u003e Reverse-engineered from **Sony Creators App v3.3.1** (XAPK, decompiled with jadx 1.5.5).\n\n**[⬇ Download latest APK](https://github.com/Asdoos/SonyGpsApp/releases/latest)**\n\n---\n\n## Feature Overview\n\n| Feature | Detail |\n|---|---|\n| BLE camera discovery | Scan for Sony devices (manufacturer ID 301) |\n| GPS transfer | WGS-84, every 5 seconds, 91 or 95-byte packet |\n| APO keepalive | Prevents camera sleep mode, every 9 seconds |\n| Auto-reconnect | Up to 10 attempts after unexpected disconnection |\n| Foreground Service | GPS + BLE run persistently, even with the app closed |\n\n---\n\n## Supported Cameras\n\nAll Sony cameras that support the proprietary Sony BLE GPS protocol (`LocationInfoFromSmartPhone_1_0` / `_1_1`), including:\n\n- Sony ZV-E1, ZV-E10, ZV-1 II\n- Sony ILCE series (α7 IV, α7C, α7R V, α6700, ...)\n- Sony FX3, FX30\n\nThe camera must be **paired via Bluetooth** through the Android system settings before using this app.\n\n---\n\n## How It Works\n\n### 1. Camera Discovery (BLE Scan)\n\nSony cameras always include **manufacturer ID 301** (`0x012D` = Sony Corporation, per Bluetooth SIG) in their BLE advertisement. The app filters exclusively for this ID:\n\n```\nScanFilter: ManufacturerData(companyId = 301, data = [])\nScanMode:   SCAN_MODE_LOW_LATENCY\n```\n\nDiscovered devices are presented to the user as a list showing name, MAC address, and signal strength (RSSI).\n\n---\n\n### 2. BLE GATT Profile (Sony-proprietary)\n\nSony cameras expose a proprietary GATT profile with two relevant services:\n\n#### Service 1 — Camera Control (`CC00`)\n```\nUUID: 8000CC00-CC00-FFFF-FFFF-FFFFFFFFFFFF\n```\n\n| Characteristic (Prefix) | Type | Content |\n|---|---|---|\n| `0000CC02` | WRITE | Generic camera control commands (incl. APO avoidance) |\n\n#### Service 2 — GPS / Location (`DD00`)\n```\nUUID: 8000DD00-DD00-FFFF-FFFF-FFFFFFFFFFFF\n```\n\n| Characteristic (Prefix) | Type | Content |\n|---|---|---|\n| `0000DD01` | NOTIFY | Camera → phone: transfer disabled signal `{3,1,2,0}` |\n| `0000DD11` | WRITE  | GPS payload (91 or 95 bytes) |\n| `0000DD21` | READ   | Capability flags: byte[4] \u0026 `0x02` → timezone support |\n| `0000DD30` | WRITE  | Lock: `{0x01}` = acquire, `{0x00}` = release |\n| `0000DD31` | WRITE  | Location transfer: `{0x01}` = on, `{0x00}` = off |\n| `0000DD32` | READ   | Time correction setting (informational) |\n| `0000DD33` | READ   | Area adjustment setting (informational) |\n\n\u003e **Lookup method:** Characteristics are not resolved by their full 128-bit UUID but by **prefix-matching** the first 8 characters of the UUID string — exactly as in the original Sony app: `uuid.toString().uppercase().startsWith(prefix)`.\n\n---\n\n### 3. Connection Protocol (Handshake Sequence)\n\nAfter GATT connect and service discovery, the following sequence is executed:\n\n```\n① enableNotify(DD01)         — camera can abort transfer at any time\n      ↓ CCCD descriptor written\n② write {0x01} → DD30       — acquire exclusive lock\n      ↓ write callback OK\n③ write {0x01} → DD31       — enable GPS transfer on camera\n      ↓ write callback OK\n④ read DD32                  — read time correction setting\n      ↓ read callback\n⑤ read DD33                  — read area adjustment setting\n      ↓ read callback\n⑥ read DD21                  — read capability flags\n      ↓ read callback: byte[4] \u0026 0x02 → timezoneSupport = true/false\n⑦ → onReady(): start GPS updates + APO keepalive timer\n```\n\nAll GATT operations run **serialised** through an `ArrayDeque`-based op-queue, since BLE only permits one concurrent operation. Each operation is only started once the callback of the previous one has been received.\n\n---\n\n### 4. GPS Packet Format\n\nSource: `BluetoothLeUtil.setLocationAndTime()` + `TransferringLocationInfoWithLockState.onLocationUpdated()`\n\nGPS coordinates are written as a **big-endian binary packet** to characteristic `DD11`. Two formats exist depending on the camera's capabilities:\n\n#### Format A — 91 bytes (no timezone support)\n#### Format B — 95 bytes (with timezone support, when `DD21[4] \u0026 0x02 != 0`)\n\n```\nOffset  Size  Value / Description\n────────────────────────────────────────────────────────────\n[0]      1     0x00         (fixed)\n[1]      1     89 / 93      (payload length minus 2; Format A / B)\n[2]      1     0x08         (fixed)\n[3]      1     0x02         (fixed)\n[4]      1     0xFC (= -4)  (fixed)\n[5]      1     0x00 / 0x03  (Format A / B)\n[6]      1     0x00         (fixed)\n[7]      1     0x00         (fixed)\n[8]      1     0x10         (fixed)\n[9]      1     0x10         (fixed)\n[10]     1     0x10         (fixed)\n────────── Payload ────────────────────────────────────────\n[11–14]  4     Latitude  × 10,000,000  as big-endian Int32\n                Example: 48.137154° → 481,371,540 = 0x1CB77914\n[15–18]  4     Longitude × 10,000,000  as big-endian Int32\n                Example: 11.575533° → 115,755,330 = 0x06E6A382\n[19–20]  2     UTC year  as big-endian Int16 (e.g. 2025 = 0x07E9)\n[21]     1     UTC month (1-based, 1–12)\n[22]     1     UTC day   (1–31)\n[23]     1     UTC hour\n[24]     1     UTC minute\n[25]     1     UTC second\n[26–90]  65    Reserved / padding (zeros)\n────────── Format B only ──────────────────────────────────\n[91–92]  2     Timezone offset in minutes, big-endian Int16\n                = TimeZone.getDefault().rawOffset / 60,000\n                Example: UTC+1 → 60 = 0x003C\n[93–94]  2     DST savings in minutes, big-endian Int16\n                = getDSTSavings() / 60,000 if daylight saving is active, else 0\n────────────────────────────────────────────────────────────\n```\n\n**Coordinate encoding:** `(double) degrees × 1e7` → `toInt()` → `ByteBuffer.allocate(4).putInt(...)`  \n**Timestamp:** UTC timezone, derived from `location.getTime()` via `Calendar.getInstance(UTC)`  \n**Validation:** GPS fixes older than **10 seconds** are discarded (based on `elapsedRealtimeNanos` delta)\n\n---\n\n### 5. APO Keepalive (Auto Power Off Avoidance)\n\nThe camera disconnects BLE after ~30 seconds of inactivity (standby / sleep mode).\n\nThe original Sony app therefore sends a keepalive command every **9 seconds**:\n\n```\nService:        8000CC00-CC00-FFFF-FFFF-FFFFFFFFFFFF\nCharacteristic: 0000CC02-...\nValue:          {0x03, 0x08, 0x10, 0x00}\nInterval:       9,000 ms\n```\n\nSource: `ExecutingApoAvoidanceState.onGattCharacteristicWrite()` → `startCommandTimeout(apoAvoidanceRunnable, 9000L)`\n\nThe keepalive runs **independently** of GPS updates through the same op-queue and is rescheduled after every successful write callback. A failed write does not abort the session — the next attempt is made at the regular interval, since isolated failures are non-critical.\n\n---\n\n### 6. Auto-Reconnect\n\nUpon unexpected connection loss (camera sleep despite keepalive, out of range, etc.) the app automatically attempts to reconnect:\n\n- **Delay:** 4 seconds between attempts\n- **Maximum attempts:** 10\n- **Counter reset:** on successfully completed handshake (`onReady`)\n- **No reconnect** if the user manually tapped \"Stop\" (`userStopped` flag)\n\n---\n\n### 7. Foreground Service \u0026 Energy Efficiency\n\n#### Why a Foreground Service?\n\n| Android mechanism | Effect without service | With Foreground Service |\n|---|---|---|\n| Doze Light (~3 min screen off) | GPS callbacks batched / delayed | Exempt |\n| Doze Deep (extended idle) | `Handler.postDelayed` freezes → APO keepalive dies | Exempt |\n| Memory pressure | Process is killed | `START_STICKY`: system restarts automatically |\n\n#### Service type declaration (mandatory on Android 14+ / API 34)\n```xml\nandroid:foregroundServiceType=\"location|connectedDevice\"\n```\n- `location` — grants GPS access from a background foreground service\n- `connectedDevice` — grants BLE access from a background foreground service\n\n#### Power consumption estimate (screen off)\n\n| Component | Avg. current |\n|---|---|\n| GPS chip (`HIGH_ACCURACY`, 5 s interval) | ~40–60 mA |\n| BLE (connected + ~6 writes/10 s) | ~2–4 mA |\n| CPU (callbacks, packet assembly) | ~1–2 mA |\n| APO keepalive (included in BLE) | ~0.1 mA |\n| **App total (incremental)** | **~45–65 mA** |\n| **Realistic incl. system baseline** | **~75–90 mA** |\n\n→ **~4–7% battery per hour** on a typical 4,000 mAh device  \n→ Comparable to a GPS tracking app like Strava running in the background\n\n---\n\n### 8. Architecture\n\n```\n┌─────────────────────────────────┐\n│           MainActivity          │\n│  ┌──────────────┐               │\n│  │  BLE Scan    │  (short-lived)│\n│  └──────┬───────┘               │\n│         │ device selected       │\n│  ┌──────▼───────────────────┐   │\n│  │  startForegroundService  │   │\n│  │  bindService (Binder)    │   │\n│  └──────────────────────────┘   │\n│  StatusListener (UI updates)    │\n└────────────┬────────────────────┘\n             │ Binder (LocalBinder)\n┌────────────▼────────────────────┐\n│      GpsForegroundService       │  ← runs persistently in background\n│                                 │\n│  FusedLocationProviderClient    │  GPS every 5 s\n│  Handler (APO timer, reconnect) │\n│                                 │\n│  ┌──────────────────────────┐   │\n│  │      SonyCameraGatt      │   │\n│  │  ┌──────────────────┐    │   │\n│  │  │  ArrayDeque      │    │   │  Serialised op-queue\n│  │  │  (Op-Queue)      │    │   │\n│  │  └──────────────────┘    │   │\n│  │  GATT Callback           │   │\n│  └──────────────────────────┘   │\n│                                 │\n│  Persistent Notification        │  \"Stop\" action directly in notification\n└─────────────────────────────────┘\n             │ BLE GATT\n┌────────────▼────────────────────┐\n│          Sony Camera            │\n│  Service CC00 (Control)         │  ← APO keepalive {3,8,16,0} every 9 s\n│  Service DD00 (GPS)             │  ← GPS packet (91/95 B) every 5 s\n└─────────────────────────────────┘\n```\n\n---\n\n## Project Structure\n\n```\nSonyGpsApp/\n├── app/src/main/\n│   ├── java/com/example/sonygps/\n│   │   ├── GpsForegroundService.kt   Foreground service: GPS + BLE session management\n│   │   ├── MainActivity.kt           UI: BLE scan, camera selection, service binding\n│   │   ├── SonyCameraGatt.kt         BLE GATT client: handshake, op-queue, APO keepalive\n│   │   └── SonyGpsPacket.kt          GPS packet assembly (91/95 bytes, Sony format)\n│   ├── res/\n│   │   ├── layout/activity_main.xml\n│   │   ├── drawable/ic_launcher_*.xml\n│   │   ├── mipmap-anydpi-v26/\n│   │   └── values/themes.xml, colors.xml\n│   └── AndroidManifest.xml\n├── gradle/\n│   ├── libs.versions.toml\n│   └── wrapper/gradle-wrapper.properties\n├── build.gradle.kts\n├── settings.gradle.kts\n├── gradle.properties            android.useAndroidX=true\n└── gradlew / gradlew.bat\n```\n\n---\n\n## Build \u0026 Installation\n\n### Requirements\n- Android Studio Hedgehog (2023.1.1) or newer\n- Android SDK 34\n- Kotlin 1.9.x\n- Physical device running Android 8.0+ (API 26) with BLE support\n\n### Steps\n1. Open the `SonyGpsApp/` folder in Android Studio\n2. Wait for Gradle sync to complete (all dependencies are downloaded automatically)\n3. Connect a device via USB\n4. **Run** (`Shift+F10`)\n\n\u003e **Note:** An emulator supports neither BLE nor real GPS — a physical device is required for testing.\n\n---\n\n## Permissions\n\n| Permission | Purpose | Required from |\n|---|---|---|\n| `ACCESS_FINE_LOCATION` | GPS coordinates | API 1 |\n| `ACCESS_COARSE_LOCATION` | Fallback location | API 1 |\n| `BLUETOOTH_SCAN` | BLE scanning | API 31 |\n| `BLUETOOTH_CONNECT` | GATT connection | API 31 |\n| `BLUETOOTH` + `BLUETOOTH_ADMIN` | BLE (legacy) | API ≤ 30 |\n| `FOREGROUND_SERVICE` | Start a foreground service | API 28 |\n| `FOREGROUND_SERVICE_LOCATION` | GPS access in foreground service | API 34 |\n| `FOREGROUND_SERVICE_CONNECTED_DEVICE` | BLE access in foreground service | API 34 |\n| `POST_NOTIFICATIONS` | Display service notification | API 33 |\n\n---\n\n## Known Limitations\n\n- **Pairing required:** The camera must be paired via the Android system Bluetooth settings (not through this app). The pairing protocol from the Sony app is not implemented.\n- **BLE GPS protocol only:** WiFi (PTP/IP, port 15740) and USB (PTP/MTP) are not supported — BLE only.\n- **No live viewfinder:** The app transfers GPS data only. Camera control and image transfer are not implemented.\n- **GPS accuracy:** `PRIORITY_HIGH_ACCURACY` uses the hardware GPS chip. Indoors or in poor reception conditions, fixes older than 10 seconds are automatically discarded.\n\n---\n\n## Sources \u0026 Reverse Engineering\n\n| File (decompiled from Sony Creators App v3.3.1) | Finding |\n|---|---|\n| `BluetoothLeUtil.java` | GPS packet encoding (`setLocationAndTime`) |\n| `TransferringLocationInfoWithLockState.java` | Handshake sequence, packet structure, byte layout |\n| `ExecutingApoAvoidanceState.java` | APO keepalive command `{3,8,16,0}`, interval 9,000 ms |\n| `BluetoothGattUtil.java` | UUID constants, byte constants for all commands |\n| `EnumCameraInfo.java` | Timezone support flag (Format A vs. B) |\n| `BluetoothLeUtil.startLeScanWithLowPower()` | BLE scan filter (manufacturer ID 301) |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasdoos%2Fsonygpsapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasdoos%2Fsonygpsapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasdoos%2Fsonygpsapp/lists"}