#include "protocol.h" #include "helper/protocol_const.h" #include "helper/functions.h" #include #include #include Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding) : Connector(padding), videoData(Settings::videoQueue), audioStreamMain(Settings::audioQueue), audioStreamAux(Settings::audioQueue), _recorder(Settings::audioQueue), _width(width), _height(height), _fps(fps), _keySent(false), _phoneConnected(false) { } Protocol::~Protocol() { } void Protocol::start(uint32_t evtStatus, uint32_t evtPhone) { _evtStatusId = evtStatus; _evtPhoneId = evtPhone; Connector::start(); } void Protocol::stop() { Connector::stop(); } void Protocol::sendInit(int width, int height, int fps) { uint8_t buf[28]; write_uint32_le(&buf[0], width); write_uint32_le(&buf[4], height); write_uint32_le(&buf[8], fps); write_uint32_le(&buf[12], 5); write_uint32_le(&buf[16], 49152); write_uint32_le(&buf[20], 2); write_uint32_le(&buf[24], 2); send(CMD_OPEN, true, buf, 28); } void Protocol::sendConfig() { int syncTime = std::time(nullptr); int drivePosition = Settings::leftDrive ? 0 : 1; // 0==left, 1==right int nightMode = Settings::nightMode; // 0==day, 1==night, 2==auto if (nightMode < 0 || nightMode > 2) nightMode = 2; int mic = 7; if (Settings::micType == 2) mic = 15; if (Settings::micType == 3) mic = 21; int width; int height; switch (Settings::androidMode) { default: width = 800; height = 480; break; case 2: width = 1280; height = 720; break; case 3: width = 1920; height = 1080; break; } if (_width < _height) std::swap(width, height); float scale = std::min((float)width / _width, (float)height / _height); width = _width * scale; height = _height * scale; std::cout << "[Protocol] Request android image " << width << "x" << height << std::endl; char buffer[512]; snprintf(buffer, sizeof(buffer), "{\"syncTime\":%d,\"mediaDelay\":%d,\"drivePosition\":%d," "\"androidAutoSizeW\":%d,\"androidAutoSizeH\":%d,\"HiCarConnectMode\":0," "\"GNSSCapability\":7,\"DashboardInfo\":1,\"UseBTPhone\":0}", syncTime, Settings::mediaDelay.value, drivePosition, width, height); sendString(CMD_JSON_CONTROL, buffer); snprintf(buffer, sizeof(buffer), "{\"DayNightMode\":%d}", nightMode); sendString(CMD_DAYNIGHT, buffer); sendFile("/tmp/night_mode", nightMode); sendFile("/tmp/charge_mode", Settings::weakCharge ? 0 : 2); // Weak charge 0, other 2 sendFile("/etc/box_name", "CarPlay"); sendFile("/tmp/hand_drive_mode", drivePosition); sendInt(CMD_CONTROL, mic); sendInt(CMD_CONTROL, Settings::wifi5 ? 25 : 24); sendInt(CMD_CONTROL, Settings::bluetoothAudio ? 22 : 23); if (Settings::autoconnect) sendInt(CMD_CONTROL, 1002); } bool Protocol::checkKey() { bool result = _keySent; _keySent = false; return result; } void Protocol::sendKey(int key) { sendInt(CMD_CONTROL, key, false); _keySent = true; } void Protocol::requestKeyframe() { sendInt(CMD_CONTROL, BTN_SCREEN_REFRESH, false); } void Protocol::sendFile(const char *filename, const char *value) { uint32_t len = strlen(value); if (len > 16) { throw std::invalid_argument("String too long (max 16 bytes)"); } // note: we send only the ASCII bytes, no trailing '\0' sendFile(filename, reinterpret_cast(value), static_cast(len)); } // overload for a single 32‑bit integer void Protocol::sendFile(const char *filename, int value) { uint8_t buf[4]; write_uint32_le(buf, value); sendFile(filename, buf, 4); } void Protocol::sendClick(float x, float y, bool down) { uint8_t buf[16]; write_uint32_le(buf, down ? 14 : 16); write_uint32_le(buf + 4, int(10000 * x)); write_uint32_le(buf + 8, int(10000 * y)); write_uint32_le(buf + 12, 0); send(CMD_TOUCH, false, buf, 16); } void Protocol::sendMultiTouch(const Multitouch &touches) { int count = touches.size(); if (count == 0) return; uint8_t buf[MUTLITOUCH_MAX_TOUCH * sizeof(Multitouch::Touch)]; uint8_t *p = buf; for (int i = 0; i < count; ++i) { const Multitouch::Touch &t = touches[i]; write_float_le(p + 0, t.x); write_float_le(p + 4, t.y); write_uint32_le(p + 8, static_cast(t.state)); write_uint32_le(p + 12, static_cast(t.id)); p += 16; } send(CMD_MULTI_TOUCH, false, buf, 16 * count); } void Protocol::sendMove(float dx, float dy) { uint8_t buf[16]; write_uint32_le(buf, 15); write_uint32_le(buf + 4, int(10000 * dx)); write_uint32_le(buf + 8, int(10000 * dy)); write_uint32_le(buf + 12, 0); send(CMD_TOUCH, false, buf, 16); } void Protocol::sendAudio(uint8_t *data, uint32_t length) { write_uint32_le(data, 5); write_uint32_le(data + 4, 0); write_uint32_le(data + 8, 3); send(CMD_AUDIO_DATA, false, data, length); } void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t length) { // filename is assumed null‑terminated, so strlen + 1 to include the '\0' uint32_t fn_len = strlen(filename) + 1; // Total buffer size: 4 (fn_len) + fn_len + 4 (content_len) + content_len uint32_t total = 4 + fn_len + 4 + length; std::vector result(total); uint8_t *buf = result.data(); // 1) filename length (LE) write_uint32_le(buf, fn_len); buf += 4; // 2) filename bytes (including the '\0') std::memcpy(buf, filename, fn_len); buf += fn_len; // 3) content length (LE) write_uint32_le(buf, length); buf += 4; // 4) content bytes std::memcpy(buf, data, length); send(CMD_SEND_FILE, true, result.data(), total); } void Protocol::sendInt(uint32_t cmd, uint32_t value, bool encryption) { uint8_t buf[4]; write_uint32_le(buf, value); send(cmd, encryption, buf, 4); } void Protocol::sendString(uint32_t cmd, char *str, bool encryption) { uint32_t total = strlen(str); send(cmd, true, (uint8_t *)str, total); } void Protocol::sendEncryption() { if (!_cipher) { std::cout << "[Protocol] Can't enable encryption: cypher is not initalised" << std::endl; return; } uint8_t buf[4]; write_uint32_le(buf, _cipher->Seed()); send(CMD_ENCRYPTION, false, buf, 4); } void Protocol::onStatus(uint8_t status) { std::cout << "[Protocol] Status " << (int)status << std::endl; pushEvent(_evtStatusId, status); } void Protocol::onDevice(bool connected) { if (connected) { if (Settings::encryption) sendEncryption(); if (Settings::dpi > 0) sendFile("/tmp/screen_dpi", Settings::dpi); sendFile("/etc/android_work_mode", 1); sendInit(_width, _height, _fps); sendConfig(); } else { onPhone(false); setEncryption(false); } } void Protocol::onPhone(bool connected) { if (connected == _phoneConnected) return; _phoneConnected = connected; std::cout << (connected ? "[Protocol] Phone connected" : "[Protocol] Phone disconnected") << std::endl; if (!connected) _recorder.stop(); pushEvent(_evtPhoneId, connected ? 1 : 0); if (connected && Settings::onConnect.value.length() > 1) execute(Settings::onConnect.value.c_str()); if (!connected && Settings::onDisconnect.value.length() > 1) execute(Settings::onDisconnect.value.c_str()); } void Protocol::onControl(int cmd) { switch (cmd) { case 1: _recorder.start(this); break; case 2: _recorder.stop(); break; } } void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data) { bool dispose = true; switch (cmd) { case CMD_CONTROL: if (length == 4) { int cmd = 0; memcpy(&cmd, data, sizeof(int)); onControl(cmd); } break; case CMD_PLUGGED: onPhone(true); break; case CMD_UNPLUGGED: onPhone(false); break; case CMD_VIDEO_DATA: { if (length <= 20) break; videoData.pushDiscard(std::make_unique(data, length, 20)); dispose = false; break; } case CMD_AUDIO_DATA: { if (length <= 16) { break; } int channel = 0; memcpy(&channel, data + 8, sizeof(int)); if (channel == 1) { audioStreamMain.pushDiscard(std::make_unique(data, length, 12)); dispose = false; break; } if (channel == 2) { audioStreamAux.pushDiscard(std::make_unique(data, length, 12)); dispose = false; break; } break; } case CMD_ENCRYPTION: if (length == 0) setEncryption(true); break; } if (dispose && length > 0 && data) free(data); }