# Milestone 18: Inter-App Communication **Status**: Complete **Goal**: Kernel-mediated message passing between apps. --- ## Overview This milestone implements secure inter-app communication via an intent system: - Apps register to receive specific intent actions - Senders must have required permissions - All messages go through kernel mediation (no direct app-to-app) - Message size limits prevent resource exhaustion - Audit logging for all inter-app communication ### Key Deliverables 1. **MessageBus class** - Central message routing and validation 2. **Intent struct** - Message format with action, type, and data 3. **Lua intents API** - `intents.send()`, `intents.on()`, `intents.broadcast()` 4. **Security validation** - Permission checks, sender/receiver verification --- ## File Structure ``` src/main/cpp/sandbox/ ├── message_bus.h # NEW - Message bus API header └── message_bus.cpp # NEW - Message bus implementation ``` --- ## Implementation Details ### 1. MessageBus Class ```cpp // message_bus.h #pragma once #include #include #include #include #include #include struct lua_State; namespace mosis { struct Intent { std::string action; // Intent action (e.g., "share", "view", "edit") std::string type; // MIME type (e.g., "text/plain", "image/png") std::string data; // Intent data/payload std::string from_app; // Sender app ID (set by kernel) }; struct IntentFilter { std::string action; // Required action std::vector types; // Accepted MIME types (empty = all) }; enum class MessageError { None, NoReceivers, PermissionDenied, InvalidIntent, DataTooLarge, ReceiverNotFound, SenderBlocked }; class MessageBus { public: using IntentHandler = std::function; MessageBus(); ~MessageBus(); // Register an app to receive intents void RegisterReceiver( const std::string& app_id, const IntentFilter& filter, IntentHandler handler ); // Unregister all handlers for an app void UnregisterApp(const std::string& app_id); // Send intent to specific app (requires permission) MessageError Send( const std::string& sender_app, const std::string& target_app, const Intent& intent, std::string& error ); // Broadcast intent to all registered receivers MessageError Broadcast( const std::string& sender_app, const Intent& intent, std::string& error ); // Check if any app handles this action bool HasReceiverFor(const std::string& action) const; // Get list of apps that handle an action std::vector GetReceiversFor(const std::string& action) const; // Permission management void SetAppPermission(const std::string& app_id, const std::string& permission, bool granted); bool HasPermission(const std::string& app_id, const std::string& permission) const; // Block specific app from sending/receiving void BlockApp(const std::string& app_id); void UnblockApp(const std::string& app_id); bool IsBlocked(const std::string& app_id) const; // Clear all registrations (for testing) void Clear(); // Statistics size_t GetReceiverCount() const; size_t GetMessageCount() const { return m_message_count; } private: struct ReceiverEntry { std::string app_id; IntentFilter filter; IntentHandler handler; }; std::vector m_receivers; std::unordered_map> m_permissions; std::unordered_set m_blocked_apps; mutable std::mutex m_mutex; size_t m_message_count = 0; static constexpr size_t MAX_DATA_SIZE = 1024 * 1024; // 1MB max bool MatchesFilter(const Intent& intent, const IntentFilter& filter) const; bool ValidateIntent(const Intent& intent, std::string& error) const; }; // Register intents.* APIs as globals void RegisterIntentsAPI(lua_State* L, MessageBus* bus, const std::string& app_id); } // namespace mosis ``` ### 2. Standard Intent Actions | Action | Description | Required Permission | |--------|-------------|---------------------| | `share` | Share content with another app | None | | `view` | Request app to view content | None | | `edit` | Request app to edit content | None | | `pick` | Request user to pick item | None | | `call` | Initiate phone call | `phone.call` | | `message` | Send SMS/message | `sms.send` | | `email` | Compose email | None | ### 3. Permission Requirements | Operation | Permission Required | |-----------|---------------------| | Send intent | Action-specific (see above) | | Broadcast intent | Action-specific | | Register receiver | None | | Receive intent | None | ### 4. Lua API ```lua -- Register to receive intents intents.on("share", function(intent) print("Received share from:", intent.from) print("Type:", intent.type) print("Data:", intent.data) end) -- Register with MIME type filter intents.on("view", function(intent) -- Handle view intent end, {types = {"image/png", "image/jpeg"}}) -- Send intent to specific app local success, err = intents.send("target.app.id", { action = "share", type = "text/plain", data = "Hello world" }) -- Broadcast to all handlers local count = intents.broadcast({ action = "share", type = "text/plain", data = "Hello everyone" }) -- Check if any app handles an action local hasHandler = intents.hasReceiver("share") -- Get list of apps that handle an action local apps = intents.getReceivers("share") -- Unregister all handlers for this app intents.unregisterAll() ``` ### 5. Error Handling ```lua -- Errors raised via Lua errors local ok, err = pcall(function() intents.send("target.app", {action = "share", data = "test"}) end) if not ok then print("Error:", err) -- e.g., "no receivers for action: share" end ``` --- ## Test Cases ### Test 1: Send to Registered Receiver ```cpp bool Test_MessageBusSendToReceiver(std::string& error_msg) { mosis::MessageBus bus; bool received = false; mosis::Intent receivedIntent; // Register receiver bus.RegisterReceiver("receiver.app", {"share", {}}, [&](const mosis::Intent& i) { received = true; receivedIntent = i; }); // Send intent mosis::Intent intent{"share", "text/plain", "Hello", "sender.app"}; std::string err; auto result = bus.Send("sender.app", "receiver.app", intent, err); EXPECT_TRUE(result == mosis::MessageError::None); EXPECT_TRUE(received); EXPECT_TRUE(receivedIntent.action == "share"); EXPECT_TRUE(receivedIntent.data == "Hello"); EXPECT_TRUE(receivedIntent.from_app == "sender.app"); return true; } ``` ### Test 2: Block Unregistered Action ```cpp bool Test_MessageBusBlockUnregistered(std::string& error_msg) { mosis::MessageBus bus; // No receivers registered mosis::Intent intent{"share", "text/plain", "Hello", "sender.app"}; std::string err; auto result = bus.Send("sender.app", "receiver.app", intent, err); EXPECT_TRUE(result == mosis::MessageError::ReceiverNotFound); EXPECT_TRUE(err.find("not found") != std::string::npos || err.find("no receiver") != std::string::npos); return true; } ``` ### Test 3: Broadcast to Multiple Receivers ```cpp bool Test_MessageBusBroadcast(std::string& error_msg) { mosis::MessageBus bus; int receiveCount = 0; // Register multiple receivers for same action bus.RegisterReceiver("app1", {"share", {}}, [&](const mosis::Intent&) { receiveCount++; }); bus.RegisterReceiver("app2", {"share", {}}, [&](const mosis::Intent&) { receiveCount++; }); bus.RegisterReceiver("app3", {"other", {}}, [&](const mosis::Intent&) { receiveCount++; }); // Broadcast share intent mosis::Intent intent{"share", "text/plain", "Hello", "sender.app"}; std::string err; auto result = bus.Broadcast("sender.app", intent, err); EXPECT_TRUE(result == mosis::MessageError::None); EXPECT_TRUE(receiveCount == 2); // Only app1 and app2 return true; } ``` ### Test 4: MIME Type Filtering ```cpp bool Test_MessageBusMimeFilter(std::string& error_msg) { mosis::MessageBus bus; bool imageReceived = false; bool textReceived = false; // Register with MIME filter bus.RegisterReceiver("image.viewer", {"view", {"image/png", "image/jpeg"}}, [&](const mosis::Intent&) { imageReceived = true; }); bus.RegisterReceiver("text.viewer", {"view", {"text/plain"}}, [&](const mosis::Intent&) { textReceived = true; }); // Send image intent mosis::Intent imgIntent{"view", "image/png", "data", "sender"}; std::string err; bus.Broadcast("sender", imgIntent, err); EXPECT_TRUE(imageReceived); EXPECT_TRUE(!textReceived); return true; } ``` ### Test 5: Data Size Limit ```cpp bool Test_MessageBusDataLimit(std::string& error_msg) { mosis::MessageBus bus; bus.RegisterReceiver("receiver", {"share", {}}, [](const mosis::Intent&) {}); // Create oversized data (> 1MB) std::string largeData(2 * 1024 * 1024, 'x'); mosis::Intent intent{"share", "text/plain", largeData, "sender"}; std::string err; auto result = bus.Send("sender", "receiver", intent, err); EXPECT_TRUE(result == mosis::MessageError::DataTooLarge); return true; } ``` ### Test 6: Blocked App Cannot Send ```cpp bool Test_MessageBusBlockedApp(std::string& error_msg) { mosis::MessageBus bus; bus.RegisterReceiver("receiver", {"share", {}}, [](const mosis::Intent&) {}); bus.BlockApp("bad.app"); mosis::Intent intent{"share", "text/plain", "Hello", "bad.app"}; std::string err; auto result = bus.Send("bad.app", "receiver", intent, err); EXPECT_TRUE(result == mosis::MessageError::SenderBlocked); return true; } ``` ### Test 7: Lua Integration ```cpp bool Test_MessageBusLuaIntegration(std::string& error_msg) { SandboxContext ctx = TestContext(); LuaSandbox sandbox(ctx); mosis::MessageBus bus; mosis::RegisterIntentsAPI(sandbox.GetState(), &bus, "test.app"); std::string script = R"lua( -- Test that intents global exists if not intents then error("intents global not found") end if not intents.on then error("intents.on not found") end if not intents.send then error("intents.send not found") end if not intents.broadcast then error("intents.broadcast not found") end if not intents.hasReceiver then error("intents.hasReceiver not found") end if not intents.getReceivers then error("intents.getReceivers not found") end if not intents.unregisterAll then error("intents.unregisterAll not found") end )lua"; bool ok = sandbox.LoadString(script, "intents_test"); if (!ok) { error_msg = "Lua test failed: " + sandbox.GetLastError(); return false; } return true; } ``` --- ## Acceptance Criteria All tests must pass: - [x] `Test_MessageBusSendToReceiver` - Send reaches registered receiver - [x] `Test_MessageBusBlockUnregistered` - Unregistered action fails - [x] `Test_MessageBusBroadcast` - Broadcast reaches all matching receivers - [x] `Test_MessageBusMimeFilter` - MIME type filtering works - [x] `Test_MessageBusDataLimit` - Data size limit enforced - [x] `Test_MessageBusBlockedApp` - Blocked apps cannot send - [x] `Test_MessageBusLuaIntegration` - Lua API works --- ## Dependencies - Milestone 1 (LuaSandbox) - Milestone 2 (PermissionGate) - Milestone 3 (AuditLog) --- ## Notes ### Security Considerations 1. **Kernel mediation**: All messages route through MessageBus, no direct app-to-app 2. **Sender verification**: `from_app` is set by kernel, cannot be spoofed 3. **Permission checks**: Sensitive actions require permissions 4. **Size limits**: 1MB max to prevent resource exhaustion 5. **App blocking**: Misbehaving apps can be blocked 6. **Audit logging**: All inter-app communication is logged ### Privacy Features 1. **Opt-in receiving**: Apps must explicitly register for actions 2. **MIME filtering**: Receivers can filter by content type 3. **No discovery**: Apps cannot enumerate other apps (only check receivers) --- ## Next Steps After Milestone 18 passes: 1. Milestone 19: Security Testing Suite