Files
FastCarPlay/src/protocol.cpp
T
2026-03-10 01:15:01 +02:00

381 lines
9.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "protocol.h"
#include "helper/protocol_const.h"
#include "helper/functions.h"
#include <cstring>
#include <iomanip>
#include <cctype>
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<const uint8_t *>(value),
static_cast<uint32_t>(len));
}
// overload for a single 32bit 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<uint32_t>(t.state));
write_uint32_le(p + 12, static_cast<uint32_t>(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 nullterminated, 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<uint8_t> 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<Message>(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<Message>(data, length, 12));
dispose = false;
break;
}
if (channel == 2)
{
audioStreamAux.pushDiscard(std::make_unique<Message>(data, length, 12));
dispose = false;
break;
}
break;
}
case CMD_ENCRYPTION:
if (length == 0)
setEncryption(true);
break;
}
if (dispose && length > 0 && data)
free(data);
}