
USB AOA के माध्यम से प्रोटोकॉल रिवर्स इंजीनियरिंग, TLS पारस्परिक प्रमाणीकरण, H.264 वीडियो प्रोजेक्शन, टच इनपुट इंजेक्शन और सेंसर डेटा स्ट्रीमिंग वाला ओपन-सोर्स Android Auto फ़ोन-साइड कार्यान्वयन।
Android Auto फ़ोन-साइड ऐप का एक ओपन-सोर्स कार्यान्वयन। यह ऐप आपके फ़ोन पर चलता है और USB के माध्यम से कार के हेड यूनिट पर प्रोजेक्ट करता है, जो Google के मालिकाना com.google.android.projection.gearhead APK की जगह लेता है।
यह प्रोजेक्ट प्रारंभिक विकास चरण में है। प्रोटोकॉल हैंडशेक और वीडियो प्रोजेक्शन एक वास्तविक हेड यूनिट के साथ काम कर रहे हैं। फ़ोन की स्क्रीन डिस्कनेक्ट होने से पहले कुछ सेकंड के लिए कार के हेड यूनिट पर सफलतापूर्वक प्रदर्शित होती है (वीडियो स्थिरता में सुधार किया जा रहा है)।
अपने जोखिम पर उपयोग करें। यह सॉफ़्टवेयर "जैसा है" प्रदान किया गया है, बिना किसी प्रकार की वारंटी के।
USB Plug-in → MainActivity → ProjectionService ↓ UsbAoaTransport (USB AOA accessory mode) ↓ MessageFramer (16KB frame fragmentation) ↓ InBandTls (TLSv1.2 via SSLEngine) ↓ ProtocolEngine (AAP state machine) ↓ ┌───────────┼───────────┐ Video Input Audio (H.264) (touch/keys) (PCM)
## ज्ञात बग
- **चैनल असाइनमेंट क्रम मान लेता है** — हम SERVICE_DISCOVERY_RESPONSE में पहले `av_channel` को वीडियो और दूसरे को ऑडियो के रूप में निर्दिष्ट करते हैं। यह कार हेड यूनिट के साथ काम करता है (चैनल 1 = वीडियो) लेकिन openauto के साथ विफल रहता है (चैनल 4 = ऑडियो, वीडियो नहीं)। हल: `av_channel` के अंदर `stream_type` फ़ील्ड को पार्स करके `VIDEO(3)` को `AUDIO(1)` से अलग करें।
- **वीडियो स्थिरता** — हेड यूनिट के USB बफ़र ओवरफ्लो के कारण लंबे समय तक स्ट्रीमिंग के बाद कनेक्शन टूट जाता है। नीचे परीक्षण परिणाम देखें।
- **हेड यूनिट पर डुप्लीकेट डिवाइस सूची** — हेड यूनिट का स्मार्टफ़ोन पृष्ठ हमारे ऐप को दो अलग-अलग प्रविष्टियों (एक Android Auto के लिए, एक Bluetooth के लिए) के रूप में दिखाता है, न कि दोनों क्षमताओं वाली एकल प्रविष्टि के रूप में। यह Android 12+ द्वारा वास्तविक Bluetooth MAC पते तक पहुंच को अवरुद्ध करने के कारण होता है (`02:00:00:00:00:00` लौटाता है)। समाधान: वास्तविक पते को कॉन्फ़िग फ़ाइल में लिखें: `adb shell "echo $(adb shell settings get secure bluetooth_address) > /sdcard/Android/data/org.openandroidauto/files/bt_address.txt"`। उपयोगकर्ता को मैन्युअल रूप से अपना BT MAC दर्ज करने देने के लिए एक UI सेटिंग्स स्क्रीन की आवश्यकता है।
- **वॉइस असिस्टेंट बटन हैंडल नहीं किया गया** — जब ड्राइवर हेड यूनिट पर वॉइस/असिस्टेंट बटन दबाता है, तो हमें VOICE_SESSION_REQUEST प्राप्त होता है और हम एक वॉइस असिस्टेंट (Dicio या सिस्टम डिफ़ॉल्ट) लॉन्च करने का प्रयास करते हैं। हालाँकि, लॉन्च किया गया असिस्टेंट अभी हेड यूनिट के माइक्रोफ़ोन से ऑडियो प्राप्त नहीं करता है।
### वीडियो स्थिरता परीक्षण परिणाम
परीक्षण पैटर्न (कलर बार) 800x480 पर, I-फ्रेम अंतराल 1 सेकंड:
| FPS | बिटरेट | फ़्रैगमेंट | अवधि | फ़्रेम | स्थिति |
|-----|---------|----------|----------|--------|--------|
| 30 | 2Mbps | नहीं | ~3s | ~90 | ❌ बहुत तेज़ |
| 15 | 2Mbps | नहीं | ~33s | ~500 | ⚠️ बेहतर |
| 10 | 2Mbps | नहीं | ~93s | ~930 | ⚠️ अच्छा |
| 30 | 2Mbps | हाँ (2KB) | 5-25s | 150-750 | ⚠️ परिवर्तनशील |
| 30 | 500Kbps | हाँ (2KB) | ~54s | ~1691 | ⚠️ बेहतर |
| 15 | 250Kbps | हाँ (2KB) | ~67s+ | 1000+ | ⚠️ अच्छा |
| 30 | 250Kbps | हाँ (2KB), I=5s | ~20s | ~600 | ❌ लंबे I-फ्रेम के साथ बदतर |
| 15 | 250Kbps | नहीं | ~13s | ~200 | ❌ यहाँ फ़्रैगमेंटेशन ने मदद की |
मूल कारण: निरंतर उच्च थ्रूपुट के साथ हेड यूनिट का USB रिसीव बफ़र ओवरफ्लो हो जाता है। कम डेटा दर = लंबा कनेक्शन।
**सबसे अच्छी पुष्टि की गई कॉन्फ़िग:** 10fps, 2Mbps, कोई फ़्रैगमेंटेशन नहीं = 93 सेकंड। एन्क्रिप्ट-से-पहले-फ़्रैगमेंट (Fragment-before-encrypt) कार्यान्वयन टूटा हुआ है (हेड यूनिट पुनः जोड़ नहीं सकता) — आगे जाँच की आवश्यकता है।
## निर्माण```bash
./gradlew assembleDebug
Android SDK, प्लेटफ़ॉर्म 35 के साथ आवश्यक है।
./gradlew testDebugUnitTest
113 यूनिट और इंटीग्रेशन टेस्ट जो प्रोटोकॉल, फ्रेमिंग, TLS, चैनल लॉजिक, वीडियो स्टेट मशीन, सेंसर हैंडलिंग और टच इनपुट को कवर करते हैं।
### openauto (Docker) के साथ इंटीग्रेशन टेस्टिंग
openauto एक तृतीय-पक्ष हेड यूनिट एमुलेटर है जो पूर्ण Android Auto प्रोटोकॉल बोलता है। हम इसका उपयोग बिना वास्तविक कार की आवश्यकता के अपने प्रोटोकॉल इम्प्लीमेंटेशन को सत्यापित करने के लिए करते हैं।
#### पूर्वापेक्षाएँ
- Docker इंस्टॉल हो और चालू हो
- फ़ोन ADB के माध्यम से कनेक्ट हो (USB या वायरलेस)
- फ़ोन पर ऐप इंस्टॉल हो: `./gradlew assembleDebug && adb install -r app/build/outputs/apk/debug/app-debug.apk`
#### 1. openauto Docker इमेज बनाएँ (एक बार)```bash
cd thirdparty/openauto
docker build -f Dockerfile.headless -t openauto-headless .
यह Debian कंटेनर में सभी निर्भरताओं (Qt5, boost, protobuf, OpenSSL) के साथ openauto बनाता है। पहली बार बनाने में ~5 मिनट लगते हैं।
docker run --rm -p 5100:5000 -e QT_QPA_PLATFORM=offscreen
openauto-headless timeout 60 /src/build/bin/autoapp
openauto कंटेनर के अंदर पोर्ट 5000 पर सुनता है, जो होस्ट पर पोर्ट 5100 पर मैप किया गया है। यह हेडलेस मोड में चलता है (कोई डिस्प्ले आवश्यक नहीं)।
#### 3. ADB रिवर्स पोर्ट फ़ॉरवर्डिंग सेट करें```bash
adb reverse tcp:5000 tcp:5100
यह फ़ोन के localhost:5000 को कंप्यूटर के localhost:5100 (openauto) से टनल करता है। जब कोई USB एक्सेसरी नहीं मिलती है, तो हमारा ऐप TCP क्लाइंट के रूप में localhost:5000 से कनेक्ट होता है।
adb shell am start -n org.openandroidauto/.MainActivity
ऐप निम्न कार्य करेगा:
1. USB एक्सेसरी नहीं मिल पाएगी
2. `localhost:5000` से कनेक्ट करेगा (adb reverse के माध्यम से openauto)
3. पूर्ण प्रोटोकॉल हैंडशेक करेगा (VERSION → TLS → AUTH → SERVICE_DISCOVERY)
4. चैनल खोलेगा (वीडियो, ऑडियो, इनपुट, सेंसर)
5. वीडियो स्ट्रीमिंग शुरू करेगा (टेस्ट पैटर्न)
#### 5. openauto लॉग्स में सत्यापित करें
आपको openauto आउटपुट में यह दिखना चाहिए:```
[OpenAuto] handleNewClient() - Handle WIFI Client Connection
[OpenAuto] [AndroidAutoEntity] Send Version Request.
[OpenAuto] [AndroidAutoEntity] onVersionResponse()
[OpenAuto] [AndroidAutoEntity] Beginning SSL handshake.
[OpenAuto] [AndroidAutoEntity] Handshake completed.
[OpenAuto] [AndroidAutoEntity] onServiceDiscoveryRequest()
[OpenAuto] [AndroidAutoEntity] onAudioFocusRequest()
[OpenAuto] [AudioMediaSinkService] onChannelOpenRequest()
[OpenAuto] [VideoMediaSinkService] onChannelOpenRequest() (if video focus granted)
adb pull /sdcard/Android/data/org.openandroidauto/files/aa_log.txt cat aa_log.txt
लॉग फ़ाइल USB स्विचों के बीच फोन पर बनी रहती है (वास्तविक कार हेड यूनिट के साथ परीक्षण करते समय उपयोगी)।
#### त्वरित वन-लाइनर परीक्षण```bash
# Assumes openauto image already built and app installed
docker run --rm -p 5100:5000 -e QT_QPA_PLATFORM=offscreen openauto-headless timeout 20 /src/build/bin/autoapp &
sleep 3 && adb reverse tcp:5000 tcp:5100 && adb shell am start -n org.openandroidauto/.MainActivity
Message Id not Handled: 4 लॉग करता है — यह एक ज्ञात openauto विशेषता है, त्रुटि नहींAndroid Auto प्रोटोकॉल को कई परियोजनाओं में रिवर्स-इंजीनियर किया गया था। प्रत्येक ने पिछले के आधार पर निर्माण किया:
opencardev/aasdk AACS से अतिरिक्त क्या जोड़ता है:
प्रमुख संरचनात्मक अंतर:
priority + channel_id का उपयोग करता है; aasdk priority (sint32) + service_id का उपयोग करता हैthirdparty/aasdk/protobuf/ निर्देशिका इस परियोजना के लिए आधिकारिक प्रोटोकॉल संदर्भ है।
Android Auto पारस्परिक TLS का उपयोग करता है। फ़ोन TLS सर्वर के रूप में कार्य करता है और उसे Google Automotive Link CA (हेड यूनिट फर्मवेयर में निर्मित) द्वारा हस्ताक्षरित प्रमाणपत्र प्रस्तुत करना चाहिए। सही निजी कुंजी के बिना, हेड यूनिट AUTH_COMPLETE status=-3 के साथ कनेक्शन अस्वीकार कर देती है।
फ़ोन 2-प्रमाणपत्र श्रृंखला प्रस्तुत करता है:
O=CarService, Google Automotive Link CA द्वारा हस्ताक्षरितO=Google Automotive Link (मान्य 2014-2044)ऐसा प्रतीत होता है कि Google Android Auto APK में एम्बेडेड प्रमाणपत्र+कुंजी को लगभग हर 8 महीने में घुमाता है (प्रमाणपत्र की वैधता अवधि से मेल खाता हुआ)। यह निकाली गई कुंजियों की उपयोगिता को सीमित करने के लिए एक जानबूझकर उपाय हो सकता है — यदि कोई हेड यूनिट प्रमाणपत्र समाप्ति की जाँच करती है, तो पुरानी निकाली गई कुंजी काम करना बंद कर देगी। आधिकारिक ऐप के उपयोगकर्ताओं को ऐप अपडेट के माध्यम से नए प्रमाणपत्र मिलते हैं। यदि यह सिद्धांत सही है, तो एक उपयोगकर्ता जो आधिकारिक ऐप को कभी अपडेट नहीं करता, उसे समाप्ति लागू करने वाली हेड यूनिट द्वारा अंततः अस्वीकार किया जा सकता है। सभी हेड यूनिट समाप्ति की जाँच नहीं कर सकतीं — यह व्यवहार मॉडल-निर्भर है।
निजी कुंजी Android Auto APK के अंदर AES-256-CBC एन्क्रिप्टेड होती है। हेड यूनिट फ़ोन के प्रमाणपत्र को Google Automotive Link CA के विरुद्ध सत्यापित करती है — उस CA द्वारा हस्ताक्षरित कोई भी प्रमाणपत्र स्वीकार किया जाता है।
कुंजी Android Auto APK में एम्बेडेड (एन्क्रिप्टेड) होती है और APK के स्वयं के एल्गोरिदम का उपयोग करके डिक्रिप्ट की जा सकती है। इसके लिए ADB एक्सेस वाले किसी भी Android डिवाइस की आवश्यकता होती है (रूट की आवश्यकता नहीं, Google Play Services की आवश्यकता नहीं) डिक्रिप्शन चलाने के लिए, क्योंकि Android का Base64 डिकोडर डेस्कटॉप JVM से भिन्न व्यवहार करता है।
आवश्यकताएँ:
adb pull द्वारा खींचें, या APKPure/APKMirror से डाउनलोड करें)d8 बिल्ड टूल, adb)प्रक्रिया:
adb pull $(adb shell pm path com.google.android.projection.gearhead | grep base | cut -d: -f2) aa.apkdalvikvm के साथ डिवाइस पर चलाएँप्रमाणपत्र प्रदाता क्लास ढूँढना (चरण 2):
क्लास नाम अस्पष्ट (obfuscated) हैं और APK संस्करणों के बीच बदलते हैं, लेकिन संरचना हमेशा समान होती है। JADX में "-----BEGIN CERTIFICATE-----" खोजें — आपको तीन विधियों वाला एक छोटा इंटरफ़ेस कार्यान्वित करने वाला क्लास मिलेगा:
a() → एक String लौटाता है (CarService प्रमाणपत्र PEM)b() → एक byte[] लौटाता है (~1712 बाइट्स — AES-एन्क्रिप्टेड निजी कुंजी)c() → एक byte[] लौटाता है (256 बाइट्स — KDF नमक)संस्करण के अनुसार ज्ञात क्लास नाम:
डिक्रिप्शन फ़ंक्शन पास के किसी क्लास में है — इसे खोजने के लिए "AES/CBC/PKCS5Padding" खोजें। यह प्रमाणपत्र प्रदाता इंटरफ़ेस को पैरामीटर के रूप में लेता है।
नोट: डिक्रिप्शन चरण (चरण 4) के लिए केवल dalvikvm की आवश्यकता है — ADB वाला कोई भी Android डिवाइस काम करता है, रूट या Google Play Services की आवश्यकता नहीं। GApps आवश्यकता केवल चरण 1 (APK खींचने) के लिए है, क्योंकि AA ऐप Play Store के माध्यम से वितरित किया जाता है।
नोट: आप डीकंपाइल किए गए APK कोड को संशोधित या चलाते नहीं हैं। इसके बजाय, आप एक स्टैंडअलोन Decrypt.java क्लास लिखते हैं जो डिक्रिप्शन तर्क को फिर से लागू करता है, निकाले गए बाइट सरणियों को फ़ाइलों से पढ़ता है, और उसका अपना main() प्रवेश बिंदु होता है। डीकंपाइल किया गया स्रोत केवल एल्गोरिदम को समझने और बाइट सरणियों को कॉपी करने के लिए संदर्भ के रूप में उपयोग किया जाता है। पूर्ण Decrypt.java स्रोत के लिए tools/decrypt_key_from_apk.md देखें।
महत्वपूर्ण JADX बग: JADX KDF सहायक को byte b = bArr2[i2] & 255; के रूप में डीकंपाइल करता है लेकिन इसे int b = bArr2[i2] & 255; होना चाहिए। byte प्रकार वापस साइन्ड में छोटा कर देता है, जिससे बेकार आउटपुट उत्पन्न होता है। इसे int में ठीक करें और डिक्रिप्शन काम करता है।
KDF (tweakBytes/ap) फ़ंक्शन:```java
static void tweakBytes(byte[] bArr, byte[] bArr2, byte[] bArr3) {
for (int i = 0; i < bArr.length; i++) {
for (int i2 = 0; i2 < 48; i2++) {
int b = bArr2[i2] & 255; // MUST be int, not byte
bArr2[i2] = (byte) (((((b >> 7) | (b + b)) + 33) ^ bArr3[i2 % bArr3.length]) ^ bArr[i]);
}
}
}
After AES डिक्रिप्शन के बाद, `T()` फ़ंक्शन कुंजी निकालता है:
- पहले 28 बाइट्स छोड़ें, अंतिम 26 बाइट्स ट्रिम करें
- मध्य भाग को Base64 डिकोड करें (URL_SAFE, flag=2)
- परिणाम PKCS#8 DER एन्कोडेड RSA निजी कुंजी है
**नोट:** `android.util.Base64` बनाम `java.util.Base64` अंतर के कारण Android पर चलना चाहिए (डेस्कटॉप JVM पर नहीं)। डेस्कटॉप JVM का `Base64.getUrlDecoder()` मानक base64 वर्णों (`+`, `/`) और नई पंक्तियों को अस्वीकार करता है जिन्हें Android का डिकोडर स्वीकार करता है। डेस्कटॉप पर `Base64.getMimeDecoder()` का उपयोग करें, या डिक्रिप्शन को `dalvikvm` के साथ डिवाइस पर चलाएं:```bash
# Compile to DEX and run on any Android device with ADB access
javac Decrypt.java -d out
d8 out/Decrypt.class --output dex_out
adb push dex_out/classes.dex /data/local/tmp/decrypt.dex
adb shell "dalvikvm -cp /data/local/tmp/decrypt.dex Decrypt"
यह PKCS#8 निजी कुंजी को base64 में आउटपुट करता है। इसे PEM हेडर में लपेटें और app/src/main/assets/carservice_key.pem पर रखें।
संपूर्ण चरण-दर-चरण मार्गदर्शिका के लिए tools/decrypt_key_from_apk.md देखें।
चूँकि कुछ हेड यूनिट्स प्रमाणपत्र समाप्ति की जाँच नहीं कर सकते हैं, पहले से निकाला गया cert+key जोड़ा (समाप्त हो चुका होने पर भी) काम कर सकता है। स्रोत:
[email protected] (देखें gamelaster/opengal_proxy)KeyFactory.generatePrivate() को हुक करने के लिए Frida का उपयोग करें (एक ही डिवाइस पर रूट और Google Play Services दोनों की आवश्यकता है): ```bash
frida -U -n "com.google.android.projection.gearhead" -l tools/dump_key_frida.js
एक बार प्राप्त होने पर, cert+key को app/src/main/assets/carservice_key.pem पर रखें।
रनटाइम निष्कर्षण स्क्रिप्ट के लिए tools/dump_key.sh और tools/dump_key_frida.js देखें।
यह प्रोजेक्ट GNU General Public License v3.0 के अंतर्गत लाइसेंस प्राप्त है।
इस प्रोजेक्ट में aasdk (GPLv3, Copyright © 2018 f1x.studio / Michal Szwaj) से प्रोटोकॉल बफर परिभाषाएँ एक git सबमॉड्यूल के रूप में शामिल हैं।
| प्रोजेक्ट | वर्ष | प्रोटो फ़ाइलें | भूमिका |
|---|
| f1xpl/aasdk | 2018 | 1 मोनोलिथिक (Wifi.proto) | मूल RE — कोर प्रोटोकॉल, वीडियो, ऑडियो, इनपुट, सेंसर |
| AACS | 2020 | 28 (संदेश द्वारा विभाजित) | फ़ोन-पक्ष कार्यान्वयन — वीडियो प्रोजेक्शन के लिए न्यूनतम कवरेज |
| opencardev/aasdk | 2024 | 254 (सेवा द्वारा पदानुक्रमित) | निश्चित संदर्भ — सभी सेवाओं के साथ पूर्ण प्रोटोकॉल |
| APK संस्करण | प्रमाणपत्र प्रदाता क्लास | नमक+कुंजी क्लास | डिक्रिप्शन क्लास |
|---|
| v6.4 | SslWrapper (फ़ील्ड o, p) | वही क्लास | SslWrapper.m23915f() |
| v16.8 | ivo / rqi | ivq / rql (फ़ील्ड b, c) | ivq.d() |