Skip to content

Commit a0b1093

Browse files
committed
Fix permissions
1 parent 9a8bf7f commit a0b1093

File tree

9 files changed

+85
-28
lines changed

9 files changed

+85
-28
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,7 @@ capacitor.config.json
4747

4848
# Ignore Gradle build output directory
4949
build
50+
51+
# Ignore capcitor android build output directory
52+
capacitor-plugin-betaflight-serial/android/bin/
53+

android/app/src/main/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@
4141
android:name="android.support.FILE_PROVIDER_PATHS"
4242
android:resource="@xml/file_paths"></meta-data>
4343
</provider>
44+
45+
<!-- Explicit receiver to handle USB permission PendingIntent on Android 14+ -->
46+
<receiver
47+
android:name="com.betaflight.plugin.serial.UsbPermissionReceiver"
48+
android:exported="true">
49+
<intent-filter>
50+
<action android:name="com.betaflight.USB_PERMISSION" />
51+
</intent-filter>
52+
</receiver>
4453
</application>
4554

4655
<!-- Permissions -->

android/capacitor.settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ include ':capacitor-android'
33
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
44

55
include ':capacitor-plugin-betaflight-serial'
6-
project(':capacitor-plugin-betaflight-serial').projectDir = new File('../node_modules/capacitor-plugin-betaflight-serial/android')
6+
project(':capacitor-plugin-betaflight-serial').projectDir = new File('../capacitor-plugin-betaflight-serial/android')

capacitor-plugin-betaflight-serial/android/src/main/java/com/betaflight/plugin/serial/BetaflightSerialPlugin.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
}
5050
)
5151
public class BetaflightSerialPlugin extends Plugin implements SerialInputOutputManager.Listener {
52+
// Hold a static reference for forwarding permission callbacks from an explicit BroadcastReceiver
53+
private static java.lang.ref.WeakReference<BetaflightSerialPlugin> sInstance = new java.lang.ref.WeakReference<>(null);
5254
private static final String TAG = "BetaflightSerial";
5355
private static final String ACTION_USB_PERMISSION = "com.betaflight.USB_PERMISSION";
5456
private static final int WRITE_WAIT_MILLIS = 2000;
@@ -81,6 +83,7 @@ public void onReceive(Context context, Intent intent) {
8183
@Override
8284
public void load() {
8385
super.load();
86+
sInstance = new java.lang.ref.WeakReference<>(this);
8487
usbManager = (UsbManager) getContext().getSystemService(Context.USB_SERVICE);
8588

8689
// Register USB broadcast receivers
@@ -136,13 +139,32 @@ public void requestPermission(PluginCall call) {
136139
String deviceKey = getDeviceKey(device);
137140
permissionRequestedDevices.put(deviceKey, device);
138141

142+
// Create fully explicit broadcast intent with component
143+
Intent permissionAction = new Intent(ACTION_USB_PERMISSION);
144+
permissionAction.setComponent(new android.content.ComponentName(
145+
getContext(),
146+
UsbPermissionReceiver.class
147+
));
148+
permissionAction.putExtra(UsbManager.EXTRA_DEVICE, device);
149+
150+
int requestCode = device.getDeviceId();
151+
int flags;
152+
153+
if (Build.VERSION.SDK_INT >= 34) { // Android 14+ (U / API 34)
154+
// Android 14+ requires IMMUTABLE for explicit intents
155+
flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
156+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
157+
// Android 12-13 requires MUTABLE for UsbManager
158+
flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
159+
} else {
160+
flags = PendingIntent.FLAG_UPDATE_CURRENT;
161+
}
162+
139163
PendingIntent permissionIntent = PendingIntent.getBroadcast(
140164
getContext(),
141-
0,
142-
new Intent(ACTION_USB_PERMISSION),
143-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
144-
? PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
145-
: PendingIntent.FLAG_UPDATE_CURRENT
165+
requestCode,
166+
permissionAction,
167+
flags
146168
);
147169

148170
usbManager.requestPermission(device, permissionIntent);
@@ -356,7 +378,7 @@ public void onRunError(Exception e) {
356378

357379
// ===== Private helper methods =====
358380

359-
private void handlePermissionResult(Intent intent) {
381+
public void handlePermissionResult(Intent intent) {
360382
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
361383
if (device == null) return;
362384

@@ -374,6 +396,14 @@ private void handlePermissionResult(Intent intent) {
374396
}
375397
}
376398

399+
// Static entry point for the explicit BroadcastReceiver to forward the permission result
400+
public static void onUsbPermissionResult(Context context, Intent intent) {
401+
BetaflightSerialPlugin instance = sInstance.get();
402+
if (instance != null) {
403+
instance.handlePermissionResult(intent);
404+
}
405+
}
406+
377407
private void handleDeviceAttached(Intent intent) {
378408
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
379409
if (device == null) return;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.betaflight.plugin.serial;
2+
3+
import android.content.BroadcastReceiver;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
import android.util.Log;
7+
8+
/**
9+
* Explicit BroadcastReceiver to receive USB permission results on Android 14+.
10+
* Forwards the Intent to the active BetaflightSerialPlugin instance.
11+
*/
12+
public class UsbPermissionReceiver extends BroadcastReceiver {
13+
private static final String TAG = "BetaflightSerial";
14+
15+
@Override
16+
public void onReceive(Context context, Intent intent) {
17+
Log.d(TAG, "UsbPermissionReceiver.onReceive called with action: " + intent.getAction());
18+
BetaflightSerialPlugin.onUsbPermissionResult(context, intent);
19+
}
20+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"@capacitor/core": "^7.0.1",
4646
"@fortawesome/fontawesome-free": "^6.5.2",
4747
"@vitejs/plugin-vue": "^6.0.1",
48-
"capacitor-plugin-betaflight-serial": "file:./capacitor-plugin-betaflight-serial",
48+
"capacitor-plugin-betaflight-serial": "link:./capacitor-plugin-betaflight-serial",
4949
"crypto-es": "^2.1.0",
5050
"d3": "^7.9.0",
5151
"djv": "^2.1.4",

src/js/protocols/CapacitorSerial.js

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ class CapacitorSerial extends EventTarget {
144144

145145
console.log(`${logHead} Permission granted for ${this.ports.length} devices`);
146146

147+
// Dispatch addedDevice event for each new port to update UI
148+
for (const port of this.ports) {
149+
this.dispatchEvent(new CustomEvent("addedDevice", { detail: port }));
150+
}
151+
147152
// Return the first device if available
148153
return this.ports.length > 0 ? this.ports[0] : null;
149154
} catch (error) {
@@ -267,24 +272,7 @@ class CapacitorSerial extends EventTarget {
267272
return { bytesSent: 0 };
268273
}
269274

270-
// Normalize data to Uint8Array (MSP passes Uint8Array or ArrayBuffer)
271-
if (data instanceof ArrayBuffer) {
272-
data = new Uint8Array(data);
273-
} else if (ArrayBuffer.isView(data)) {
274-
// Accept DataView or other typed views; re-wrap to Uint8Array slice
275-
data = new Uint8Array(data.buffer, data.byteOffset || 0, data.byteLength);
276-
} else if (Array.isArray(data)) {
277-
data = new Uint8Array(data);
278-
}
279-
280-
// Handle empty or invalid data
281-
if (!data || typeof data.length !== "number" || data.length === 0) {
282-
console.log(`${logHead} Empty data, skipping send (data:`, data, `length:`, data?.length, `)`);
283-
if (callback) {
284-
callback({ bytesSent: 0 });
285-
}
286-
return { bytesSent: 0 };
287-
}
275+
data = new Uint8Array(data);
288276

289277
try {
290278
// Convert Uint8Array to hex string

vite.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ export default defineConfig({
115115
alias: {
116116
"/src": path.resolve(process.cwd(), "src"),
117117
vue: path.resolve(__dirname, "node_modules/vue/dist/vue.esm-bundler.js"),
118+
// Ensure local Capacitor plugin resolves to its ESM entry without relying on package exports
119+
"capacitor-plugin-betaflight-serial": path.resolve(
120+
__dirname,
121+
"capacitor-plugin-betaflight-serial/dist/esm/index.js",
122+
),
118123
},
119124
},
120125
server: {

yarn.lock

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3203,8 +3203,9 @@ caniuse-lite@^1.0.30001737:
32033203
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz#67fb92953edc536442f3c9da74320774aa523143"
32043204
integrity sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==
32053205

3206-
"capacitor-plugin-betaflight-serial@file:./capacitor-plugin-betaflight-serial":
3207-
version "1.0.0"
3206+
"capacitor-plugin-betaflight-serial@link:./capacitor-plugin-betaflight-serial":
3207+
version "0.0.0"
3208+
uid ""
32083209

32093210
caseless@~0.12.0:
32103211
version "0.12.0"

0 commit comments

Comments
 (0)