implement Milestone 16: Bluetooth interface with user consent

This commit is contained in:
2026-01-18 16:21:06 +01:00
parent 4ab5e52259
commit 00b9ceb467
6 changed files with 1529 additions and 1 deletions

View File

@@ -29,6 +29,7 @@ add_library(mosis-sandbox STATIC
../src/main/cpp/sandbox/audio_output.cpp
../src/main/cpp/sandbox/location_interface.cpp
../src/main/cpp/sandbox/sensor_interface.cpp
../src/main/cpp/sandbox/bluetooth_interface.cpp
)
target_include_directories(mosis-sandbox PUBLIC
../src/main/cpp/sandbox

View File

@@ -19,6 +19,7 @@
#include "audio_output.h"
#include "location_interface.h"
#include "sensor_interface.h"
#include "bluetooth_interface.h"
#include <filesystem>
#include <fstream>
#include <sstream>
@@ -2687,6 +2688,208 @@ bool Test_SensorLuaIntegration(std::string& error_msg) {
return true;
}
//=============================================================================
// Milestone 16: Bluetooth
//=============================================================================
bool Test_BluetoothRequiresPermission(std::string& error_msg) {
mosis::BluetoothInterface bluetooth("test.app");
// No permission granted
std::string err;
bool started = bluetooth.StartDiscovery(
{},
[](const std::vector<mosis::BluetoothDevice>&) {},
[](mosis::BluetoothError, const std::string&) {},
err
);
EXPECT_TRUE(!started);
EXPECT_TRUE(err.find("permission") != std::string::npos);
return true;
}
bool Test_BluetoothRequiresUserConsent(std::string& error_msg) {
mosis::BluetoothInterface bluetooth("test.app");
bluetooth.SetBluetoothPermission(true);
// User consent NOT granted
std::string err;
bool started = bluetooth.StartDiscovery(
{},
[](const std::vector<mosis::BluetoothDevice>&) {},
[](mosis::BluetoothError, const std::string&) {},
err
);
EXPECT_TRUE(!started);
EXPECT_TRUE(err.find("consent") != std::string::npos ||
err.find("gesture") != std::string::npos);
return true;
}
bool Test_BluetoothDiscoveryWorks(std::string& error_msg) {
mosis::BluetoothInterface bluetooth("test.app");
bluetooth.SetBluetoothPermission(true);
bluetooth.SetUserConsent(true);
// Set mock devices
std::vector<mosis::BluetoothDevice> devices = {
{"Device A", "AA:BB:CC:DD:EE:FF"},
{"Device B", "11:22:33:44:55:66"}
};
bluetooth.SetMockDevices(devices);
std::vector<mosis::BluetoothDevice> discovered;
bool got_devices = false;
std::string err;
bool started = bluetooth.StartDiscovery(
{},
[&](const std::vector<mosis::BluetoothDevice>& devs) {
got_devices = true;
discovered = devs;
},
[](mosis::BluetoothError, const std::string&) {},
err
);
EXPECT_TRUE(started);
EXPECT_TRUE(got_devices);
EXPECT_TRUE(discovered.size() == 2);
EXPECT_TRUE(discovered[0].name == "Device A");
EXPECT_TRUE(discovered[0].address == "AA:BB:CC:DD:EE:FF");
return true;
}
bool Test_BluetoothConnectionLimit(std::string& error_msg) {
mosis::BluetoothInterface bluetooth("test.app");
bluetooth.SetBluetoothPermission(true);
std::vector<std::shared_ptr<mosis::BluetoothConnection>> connections;
std::string err;
// Create MAX_CONNECTIONS (5)
for (int i = 0; i < 5; i++) {
char addr[20];
snprintf(addr, sizeof(addr), "AA:BB:CC:DD:EE:%02X", i);
auto conn = bluetooth.Connect(addr, {}, err);
EXPECT_TRUE(conn != nullptr);
connections.push_back(conn);
}
// 6th should fail
auto extra = bluetooth.Connect("AA:BB:CC:DD:EE:FF", {}, err);
EXPECT_TRUE(extra == nullptr);
EXPECT_TRUE(err.find("limit") != std::string::npos);
return true;
}
bool Test_BluetoothSendReceive(std::string& error_msg) {
mosis::BluetoothInterface bluetooth("test.app");
bluetooth.SetBluetoothPermission(true);
std::string err;
auto conn = bluetooth.Connect("AA:BB:CC:DD:EE:FF", {}, err);
EXPECT_TRUE(conn != nullptr);
EXPECT_TRUE(conn->IsConnected());
// Test send
std::vector<uint8_t> data = {0x01, 0x02, 0x03};
bool sent = conn->Send(data, err);
EXPECT_TRUE(sent);
// Test receive via simulation
std::vector<uint8_t> received;
bool got_data = false;
conn->SetOnData([&](const std::vector<uint8_t>& d) {
got_data = true;
received = d;
});
std::vector<uint8_t> incoming = {0xAA, 0xBB};
conn->SimulateData(incoming);
EXPECT_TRUE(got_data);
EXPECT_TRUE(received.size() == 2);
EXPECT_TRUE(received[0] == 0xAA);
return true;
}
bool Test_BluetoothCleansUpOnShutdown(std::string& error_msg) {
mosis::BluetoothInterface bluetooth("test.app");
bluetooth.SetBluetoothPermission(true);
std::string err;
auto conn1 = bluetooth.Connect("AA:BB:CC:DD:EE:01", {}, err);
auto conn2 = bluetooth.Connect("AA:BB:CC:DD:EE:02", {}, err);
EXPECT_TRUE(bluetooth.GetActiveConnectionCount() == 2);
bluetooth.Shutdown();
EXPECT_TRUE(bluetooth.GetActiveConnectionCount() == 0);
EXPECT_TRUE(!conn1->IsConnected());
EXPECT_TRUE(!conn2->IsConnected());
return true;
}
bool Test_BluetoothLuaIntegration(std::string& error_msg) {
SandboxContext ctx = TestContext();
LuaSandbox sandbox(ctx);
mosis::BluetoothInterface bluetooth("test.app");
mosis::RegisterBluetoothAPI(sandbox.GetState(), &bluetooth);
std::string script = R"lua(
-- Test that bluetooth global exists
if not bluetooth then
error("bluetooth global not found")
end
if not bluetooth.startDiscovery then
error("bluetooth.startDiscovery not found")
end
if not bluetooth.stopDiscovery then
error("bluetooth.stopDiscovery not found")
end
if not bluetooth.connect then
error("bluetooth.connect not found")
end
if not bluetooth.disconnectAll then
error("bluetooth.disconnectAll not found")
end
if not bluetooth.getConnectionCount then
error("bluetooth.getConnectionCount not found")
end
if not bluetooth.isDiscovering then
error("bluetooth.isDiscovering not found")
end
-- Connection count should be 0 initially
if bluetooth.getConnectionCount() ~= 0 then
error("should have no active connections initially")
end
-- Should not be discovering initially
if bluetooth.isDiscovering() then
error("should not be discovering initially")
end
)lua";
bool ok = sandbox.LoadString(script, "bluetooth_test");
if (!ok) {
error_msg = "Lua test failed: " + sandbox.GetLastError();
return false;
}
return true;
}
//=============================================================================
// MAIN
//=============================================================================
@@ -2870,6 +3073,15 @@ int main(int argc, char* argv[]) {
harness.AddTest("SensorCleansUpOnShutdown", Test_SensorCleansUpOnShutdown);
harness.AddTest("SensorLuaIntegration", Test_SensorLuaIntegration);
// Milestone 16: Bluetooth
harness.AddTest("BluetoothRequiresPermission", Test_BluetoothRequiresPermission);
harness.AddTest("BluetoothRequiresUserConsent", Test_BluetoothRequiresUserConsent);
harness.AddTest("BluetoothDiscoveryWorks", Test_BluetoothDiscoveryWorks);
harness.AddTest("BluetoothConnectionLimit", Test_BluetoothConnectionLimit);
harness.AddTest("BluetoothSendReceive", Test_BluetoothSendReceive);
harness.AddTest("BluetoothCleansUpOnShutdown", Test_BluetoothCleansUpOnShutdown);
harness.AddTest("BluetoothLuaIntegration", Test_BluetoothLuaIntegration);
// Run tests
auto results = harness.Run(filter);