diff --git a/CHANGELOG.md b/CHANGELOG.md index 39155f8..dca80ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to CarOS Profile Switcher will be documented in this file. ## [Unreleased] +### Added +- **🎵 Audio Streaming Feature**: Stream audio from another device to car speakers via two methods: + - **Bluetooth A2DP Sink** (`ENABLE_BT_AUDIO_SINK`): Receive audio via Bluetooth + - May not work if Bluetooth is used for car audio even in wired mode + - Device becomes discoverable for pairing + - Automatic A2DP sink mode configuration + - **WiFi Hotspot** (`ENABLE_WIFI_AUDIO_HOTSPOT`): Create WiFi hotspot for audio streaming + - Recommended alternative when Bluetooth is occupied by car + - Works in WIRED mode when WiFi is not used by car + - Configurable SSID and password + - Requires audio streaming app on emitting device (SoundWire, AudioRelay, etc.) + - Both methods only work in WIRED mode (USB connection) + - Automatically disable in IDLE mode to save battery + - Comprehensive documentation in README.md, EXAMPLES.md, and FAQ.md + ### Planned - Improved error handling in service.sh diff --git a/EXAMPLES.md b/EXAMPLES.md index 8f59e9c..a18ed7a 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -115,7 +115,9 @@ IDLE_MAX_CPU_FREQ="1516800" # Throttle CPU to ~1.5GHz LIMIT_QUICK_CHARGE_WIRED=1 # Limit charging SET_NOVA_DEFAULT=1 KEEP_WIFI_IN_WIRED=0 # Turn off WiFi -KEEP_WIFI_IN_IDLE=0 # Turn off WiFi when idle +KEEP_WIFI_IN_IDLE=1 +AUTO_GRANT_PERMISSIONS=1 +ENABLE_BT_AUDIO_SINK=0 # Disable to save battery VERBOSE=0 # Reduce logging overhead ``` @@ -132,6 +134,49 @@ LIMIT_QUICK_CHARGE_WIRED=0 # Allow fast charging SET_NOVA_DEFAULT=1 KEEP_WIFI_IN_WIRED=1 # Keep WiFi on KEEP_WIFI_IN_IDLE=1 # Keep WiFi on +AUTO_GRANT_PERMISSIONS=1 +ENABLE_BT_AUDIO_SINK=0 +VERBOSE=1 +``` + +### Audio Streaming via Bluetooth +```bash +# Enable receiving audio from another phone via Bluetooth +# Note: May not work if Bluetooth is used for car audio even in wired mode +AUDI_BT_NAMES="Your Car" +AUDI_BT_MAC="" +ALLOW_BT_IN_WIRED=1 # Must keep BT on for audio streaming +DATA_OFF_OUTSIDE=1 +KEEP_DATA_IN_CAR=1 +IDLE_MAX_CPU_FREQ="" +LIMIT_QUICK_CHARGE_WIRED=1 +SET_NOVA_DEFAULT=1 +KEEP_WIFI_IN_WIRED=0 +KEEP_WIFI_IN_IDLE=1 +AUTO_GRANT_PERMISSIONS=1 +ENABLE_BT_AUDIO_SINK=1 # Enable Bluetooth audio sink mode +ENABLE_WIFI_AUDIO_HOTSPOT=0 +VERBOSE=1 +``` + +### Audio Streaming via WiFi Hotspot (Recommended) +```bash +# Enable WiFi hotspot for audio streaming (works even if BT used for car audio) +AUDI_BT_NAMES="Your Car" +AUDI_BT_MAC="" +ALLOW_BT_IN_WIRED=1 +DATA_OFF_OUTSIDE=1 +KEEP_DATA_IN_CAR=1 +IDLE_MAX_CPU_FREQ="" +LIMIT_QUICK_CHARGE_WIRED=1 +SET_NOVA_DEFAULT=1 +KEEP_WIFI_IN_WIRED=1 # WiFi must be on for hotspot +KEEP_WIFI_IN_IDLE=1 +AUTO_GRANT_PERMISSIONS=1 +ENABLE_BT_AUDIO_SINK=0 +ENABLE_WIFI_AUDIO_HOTSPOT=1 # Enable WiFi hotspot for audio streaming +WIFI_AUDIO_HOTSPOT_SSID="CarOS-Audio" +WIFI_AUDIO_HOTSPOT_PASSWORD="caros123" VERBOSE=1 ``` diff --git a/FAQ.md b/FAQ.md index 3894832..bd15104 100644 --- a/FAQ.md +++ b/FAQ.md @@ -130,6 +130,52 @@ In wired mode, the module tries to reduce fast charging to prevent phone overhea ### Does Nova Launcher need to be installed? No. If `SET_NOVA_DEFAULT=1` but Nova isn't installed, the module just skips this step. No errors. +### How does audio streaming work? +CarOS supports two methods for streaming audio from another phone: + +**Method 1: Bluetooth A2DP Sink** +When `ENABLE_BT_AUDIO_SINK=1`, your CarOS phone becomes discoverable and can receive audio from another phone via Bluetooth. + +**Limitation:** May not work if Bluetooth is already used for car audio/calls, even in wired mode. + +**Method 2: WiFi Hotspot (Recommended)** +When `ENABLE_WIFI_AUDIO_HOTSPOT=1`, your CarOS device creates a WiFi hotspot that other devices can connect to for streaming audio. + +**Advantages:** Works even when Bluetooth is used by the car. Requires an audio streaming app on the emitting phone. + +**To use WiFi hotspot:** +1. Enable in config: `ENABLE_WIFI_AUDIO_HOTSPOT=1` +2. Set SSID and password: `WIFI_AUDIO_HOTSPOT_SSID="CarOS-Audio"` +3. Connect to car via USB (wired mode) +4. Emitting phone connects to WiFi hotspot +5. Use audio streaming app (SoundWire, AudioRelay, etc.) + +### Does Bluetooth audio sink work on all devices? +No, it's device-dependent. Some Android devices don't support A2DP sink mode. Additionally, even in wired mode, many cars use Bluetooth for audio and phone calls, which prevents the phone from acting as an A2DP sink simultaneously. + +**If Bluetooth doesn't work:** Use the WiFi hotspot method instead (`ENABLE_WIFI_AUDIO_HOTSPOT=1`). + +### Why isn't my phone discoverable for Bluetooth audio? +If `ENABLE_BT_AUDIO_SINK=1` but your phone isn't discoverable: +1. **Bluetooth may be in use by the car** - Even in wired mode, many cars use Bluetooth for audio/calls +2. **Try WiFi hotspot instead** - Set `ENABLE_WIFI_AUDIO_HOTSPOT=1` +3. Check that you're in WIRED mode (USB connection) +4. Look for "BT Audio Sink" messages in logs +5. Your device may not support A2DP sink mode + +### Can I use audio streaming in IDLE mode? +No, both features are automatically disabled in IDLE mode to save battery. They only work when connected to your car in WIRED mode. + +### Can I use audio streaming in WIRELESS mode? +No, in wireless mode both Bluetooth and WiFi are typically used by the car for Android Auto connection. The audio streaming features only work in WIRED mode where the car connection is via USB. + +### What audio streaming apps work with WiFi hotspot? +Popular options include: +- **SoundWire** (free) - Low latency audio streaming +- **AudioRelay** (free) - WiFi/USB audio streaming +- **AirMusic** - DLNA/UPnP streaming +- Any app that supports streaming to a WiFi network + --- ## Troubleshooting diff --git a/README.md b/README.md index 0729e88..f143634 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ A Magisk module for Android that automatically switches device profiles based on - **Battery Saver**: Automatically enable when not in car - **Nova Launcher**: Set as default launcher automatically - **🔔 Auto Permissions**: Automatically grants notification and location permissions to Android Auto, Waze, Google Maps, and Nova Launcher +- **🎵 Audio Streaming**: Stream audio from another device to car speakers via Bluetooth A2DP sink or WiFi hotspot ### Configuration - User-configurable via `/sdcard/CarOS/config.env` @@ -79,10 +80,68 @@ KEEP_WIFI_IN_IDLE=1 # Keep WiFi on when idle # Automatic permission management AUTO_GRANT_PERMISSIONS=1 # Auto-grant notifications & location to AA, Waze, Maps +# Audio streaming options (choose one or both based on what works for your setup) +ENABLE_BT_AUDIO_SINK=0 # Bluetooth A2DP sink (may not work if BT used for car audio) +ENABLE_WIFI_AUDIO_HOTSPOT=0 # WiFi hotspot for audio streaming (alternative to BT) + +# WiFi hotspot settings (if using WiFi audio streaming) +WIFI_AUDIO_HOTSPOT_SSID="CarOS-Audio" +WIFI_AUDIO_HOTSPOT_PASSWORD="caros123" + # Verbose logging VERBOSE=1 ``` +## 🎵 Audio Streaming Feature + +Stream audio from another phone to your car speakers via Bluetooth or WiFi. Two methods available: + +### Method 1: Bluetooth A2DP Sink +When enabled (`ENABLE_BT_AUDIO_SINK=1`), your CarOS device can receive audio from another phone via Bluetooth. + +**Limitation:** May not work if Bluetooth is already used for car audio/calls (even in wired mode). + +**How to use:** +1. Enable in config: `ENABLE_BT_AUDIO_SINK=1` +2. Connect your CarOS phone to car via USB +3. On the emitting phone: + - Go to Bluetooth settings + - Find and pair with your CarOS device + - Connect and play music + +### Method 2: WiFi Hotspot (Recommended Alternative) +When enabled (`ENABLE_WIFI_AUDIO_HOTSPOT=1`), your CarOS device creates a WiFi hotspot that other devices can connect to for streaming audio. + +**Advantages:** +- ✓ Works even if Bluetooth is used for car audio +- ✓ Higher quality audio streaming +- ✓ Can work in WIRED mode when WiFi is not used by the car + +**How to use:** +1. Enable in config: + ```bash + ENABLE_WIFI_AUDIO_HOTSPOT=1 + WIFI_AUDIO_HOTSPOT_SSID="CarOS-Audio" + WIFI_AUDIO_HOTSPOT_PASSWORD="caros123" + ``` +2. Connect your CarOS phone to car via USB +3. On the emitting phone: + - Connect to the WiFi hotspot (SSID: CarOS-Audio) + - Use an audio streaming app (e.g., SoundWire, AudioRelay, etc.) + - Stream audio to CarOS device + +### Use Cases +- Stream music from a passenger's phone to your car speakers +- Multiple people can share music during the drive +- Alternative to passing phone cables around + +### Important Notes +- **Both methods work in WIRED mode only** (USB connection to car) +- Does NOT work in WIRELESS mode (both Bluetooth and WiFi may be used by car) +- Automatically disabled when IDLE to save battery +- WiFi hotspot method requires an audio streaming app on the emitting phone +- Bluetooth method may not work on all devices (A2DP sink support required) + ## 📝 How It Works 1. **Boot**: The module starts automatically via `service.sh` diff --git a/caros_config.sh b/caros_config.sh index 0a84b85..aa90a94 100644 --- a/caros_config.sh +++ b/caros_config.sh @@ -38,6 +38,22 @@ DEFAULT_KEEP_WIFI_IN_IDLE=0 # Automatically grants necessary permissions for Android Auto, Waze, Maps, etc. DEFAULT_AUTO_GRANT_PERMISSIONS=1 +# Bluetooth audio streaming (enable device as A2DP sink to receive audio from another phone) +# When enabled, the phone becomes discoverable and can receive audio from another device +# Note: May not work if Bluetooth is used for car audio even in wired mode +DEFAULT_ENABLE_BT_AUDIO_SINK=0 + +# WiFi hotspot for audio streaming (alternative to Bluetooth) +# Creates a WiFi hotspot that other devices can connect to for streaming audio +# Works when WiFi is not used by the car (typically in wired mode) +DEFAULT_ENABLE_WIFI_AUDIO_HOTSPOT=0 + +# WiFi hotspot SSID for audio streaming +DEFAULT_WIFI_AUDIO_HOTSPOT_SSID="CarOS-Audio" + +# WiFi hotspot password for audio streaming (min 8 characters) +DEFAULT_WIFI_AUDIO_HOTSPOT_PASSWORD="caros123" + # Verbose logs DEFAULT_VERBOSE=1 @@ -80,6 +96,17 @@ KEEP_WIFI_IN_IDLE=$DEFAULT_KEEP_WIFI_IN_IDLE # Automatic permission management (notifications and location) AUTO_GRANT_PERMISSIONS=$DEFAULT_AUTO_GRANT_PERMISSIONS +# Bluetooth audio streaming (enable device as A2DP sink to receive audio from another phone) +# Note: May not work if Bluetooth is used for car audio even in wired mode +ENABLE_BT_AUDIO_SINK=$DEFAULT_ENABLE_BT_AUDIO_SINK + +# WiFi hotspot for audio streaming (alternative to Bluetooth) +ENABLE_WIFI_AUDIO_HOTSPOT=$DEFAULT_ENABLE_WIFI_AUDIO_HOTSPOT + +# WiFi hotspot SSID and password +WIFI_AUDIO_HOTSPOT_SSID="$DEFAULT_WIFI_AUDIO_HOTSPOT_SSID" +WIFI_AUDIO_HOTSPOT_PASSWORD="$DEFAULT_WIFI_AUDIO_HOTSPOT_PASSWORD" + # Verbose logs VERBOSE=$DEFAULT_VERBOSE EOF @@ -98,5 +125,9 @@ apply_defaults() { : "${KEEP_WIFI_IN_WIRED:=$DEFAULT_KEEP_WIFI_IN_WIRED}" : "${KEEP_WIFI_IN_IDLE:=$DEFAULT_KEEP_WIFI_IN_IDLE}" : "${AUTO_GRANT_PERMISSIONS:=$DEFAULT_AUTO_GRANT_PERMISSIONS}" + : "${ENABLE_BT_AUDIO_SINK:=$DEFAULT_ENABLE_BT_AUDIO_SINK}" + : "${ENABLE_WIFI_AUDIO_HOTSPOT:=$DEFAULT_ENABLE_WIFI_AUDIO_HOTSPOT}" + : "${WIFI_AUDIO_HOTSPOT_SSID:=$DEFAULT_WIFI_AUDIO_HOTSPOT_SSID}" + : "${WIFI_AUDIO_HOTSPOT_PASSWORD:=$DEFAULT_WIFI_AUDIO_HOTSPOT_PASSWORD}" : "${VERBOSE:=$DEFAULT_VERBOSE}" } diff --git a/grant_permissions.sh b/grant_permissions.sh index d2f840d..1ee6d05 100644 --- a/grant_permissions.sh +++ b/grant_permissions.sh @@ -80,6 +80,30 @@ grant_location_permission() { appops set "$package" FINE_LOCATION allow 2>/dev/null } +# Grant Bluetooth permissions for audio sink functionality +grant_bluetooth_audio_permissions() { + # Check if Bluetooth audio sink is enabled + if [ "$ENABLE_BT_AUDIO_SINK" != "1" ]; then + return 0 + fi + + log "Granting Bluetooth audio permissions for A2DP sink mode" + + # Grant system-level Bluetooth permissions (Android 12+) + pm grant com.android.bluetooth android.permission.BLUETOOTH_CONNECT 2>/dev/null + pm grant com.android.bluetooth android.permission.BLUETOOTH_SCAN 2>/dev/null + pm grant com.android.bluetooth android.permission.BLUETOOTH_ADVERTISE 2>/dev/null + + # Grant audio permissions + pm grant com.android.bluetooth android.permission.MODIFY_AUDIO_SETTINGS 2>/dev/null + pm grant com.android.bluetooth android.permission.RECORD_AUDIO 2>/dev/null + + # Set app ops for Bluetooth + appops set com.android.bluetooth BLUETOOTH_SCAN allow 2>/dev/null + appops set com.android.bluetooth BLUETOOTH_CONNECT allow 2>/dev/null + appops set com.android.bluetooth BLUETOOTH_ADVERTISE allow 2>/dev/null +} + # Main execution log "Starting permission grants..." @@ -93,4 +117,7 @@ for app in "${LOCATION_APPS[@]}"; do grant_location_permission "$app" done +# Grant Bluetooth audio permissions +grant_bluetooth_audio_permissions + log "Permission grants completed" diff --git a/service.sh b/service.sh index dd587ab..79e61c8 100644 --- a/service.sh +++ b/service.sh @@ -159,6 +159,103 @@ set_nova_default(){ fi } +# Enable Bluetooth audio sink mode (A2DP sink) +enable_bt_audio_sink(){ + [ "$ENABLE_BT_AUDIO_SINK" = "1" ] || return 0 + + vlog "Enabling Bluetooth audio sink mode" + + # Ensure Bluetooth is on + bt_on + sleep 1 + + # Make device discoverable for pairing + # Timeout 0 means stay discoverable indefinitely while in car + settings put global bluetooth_discoverable_timeout 0 2>/dev/null + + # Enable Bluetooth discoverability via dumpsys + cmd bluetooth_manager enable 2>/dev/null + cmd bluetooth_manager set-discoverability on 2>/dev/null || { + # Fallback method + settings put global bluetooth_on 1 2>/dev/null + } + + # Try to enable A2DP sink mode via system properties + setprop persist.bluetooth.a2dp_sink.enabled true 2>/dev/null + setprop bluetooth.a2dp.sink.enabled true 2>/dev/null + + # Try to set Bluetooth class to audio sink + setprop bluetooth.device.class 0x240404 2>/dev/null + + # Enable audio routing to main speaker for Bluetooth audio + # This ensures incoming Bluetooth audio plays through system audio + settings put system bluetooth_audio_output 0 2>/dev/null + + vlog "Bluetooth audio sink mode enabled - device should be discoverable" + log "BT Audio Sink: Device is now discoverable for audio streaming" +} + +# Disable Bluetooth audio sink mode +disable_bt_audio_sink(){ + [ "$ENABLE_BT_AUDIO_SINK" = "1" ] || return 0 + + vlog "Disabling Bluetooth audio sink mode" + + # Disable discoverability to save battery when not in car + settings put global bluetooth_discoverable_timeout 120 2>/dev/null + cmd bluetooth_manager set-discoverability off 2>/dev/null + + # Disable A2DP sink mode + setprop persist.bluetooth.a2dp_sink.enabled false 2>/dev/null + setprop bluetooth.a2dp.sink.enabled false 2>/dev/null + + # Reset Bluetooth class to default (phone) + setprop bluetooth.device.class 0x5A020C 2>/dev/null + + vlog "Bluetooth audio sink mode disabled" +} + +# Enable WiFi hotspot for audio streaming +enable_wifi_audio_hotspot(){ + [ "$ENABLE_WIFI_AUDIO_HOTSPOT" = "1" ] || return 0 + + vlog "Enabling WiFi hotspot for audio streaming" + + # Ensure WiFi is on + wifi_on + sleep 1 + + # Stop any existing hotspot + cmd wifi stop-softap 2>/dev/null + sleep 1 + + # Start WiFi hotspot with configured SSID and password + # Method 1: Using cmd wifi (Android 11+) + cmd wifi start-softap "$WIFI_AUDIO_HOTSPOT_SSID" wpa2-psk "$WIFI_AUDIO_HOTSPOT_PASSWORD" 2>/dev/null || { + # Method 2: Using settings (fallback) + settings put global wifi_ap_ssid "$WIFI_AUDIO_HOTSPOT_SSID" 2>/dev/null + settings put global wifi_ap_passwd "$WIFI_AUDIO_HOTSPOT_PASSWORD" 2>/dev/null + svc wifi enable 2>/dev/null + } + + vlog "WiFi hotspot enabled - SSID: $WIFI_AUDIO_HOTSPOT_SSID" + log "WiFi Audio Hotspot: Enabled for audio streaming (SSID: $WIFI_AUDIO_HOTSPOT_SSID)" +} + +# Disable WiFi hotspot for audio streaming +disable_wifi_audio_hotspot(){ + [ "$ENABLE_WIFI_AUDIO_HOTSPOT" = "1" ] || return 0 + + vlog "Disabling WiFi hotspot for audio streaming" + + # Stop WiFi hotspot + cmd wifi stop-softap 2>/dev/null || { + settings put global wifi_ap_ssid "" 2>/dev/null + } + + vlog "WiFi hotspot disabled" +} + usb_connected(){ if [ -f /sys/class/power_supply/usb/online ] && [ "$(cat /sys/class/power_supply/usb/online 2>/dev/null)" = "1" ]; then return 0 @@ -236,6 +333,9 @@ apply_wired_profile(){ reset_cpu_max [ "$LIMIT_QUICK_CHARGE_WIRED" = "1" ] && charge_limit_on set_nova_default + # Enable audio streaming (Bluetooth or WiFi hotspot based on config) + enable_bt_audio_sink + enable_wifi_audio_hotspot screen_off } @@ -247,6 +347,9 @@ apply_wireless_profile(){ reset_cpu_max charge_limit_off set_nova_default + # Note: BT audio sink NOT enabled in wireless mode because Bluetooth is already + # being used to connect to the car. WiFi may also be used by wireless AA. + # Audio streaming features disabled in wireless mode. screen_off } @@ -256,6 +359,8 @@ apply_idle_profile(){ battery_saver_on set_cpu_max "$IDLE_MAX_CPU_FREQ" charge_limit_off + disable_bt_audio_sink + disable_wifi_audio_hotspot } STATE=""