feat(bridge,android): BridgeEvent::PermissionState + JNI publish hook + c++_shared link
Per SDD-106 §5 add BridgeEvent::PermissionState{permission, state}
with the PermissionStateKind enum (Granted, Denied, PermanentlyDenied,
Unknown). The Kotlin side publishes mid-session permission changes
through a new JNI entry point Java_app_chanora_chanora_1flutter
_MainActivity_publishPermissionState routed by the new
permission_jni.rs module; the Rust audio engine subscribes and
authoritatively clamps the transmit gate (see SDD-106 §6).
Adds crates/chanora_bridge/build.rs to emit
cargo:rustc-link-lib=dylib=c++_shared on Android so libchanora_bridge
.so carries DT_NEEDED libc++_shared.so; this is required by Android
API 24+ per-library linker namespaces to resolve __cxa_pure_virtual
and friends at System.loadLibrary time.
Includes the FRB-regenerated Dart counterparts so each commit is
independently buildable.
Trace: SDD-105, SDD-106 §5, SDD-118 item 6 (extended).
This commit is contained in:
@@ -9,8 +9,9 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||
part 'api.freezed.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `log_file_path`, `log_sink`, `open_log_file`, `runtime`, `session`, `transmit_mode_from_u8`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`
|
||||
// These functions are ignored because they are not marked as `pub`: `log_file_path`, `log_sink`, `open_log_file`, `permission_events`, `publish_permission_state`, `runtime`, `session`, `transmit_mode_from_u8`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_fields_are_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`
|
||||
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `from_kotlin_str`, `to_permission_gate`
|
||||
|
||||
/// Return the platform-conventional log-file path as a string, or
|
||||
/// an empty string if the platform does not have one (mobile).
|
||||
@@ -160,6 +161,14 @@ Future<void> setOutputGain({required double gain}) =>
|
||||
/// blob, redacted per the production policy, that the user can
|
||||
/// share or copy. DEC-016 forbids automatic uploads — this is the
|
||||
/// only path that surfaces logs.
|
||||
///
|
||||
/// SDD-112 item 10 / SDD-113 item 7 / SDD-116 item 3: when an
|
||||
/// Android voice session is open this export embeds the
|
||||
/// `[audio.android]` verification-matrix fragment (requested /
|
||||
/// achieved stream config, per-effect engagement, latency tier).
|
||||
/// On non-Android targets or before a voice session opens the
|
||||
/// section is omitted. Per SDD-090 every field in that section is
|
||||
/// a device-side technical scalar — no PII admitted.
|
||||
String exportDiagnostics() => RustLib.instance.api.crateApiExportDiagnostics();
|
||||
|
||||
/// Wire the identity persistence store to a platform-private
|
||||
@@ -455,6 +464,28 @@ sealed class BridgeEvent with _$BridgeEvent {
|
||||
/// Resume recommendation from the platform. False on begin.
|
||||
required bool shouldResume,
|
||||
}) = BridgeEvent_InterruptionState;
|
||||
|
||||
/// SDD-106 §5: resolved platform-level permission state. On
|
||||
/// Android this is published by the JNI hook in
|
||||
/// [`crate::permission_jni`] whenever
|
||||
/// `AndroidPermissionRequester` reports a state transition
|
||||
/// (initial grant, denial, permanent denial, or mid-session
|
||||
/// revocation). The audio engine's `TransmitModeSelector`
|
||||
/// observes the `RECORD_AUDIO` variant of this event as an
|
||||
/// authoritative clamp on the transmit gate per SDD-106 §6
|
||||
/// (and SRS-209's listen-only fail-safe).
|
||||
///
|
||||
/// Carries the canonical Android permission string in
|
||||
/// `permission` (e.g. `"android.permission.RECORD_AUDIO"`)
|
||||
/// and the resolved state in `state`. No raw user input,
|
||||
/// timestamps, or other PII cross this boundary.
|
||||
const factory BridgeEvent.permissionState({
|
||||
/// Canonical Android permission identifier.
|
||||
required String permission,
|
||||
|
||||
/// Resolved permission state.
|
||||
required PermissionStateKind state,
|
||||
}) = BridgeEvent_PermissionState;
|
||||
}
|
||||
|
||||
/// Coarse OS-reported network state. Mirrors
|
||||
@@ -555,3 +586,24 @@ enum BridgeTransmitMode {
|
||||
/// as `Continuous`.
|
||||
voiceActivity,
|
||||
}
|
||||
|
||||
/// Schema-controlled mirror of the Kotlin
|
||||
/// `AndroidPermissionRequester.PermissionState` sealed class
|
||||
/// (SDD-106 §5). Crosses the bridge as an enum so the Dart side can
|
||||
/// `switch` on it exhaustively without parsing strings.
|
||||
enum PermissionStateKind {
|
||||
/// Permission granted by the user; capture may proceed.
|
||||
granted,
|
||||
|
||||
/// Permission denied (re-promptable).
|
||||
denied,
|
||||
|
||||
/// Permission permanently denied — the UI is expected to
|
||||
/// deep-link to system settings (SDD-106 §3).
|
||||
permanentlyDenied,
|
||||
|
||||
/// Any state string that did not match the contract above.
|
||||
/// Treated identically to `Denied` by the transmit clamp
|
||||
/// (fail-safe per SRS-209).
|
||||
unknown,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user