-
Notifications
You must be signed in to change notification settings - Fork 2
Add Bluetooth support for WearOS devices #1
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
base: master
Are you sure you want to change the base?
Conversation
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is being reviewed by Cursor Bugbot
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
cbe08fb to
ef58386
Compare
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Race condition with unsynchronized activeConnections map access
The new ConnectionThread synchronizes on activeConnections when checking for existing connections, but the onConnectReceived method (called via the onMessage callback at line 711) modifies activeConnections without synchronization. Since HashMap is not thread-safe, concurrent access from the Bluetooth connection thread and the message callback thread can cause ConcurrentModificationException or data corruption.
Additional Locations (1)
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
| @Override | ||
| public void onDisconnected() { | ||
| // Cleanup handled by existing logic | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Missing disconnect handler leaves Bluetooth connections orphaned
The onDisconnected callback is empty with only a comment claiming "Cleanup handled by existing logic." However, the TCP equivalent in MessageHandler.onDisconnected() explicitly calls wearable.onDisconnectReceived() to remove the connection from activeConnections and update state. Without this call, disconnected Bluetooth connections remain in activeConnections, preventing reconnection attempts and causing stale state.
| byte[] bytes = piece.toByteArray(); | ||
| os.writeInt(bytes.length); | ||
| os.write(bytes); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Missing flush after writing to DataOutputStream
The writeMessagePiece method writes to a DataOutputStream but never calls flush(). Data written to DataOutputStream may be buffered internally, so without an explicit flush, messages may not be sent immediately over the Bluetooth socket. This can cause delayed or missing protocol messages, particularly problematic for the initial handshake.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Null pointer exception when closing Bluetooth connections
The closeConnection method accesses sct.getWearableConnection() without a null check on sct. The sct variable is only initialized when enableConnection("server") is called. With the new Bluetooth implementation, connections can be created and closed via onConnectReceived and closeConnection without sct ever being set, causing a NullPointerException when closing any Bluetooth-based connection.
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java#L597-L598
GmsCore/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Lines 597 to 598 in ef58386
| } | |
| if (connection == sct.getWearableConnection()) { |
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableService.java
Show resolved
Hide resolved
33d5ff3 to
7b4047c
Compare
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableSettingsActivity.java
Show resolved
Hide resolved
76640d3 to
ece85ec
Compare
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
| super("GmsWearSvc", GmsService.WEAR); | ||
| } | ||
|
|
||
| public static WearableImpl impl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Static impl field creates thread-safety issues
The static impl field in WearableService is accessed by WearableSettingsActivity without synchronization. Since the service's onCreate and onDestroy can be called on different threads than UI access from the activity, this creates a race condition. The activity could read a non-null reference that becomes null immediately after, or use a stale WearableImpl instance that has been stopped.
Additional Locations (1)
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Outdated
Show resolved
Hide resolved
c57d56c to
ff4c1a9
Compare
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
7851adb to
1c4933f
Compare
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
a19d816 to
882fa2f
Compare
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableSettingsActivity.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableSettingsActivity.java
Outdated
Show resolved
Hide resolved
play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableSettingsActivity.java
Outdated
Show resolved
Hide resolved
34048e6 to
9149be9
Compare
f35a68b to
70caf33
Compare
Added android.permission.BLUETOOTH_CONNECT to AndroidManifest.xml as required by Android 12+ for BluetoothAdapter.getBondedDevices() API call. Fixes lint error identified by ale5000-git in code review.
…GNESH123/GmsCore into wearos-bluetooth-support # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
Added explicit permission checks before calling Bluetooth APIs that require BLUETOOTH_CONNECT permission on Android 12+: - WearableImpl.java: Check permission before getBondedDevices() - WearableSettingsActivity.java: Check permission before getBondedDevices() - Added SuppressLint for device.getName() since permission already checked Fixes all MissingPermission lint errors. Build verified with lintDebug.
|
adopted Wearable lib to Wire 4.9.9, but i unable to test it, cuz i dont have compatable device |
|
Thanks @deadYokai for the Wire 4.9.9 update! 🙏 I really appreciate you taking the time to adapt the Wearable library. This will be important for keeping everything compatible with the latest protobuf changes. I don't have a physical WearOS device for testing either at the moment. Would you like me to integrate your changes from your 'wire_upd' branch into this PR? Or would you prefer to submit them separately? Happy to collaborate on getting this working together! |
|
@SBALAVIGNESH123 i already submited PR to microg/Wearable#3 , i'm waiting for approving You can intergrate changes, but as far as PR not merged, can't be compatable with current version of GmsCore / Wearable And also, i'm trying to implement BluetoothGatt protocol, as far i know it's used by WearOS 3+ devices |
|
found UUID in google's gms maybe i make own implementation of bluetooth stuff? |
|
Hey @deadYokai, yes that would be great! |
Also fix some warnings in NetworkLocationService Incorporates work from microg#3210
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on February 19
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| if (SDK_INT >= 31 && connectionInfo != null) { | ||
| onWifiDetailsAvailable(listOf(connectionInfo.toWifiDetails())) | ||
| if (SDK_INT >= 31 && connectionInfo != null && connectionInfo.toWifiDetails() != null) { | ||
| onWifiDetailsAvailable(listOfNotNull(connectionInfo.toWifiDetails())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double call to nullable function creates race condition
The toWifiDetails() function is called twice on the same connectionInfo object - once in the condition check and again in the body. Since WifiInfo.toWifiDetails() can return null when bssid is null, and WiFi state can change between calls, there's a time-of-check to time-of-use issue. If the bssid becomes null between the two calls, the second call returns null, resulting in listOfNotNull producing an empty list and onWifiDetailsAvailable silently doing nothing. The result should be captured in a variable to ensure atomic evaluation.
| .name("Phone") | ||
| .networkId(localId) | ||
| .peerAndroidId(localId) | ||
| .peerAndroidId(0L) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bluetooth handshake sends invalid Android ID
The manual Connect message sent during Bluetooth handshake uses peerAndroidId(0L) instead of the actual device Android ID. This contradicts how MessageHandler is configured, which retrieves the real Android ID from device settings via SettingsContract.CheckIn.ANDROID_ID. Sending a hardcoded zero for the Android ID could cause WearOS devices to reject the connection or fail to properly identify the phone during pairing. The handshake should use the same Android ID that MessageHandler uses for consistency.
Co-authored-by: Marvin W <git@larma.de>
…og#3222) Co-authored-by: Marvin W <git@larma.de>
Co-authored-by: Marvin W <git@larma.de>
Co-authored-by: Marvin W <git@larma.de>
Fixes bug introduced with microg#3222
|
Is this buildable yet? If it is I'll try it out! I have a GW5 I've been wanting to use. |
| performSignOut(context, clientPackageName, it, signInAccount) | ||
| } | ||
| } | ||
| AccountUtils.get(context).removeSelectedAccount(clientPackageName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sign-out callback invoked before async operations complete
High Severity
The signOut method calls callback.onResult(Status.SUCCESS) at line 147 immediately after launching the coroutine, rather than inside it. This means the callback signals success before the actual sign-out operations (performSignOut, removeSelectedAccount) have completed. Comparing with AuthSignInService.kt which correctly places the callback inside the lifecycleScope.launchWhenStarted block, this callback should be moved inside the lifecycleScope.launch block after the sign-out operations finish. Calling apps will incorrectly believe sign-out is complete when it hasn't actually happened yet.
🔬 Verification Test
Why verification test was not possible: This is an Android service implementation that requires the full Android runtime environment, lifecycle management, and IPC mechanisms to test. The bug is clearly visible from static code analysis - comparing line 147 (callback outside the coroutine) with the similar implementation in AuthSignInService.kt lines 134 (callback inside the coroutine).
| Log.d(TAG, "updateProjectionState: useFastMode: $useFast") | ||
|
|
||
| visibleRegion = projection.visibleRegion | ||
| withoutTiltOrBearing = useFast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Projection field never updated causing stale fallback data
Medium Severity
The updateProjectionState method accepts a projection parameter that shadows the class field this.projection. While the method correctly uses the parameter to update cached values like visibleRegion, farLeft, etc., it never updates this.projection itself. The previous implementation included projection = newProjection to update the class field. When the fallback paths are taken in fromScreenLocation (lines 74, 87) and toScreenLocation (lines 100, 113)—which happens when isInvalid() returns true or withoutTiltOrBearing is false—the code uses the stale this.projection from initialization rather than the current projection state, leading to incorrect map coordinates.
🔬 Verification Test
Why verification test was not possible: This requires the Huawei HMS Maps SDK runtime environment and actual map interactions to test. The bug is evident from static analysis: the old code updated this.projection = newProjection (visible in the diff's removed line), but the new code removes this assignment while the fallback paths at lines 74, 87, 100, and 113 still reference this.projection.
| if (data.isNullOrBlank()) return null | ||
| private fun getAuthOptions(packageName: String): Set<String>? { | ||
| val data = preferences.getStringSet(DEFAULT_SIGN_IN_OPTIONS_PREFIX + getPackageNameSuffix(packageName), null) | ||
| if (data.isNullOrEmpty()) return null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SharedPreferences type mismatch causes ClassCastException on upgrade
High Severity
The getAuthOptions method calls preferences.getStringSet() directly on a key that was previously stored as a String by the old setDefaultSignInInfo method (using putString). When Android's SharedPreferencesImpl loads a String value and the code attempts to read it as a StringSet, it throws a ClassCastException because the internal map contains a String object that cannot be cast to Set<String>. While setAuthInfo uses runCatching to safely attempt reading old String data during migration, getAuthOptions lacks this protection. Users upgrading from older versions who have existing sign-in options stored will crash when calling sign-out or revoke-access operations that invoke getAuthOptions before any new sign-in triggers the migration path in setAuthInfo.
🔬 Verification Test
Why verification test was not possible: This requires testing against a real Android SharedPreferences implementation with pre-existing String data from an older app version. The bug is evident from comparing the old code (which used putString/getString for options) with the new code (which uses getStringSet without type-safe reading). Android's SharedPreferencesImpl.getStringSet performs an unchecked cast (Set<String>) mMap.get(key) that will fail if the stored value is a String.
This pull request adds full Bluetooth transport support for WearOS devices in MicroG, enabling basic wearable functionality without relying on Google Play Services. It introduces a new BluetoothWearableConnection class that implements a Bluetooth Classic RFCOMM transport using the standard SPP UUID and handles length-prefixed protobuf messages. The WearableImpl has been updated with a background ConnectionThread that automatically scans bonded devices at startup, attempts to connect using the new Bluetooth transport, and includes a 10-second reconnection loop while preserving existing TCP/IP handling. The Android manifest now includes the required Bluetooth permissions, including the newer Android 12+ BLUETOOTH_SCAN and BLUETOOTH_CONNECT. In testing, the connection successfully paired with a Pixel Watch and logged stable Bluetooth sessions, working alongside the TCP mechanism. Current limitations include broad connection attempts across all bonded devices and reliance on the general SPP UUID, though this foundation can be expanded with device-specific UUID detection, improved state management, and UI integration. This work closes issue microg#2843, relates to the long-standing issue microg#4, and fulfills the bounty request for foundational WearOS functionality such as notification sync, app communication, and media control within the existing MicroG wearable framework.
Note
BluetoothWearableConnection(RFCOMM SPP) and connection thread inWearableImpl; manifest gainsBLUETOOTH_CONNECT; settings UI lists bonded devices; robust deserialization and permission checks added.IAuthorizationServiceaddsrevokeAccess/clearToken; implementation persists per-app account via newAccountUtils, returns access/id tokens appropriately, and supports token invalidation.UnregisterReceiverwith trusted receiver +PackageIntentOpWorker(WorkManager) to clear GCM/auth data on package changes; GCM broadcast action renamed toCONNECTEDand flow adjusted; UI triggers updated.PLAY_INTEGRITY_APP_LISTsetting,PlayIntegrityDatamodel, and UI in SafetyNet (renamed to Device Attestation) to view/toggle per-app access; vending-side services track/update visit status; integrity/express services enforce enablement and per-app allow list.checkoutv6; add WorkManager (work-runtime-ktx) dependency and version; minor string/localization updates.Written by Cursor Bugbot for commit 7f8a5d0. This will update automatically on new commits. Configure here.