-
Notifications
You must be signed in to change notification settings - Fork 82
DRO-708: implement network mode with websocket #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DRO-708: implement network mode with websocket #33
Conversation
…shots - Add GestureController for native accessibility actions - Add ActionDispatcher for unified command routing - Implement WebSocket server with token auth and JSON-RPC - Update API to support binary screenshots - Add Network Access UI to MainActivity
And use more KTX extensions.
…ebSocket handling
formatting and other things
…/rpc/jsonrpc) + pass device info in conn headers
PR Review: DRO-708 - Network Mode with WebSocketThis is a substantial PR implementing network mode with WebSocket support, reverse connections, and gesture controls. Overall, the implementation is solid with good architectural decisions. ✅ StrengthsArchitecture & Design
Code Quality
|
🔒 Security ConcernsCritical Issues
Medium Priority
|
🐛 Potential BugsHigh Priority
Medium Priority
|
⚡ Performance Considerations
|
🧪 Test CoverageGood Coverage ✅
Missing Coverage ❌
Recommendation: Aim for 60%+ code coverage. Network mode is a critical path that needs thorough testing. 📝 Code Quality Issues
|
🎯 RecommendationsBefore Merge (Critical)
Post-Merge Improvements
📊 Summary
Overall: Well-architected feature that significantly enhances portal capabilities. The hardcoded User ID is critical and must be fixed before merge. Other issues are manageable as post-merge improvements. Great job on refactoring and test additions! 🎉 |
Code Review: PR #33 - Network Mode with WebSocket ImplementationOverviewThis PR introduces WebSocket and HTTP network communication as an alternative to ADB, adding significant new functionality including gesture control, reverse connections, and improved API response handling. Overall, the implementation is solid with good test coverage for new components. ✅ StrengthsCode Quality
Design Decisions
🔴 Critical Issues1. SECURITY: Hardcoded User ID (ConfigManager.kt:80, ReverseConnectionService.kt:80)headers["X-User-ID"] = "7785b089-b9aa-458d-a32e-baec315e5e16"Impact: Critical security vulnerability 2. Memory Leak Risk (ReverseConnectionService.kt:32)private val handler = Handler(Looper.getMainLooper())Impact: Handler holds implicit reference to Service
3. ConfigManager State Management (ConfigManager.kt:150)var reverseConnectionEnabled: Boolean = falseImpact: State inconsistency bug 4. Screenshot Double Encoding (ApiHandler.kt:227-229)// Result is Base64 string from Service.
// decode it back to bytes to pass as Binary response.
val bytes = android.util.Base64.decode(result, android.util.Base64.DEFAULT)Impact: Performance inefficiency
|
Pull Request Review: DRO-708 - Network Mode with WebSocketOverviewThis PR introduces significant new functionality for WebSocket communication, reverse connections, and gesture-based actions. The changes are well-structured with good separation of concerns. ✅ Strengths1. Architecture & Design
2. Test Coverage
3. Code Quality
|
🔴 Critical Issues1. Security: Hardcoded Credentials (ConfigManager.kt:223)val userID: String
get() {
return "7785b089-b9aa-458d-a32e-baec315e5e16"
}Issue: Hardcoded user ID is a security vulnerability. 2. Security: WebSocket Authentication (PortalWebSocketServer.kt:58)Issues:
3. Thread Safety: Service Lifecycle (ReverseConnectionService.kt:153)if (!::actionDispatcher.isInitialized) {
val service = DroidrunAccessibilityService.getInstance()
actionDispatcher = service.getActionDispatcher()
}Issue: Not thread-safe. Multiple concurrent messages could cause race conditions. |
|
💡 Medium Priority Suggestions8. Performance: Base64 Double Encoding (ApiHandler.kt:224-228)Good job documenting the technical debt! Consider filing an issue to track refactoring screenshot handling to avoid encode/decode cycle. 9. Code Duplication: ApiResponse.toJson() (ApiResponse.kt:25-74)Two toJson() methods with nearly identical logic. Consider refactoring with optional parameter: fun toJson(id: Int? = null): String = when (this) {
is Success -> JSONObject().apply {
id?.let { put("id", it) }
put("status", "success")
put("result", data)
}.toString()
// ... rest of cases
}10. Magic Numbers (GestureController.kt:13-16)Constants are well-named, but add comments explaining WHY these values were chosen (e.g., "50ms tap duration matches Android system behavior"). 11. Inconsistent Error Messages (PortalWebSocketServer.kt:97-101)UUID size warning is logged but no error response sent to client. Consider rejecting invalid requests. |
🐛 Minor Issues12. Typo (PortalWebSocketServer.kt:28)
13. Inconsistent Naming (ActionDispatcher.kt:48-55)Variable named 14. Removed Test (ExampleUnitTest.kt)Example test deleted (17 lines). Fine for cleanup, but verify it didn't contain useful tests. 15. ViewBinding Enabled (build.gradle.kts:20-21)✅ Good: Modern Android practice. Ensure all activities/fragments use ViewBinding consistently. 📋 Testing RecommendationsMissing Test Coverage:
Improvements:
|
🔒 Security Summary
🎯 Performance Considerations
✨ Overall AssessmentQuality Score: 7.5/10 Pros:
Cons:
Recommendation - Nearly ready for merge with these fixes:
Great work overall! The architecture is solid and the new functionality is well-implemented. 🚀 |
Pull Request Review: DRO-708 - Network Mode with WebSocketOverviewThis is a significant feature addition that implements network mode with WebSocket support, reverse connection capabilities, gesture control, and binary screenshot handling. The PR adds ~1,941 lines across 29 files with good architectural patterns. Positive HighlightsArchitecture & Design
Code Quality
Testing
Issues & Concerns1. Security VulnerabilitiesCritical: Hardcoded User CredentialsFile: val userID: String
get() {
return "7785b089-b9aa-458d-a32e-baec315e5e16"
}This is a critical security issue. A hardcoded UUID means all devices share the same user ID, which:
Recommendation: Remove hardcoded value and implement proper user ID generation/storage or external configuration. Weak Authentication Token GenerationFile: val authToken: String
get() {
var token = sharedPrefs.getString(KEY_AUTH_TOKEN, null)
if (token == null) {
token = java.util.UUID.randomUUID().toString()
sharedPrefs.edit { putString(KEY_AUTH_TOKEN, token) }
}
return token
}Issues:
Recommendation: Use Incomplete Authorization in ReverseConnectionServiceFile: The 2. Resource Leaks & Memory IssuesMissing WebSocket CleanupFile: private fun disconnect() {
try {
webSocketClient?.close()
webSocketClient = null
} catch (e: Exception) {
Log.e(TAG, "Error closing connection", e)
}
}The reconnect handler in Recommendation: Add Potential Handler LeakFile: private val handler = Handler(Looper.getMainLooper())Using main looper for network operations can cause UI jank. Consider using a background HandlerThread. 3. Data Handling IssuesInefficient Screenshot Encoding/DecodingFile: // Result is Base64 string from Service.
// decode it back to bytes to pass as Binary response.
// In future, Service should return bytes directly to avoid this encode/decode cycle.
val bytes = android.util.Base64.decode(result, android.util.Base64.DEFAULT)
ApiResponse.Binary(bytes)This double-encode/decode cycle wastes CPU and memory. The TODO comment acknowledges this but it should be fixed now. Recommendation: Refactor Missing Input ValidationFile: "tap" -> {
val x = params.optInt("x", 0)
val y = params.optInt("y", 0)
apiHandler.performTap(x, y)
}No validation that coordinates are within screen bounds. Malicious or buggy clients could send negative values or values exceeding screen dimensions. Recommendation: Add coordinate validation against actual screen dimensions. 4. Error Handling & LoggingSilent Failures in Package EnumerationFile: for (resolveInfo in resolvedApps) {
try {
// ... package processing
} catch (e: Exception) {
Log.w("ApiHandler", "Skipping package ${resolveInfo.activityInfo.packageName}: ${e.message}")
}
}Individual package failures are caught and logged but not reported to the client. The client has no way to know some packages were skipped. Recommendation: Consider collecting errors and including them in the response metadata. Incomplete Error ContextFile: } catch (e: Exception) {
Log.e(TAG, "Error processing message", e)
}Errors are logged but no error response is sent back to the server. The server will timeout waiting for a response. Recommendation: Send error responses even when exceptions occur. 5. Code Smells & MaintainabilityInconsistent API Response FormatThe PR mixes different response formats:
While this works, the serialization in Recommendation: Standardize on a single response wrapper format. ConfigManager Initialization Side EffectFile: init {
if (sharedPrefs.contains(KEY_REVERSE_CONNECTION_ENABLED)) {
sharedPrefs.edit { putBoolean(KEY_REVERSE_CONNECTION_ENABLED, false) }
}
}The init block unconditionally resets reverse connection to disabled. This seems like a workaround for a bug rather than intentional behavior. Why force-disable on every app launch? Dead Code / TODOsSeveral TODOs and comments indicate incomplete work:
6. Testing GapsWhile the PR adds good unit tests, there are gaps:
7. Android-Specific IssuesMissing Permission ChecksThe reverse connection service uses network and the gesture controller requires accessibility permissions, but there's no runtime validation that these permissions are granted before attempting operations. Potential Battery DrainFile: override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (!isServiceRunning.getAndSet(true)) {
Log.i(TAG, "Starting Reverse Connection Service")
connectToHost()
}
return START_STICKY
}
Performance ConsiderationsPositive
Concerns
Recommendations SummaryMust Fix (High Priority)
Should Fix (Medium Priority)
Nice to Have (Low Priority)
ConclusionThis is a substantial and well-architected feature that demonstrates good Kotlin practices and clean separation of concerns. The core implementation is solid, but there are critical security issues (hardcoded credentials) that must be addressed before merging. The testing is good but incomplete, and there are several opportunities for performance and maintainability improvements. Overall, this represents significant progress on the network mode functionality, but requires security fixes and additional polish before it's production-ready. Recommendation: Request changes to address security issues, then approve after fixes. |
PR Review: DRO-708 - Network Mode with WebSocketOverviewThis PR implements network communication (WebSocket & HTTP) to replace ADB dependency. This is a significant and well-structured improvement. ✅ Strengths
🔴 Critical Issues (Must Fix Before Merge)1. Hardcoded User ID (ConfigManager.kt:241-243)All devices share same UUID - serious security issue 2. Hardcoded Country Code (ConfigManager.kt:237-240)All devices report Germany 3. Missing API Check (ApiHandler.kt:85)longVersionCode needs API 28+ but check removed 4. Misleading Log (ReverseConnectionService.kt:116)Says 'connection established' but connect() is async
|
Pull Request Review: DRO-708 Network Mode ImplementationOverviewThis PR implements a significant architectural enhancement by adding WebSocket and HTTP network communication modes, eliminating the dependency on ADB for device control. The changes introduce 1961 additions across 29 files with new gesture control, action dispatching, and reverse connection capabilities. ✅ StrengthsArchitecture & Design
Code Quality
Testing
🔴 Critical Issues1. Security: Hardcoded User Credentials
|
|
@claude break this PR down, what it does and what it adds. read all relevant files, dig into it and prepare to discuss the ins and outs of it |
|
Claude finished @johnmalek312's task in 3m 19s —— View job PR #33 Comprehensive Breakdown: Network Mode with WebSocket ImplementationI've done a deep dive into this PR. Here's everything you need to know about what it does, how it works, and the architecture behind it. 🎯 What This PR DoesThis PR implements network-based device control as an alternative to ADB (Android Debug Bridge). It adds three primary communication modes:
The Big PictureBefore this PR, the app relied on ADB for remote control. This PR eliminates that dependency by implementing native network protocols, allowing the Android device to be controlled over WiFi/network without USB debugging. 🏗️ Architecture OverviewThe implementation follows a clean layered architecture: 📦 New Components Added1. ActionDispatcher.kt (109 lines) ⭐ Core InnovationLocation: Purpose: Central command router that both HTTP and WebSocket servers use to avoid code duplication. How it works:
Supported Actions: - tap(x, y) // Touch at coordinates
- swipe(startX, startY, ...) // Swipe gesture
- global(action) // System actions (home, back, recents)
- app(package, activity) // Launch apps
- keyboard/input(text) // Type text
- keyboard/clear() // Clear text
- keyboard/key(keyCode) // Press key
- screenshot(hideOverlay) // Capture screen
- packages() // List installed apps
- state(filter) // Get UI state
- version() // App version
- time() // TimestampKey Design Decision: The dispatcher normalizes both REST-style paths ( 2. GestureController.kt (87 lines) ⭐ Gesture EngineLocation: Purpose: Singleton object that executes touch gestures and global actions via Android's AccessibilityService. Why it exists: Eliminates the need for ADB shell input commands by using native Android gesture APIs. Key Methods: fun tap(x: Int, y: Int): Boolean
- Creates Path at coordinates
- 50ms duration tap
- Dispatches via AccessibilityService.dispatchGesture()
fun swipe(startX, startY, endX, endY, durationMs): Boolean
- Creates Path from start to end
- Duration clamped between 10ms - 5000ms
- Default 300ms if not specified
fun performGlobalAction(action: Int): Boolean
- GLOBAL_ACTION_HOME, BACK, RECENTS, etc.
- Direct AccessibilityService callTechnical Detail: Uses 3. ReverseConnectionService.kt (192 lines) ⭐ Cloud IntegrationLocation: Purpose: Android Service that maintains persistent WebSocket connection to a remote server (cloud/host). Use Case: Allows devices behind NAT/firewall to be controlled remotely by connecting outbound to a cloud server. How it works:
Headers Sent:
4. PortalWebSocketServer.kt (183 lines - modified significantly)Location: Purpose: WebSocket server running on the Android device (default port 8081). Changes in this PR:
Authentication Flow: onWebsocketHandshakeReceivedAsServer():
1. Check Authorization header for "Bearer <token>"
2. Fallback: check query param "?token=<token>"
3. Compare with configManager.authToken
4. Reject with 401 if invalid/missingMessage Handling: onMessage(String):
1. Parse JSON: {"id": "uuid", "method": "screenshot", "params": {}}
2. Dispatch via actionDispatcher.dispatch()
3. If Binary response → send [UUID bytes][data bytes]
4. If Text response → send {"id": "uuid", "status": "success", "result": ...}Binary Protocol (for screenshots): The client extracts the first 36 bytes as the request ID, then treats the rest as PNG image data. 5. Enhanced ApiHandler.kt (+164 lines, -45 lines modified)Location: New Methods Added: performTap(x, y) → calls GestureController.tap()
performSwipe(...) → calls GestureController.swipe()
performGlobalAction(action) → calls GestureController.performGlobalAction()
startApp(packageName, activityName?) → creates Intent and launches
getPackages() → enumerates all launchable apps with metadata
getTime() → returns System.currentTimeMillis()getPackages() Deep Dive: Returns JSON array:
[
{
"packageName": "com.example.app",
"label": "My App",
"versionName": "1.2.3",
"versionCode": 123,
"isSystemApp": false
},
...
]Error Handling: Individual package failures are caught and logged but not reported to the client (line 96-138). This prevents one broken app from failing the entire enumeration.
6. Enhanced ConfigManager.kt (+145 lines, -38 lines modified)Location: New Configuration Properties: // WebSocket Server
var websocketEnabled: Boolean (default: true)
var websocketPort: Int (default: 8081)
// Reverse Connection
var reverseConnectionUrl: String
var reverseConnectionToken: String
var reverseConnectionEnabled: Boolean (runtime only, not persisted)
// Security
val authToken: String (auto-generated UUID, persisted)
val deviceID: String (auto-generated UUID, persisted)
// Device Info
val deviceName: String (manufacturer + model, e.g., "Samsung Galaxy S21")
val deviceCountryCode: String (hardcoded "de" - TODO)
val userID: String (hardcoded test UUID - ⚠️ SECURITY ISSUE)
// Event Toggles
fun isEventEnabled(type: EventType): Boolean
fun setEventEnabled(type: EventType, enabled: Boolean)Listener Pattern: ConfigManager notifies listeners when settings change: interface ConfigChangeListener {
onOverlayVisibilityChanged(visible: Boolean)
onOverlayOffsetChanged(offset: Int)
onSocketServerEnabledChanged(enabled: Boolean)
onSocketServerPortChanged(port: Int)
onWebSocketEnabledChanged(enabled: Boolean)
onWebSocketPortChanged(port: Int)
}DroidrunAccessibilityService implements this interface to restart servers when ports change. 7. Enhanced SocketServer.kt (+124 lines, -43 lines modified)Location: Changes:
HTTP Request Flow: Authentication: Auth header check (line 132-135):
Authorization: Bearer <token>
Validation (line 140):
if (path != "/ping" && authToken != configManager.authToken) {
return 401 Unauthorized
}
8. Enhanced ApiResponse.kt (+47 lines, -4 lines modified)Location: New Response Types: sealed class ApiResponse {
data class Success(val data: Any) // Existing
data class Error(val message: String) // Existing
data class RawObject(val json: JSONObject) // ✨ NEW
data class RawArray(val json: JSONArray) // ✨ NEW
data class Binary(val data: ByteArray) // ✨ NEW
data class Text(val data: String) // ✨ NEW
}Why these were added:
toJson() Method: 🧪 TestingNew Test Files:
Test Coverage: ✅ Good coverage for new core logic, ❌ Missing tests for GestureController, ReverseConnectionService, and UI components. 🔧 UI Changes (MainActivity & SettingsBottomSheet)MainActivity.kt (+209 lines, -215 lines modified)New Features:
SettingsBottomSheet.kt (+91 lines, -20 lines modified)New Settings:
All settings persist to SharedPreferences via ConfigManager. 📊 Key Statistics🔐 Security Considerations✅ Good Security Practices:
|
|
@claude SocketServer? does the phone act as a client or server? does it require open ports? |
|
Claude finished @johnmalek312's task in 1m 6s —— View job SocketServer Architecture ExplanationGreat question! Let me clarify the networking architecture in this PR. There are actually three different networking modes, not just one: 🏗️ Three Networking Modes1. SocketServer (HTTP) - Phone as Server 🔴File: ServerSocket(port) // Line 55 - Creates TCP server socket
serverSocket?.accept() // Line 94 - Accepts incoming connectionsHow it works:
Port Requirements:
2. PortalWebSocketServer - Phone as Server 🔴File: class PortalWebSocketServer(port: Int, ...) : WebSocketServer(InetSocketAddress(port))
// Line 19 - Extends WebSocketServer, making phone a serverHow it works:
Port Requirements:
3. ReverseConnectionService - Phone as Client 🔵File: webSocketClient = object : WebSocketClient(uri, headers) { ... } // Line 94
webSocketClient?.connect() // Line 114 - Initiates OUTBOUND connectionHow it works:
Connection Flow: Headers Sent (Line 61-76): Authorization: Bearer <token>
X-User-ID: <user_id>
X-Device-ID: <device_id>
X-Device-Name: Samsung Galaxy S21
X-Device-Country: deAuto-Reconnection:
📊 Comparison Table
🔒 Port Security ConsiderationsWhen Ports ARE Open (Modes 1 & 2):
When Ports Are NOT Open (Mode 3):
🎯 Which Mode to Use?Use SocketServer/PortalWebSocketServer (phone as server) when:
Use ReverseConnectionService (phone as client) when:
💡 Key InsightThe "SocketServer" name is accurate - the phone does act as a server for that component. However, the PR adds both server and client modes, giving you flexibility:
The Let me know if you need clarification on any of these modes! 🚀 |
No description provided.