From bfbbffde94d1706dbaaeeb00e92c2afb3b2decce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 05:13:30 +0000 Subject: [PATCH] fix: adapt all permissions declarations and runtime requests for full SDK range - AndroidManifest.xml: add maxSdkVersion=30 to location permissions (not needed on API 31+ with BLUETOOTH_SCAN neverForLocation), add FOREGROUND_SERVICE_CONNECTED_DEVICE permission (required API 34+), add foregroundServiceType=connectedDevice to ObdBackgroundService - MainActivity: replace 4 individual requestPermissions() calls (only last dialog was shown) with a single batched call keyed on SDK version (BLUETOOTH_SCAN+CONNECT on API 31+, ACCESS_FINE_LOCATION on API 23-30); add POST_NOTIFICATIONS runtime request on API 33+; add onRequestPermissionsResult() to retry BT init after grant - BtDeviceListActivity: batch BLUETOOTH_SCAN+BLUETOOTH_CONNECT into one requestPermissions() call - BleDeviceListActivity: same batching fix; guard startScan() behind permission check with early return - ChartActivity: add WRITE_EXTERNAL_STORAGE runtime request before screenshot on API 23-28; add onRequestPermissionsResult() to take screenshot after permission is granted Agent-Logs-Url: https://github.com/fr3ts0n/AndrOBD/sessions/6000e1a5-7568-4451-9a6a-42692a940107 Co-authored-by: fr3ts0n <2822578+fr3ts0n@users.noreply.github.com> --- androbd/src/main/AndroidManifest.xml | 12 +++- .../gui/androbd/BleDeviceListActivity.java | 10 ++- .../ecu/gui/androbd/BtDeviceListActivity.java | 9 ++- .../ecu/gui/androbd/ChartActivity.java | 36 +++++++++- .../fr3ts0n/ecu/gui/androbd/MainActivity.java | 70 +++++++++++++++---- 5 files changed, 114 insertions(+), 23 deletions(-) diff --git a/androbd/src/main/AndroidManifest.xml b/androbd/src/main/AndroidManifest.xml index e75ede8d..ef023452 100644 --- a/androbd/src/main/AndroidManifest.xml +++ b/androbd/src/main/AndroidManifest.xml @@ -11,8 +11,11 @@ - - + + + @@ -26,6 +29,8 @@ + + + android:exported="false" + android:foregroundServiceType="connectedDevice" /> \ No newline at end of file diff --git a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BleDeviceListActivity.java b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BleDeviceListActivity.java index 7b8956d0..e4d92552 100644 --- a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BleDeviceListActivity.java +++ b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BleDeviceListActivity.java @@ -11,6 +11,7 @@ import android.os.Build; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; +import java.util.ArrayList; import java.util.List; @RequiresApi (api = Build.VERSION_CODES.LOLLIPOP) @@ -22,11 +23,16 @@ public class BleDeviceListActivity @SuppressLint("InlinedApi") @Override protected void startDeviceScan() { + List missingPermissions = new ArrayList<>(); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_SCAN}, 1); + missingPermissions.add(Manifest.permission.BLUETOOTH_SCAN); } if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_CONNECT}, 1); + missingPermissions.add(Manifest.permission.BLUETOOTH_CONNECT); + } + if (!missingPermissions.isEmpty()) { + ActivityCompat.requestPermissions(this, missingPermissions.toArray(new String[0]), 1); + return; } leScanner = mBtAdapter.getBluetoothLeScanner(); leScanner.startScan(scanCallback); diff --git a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BtDeviceListActivity.java b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BtDeviceListActivity.java index 06be8df6..3c83b726 100644 --- a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BtDeviceListActivity.java +++ b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/BtDeviceListActivity.java @@ -41,6 +41,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; +import java.util.ArrayList; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -71,11 +72,15 @@ public class BtDeviceListActivity extends Activity super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + List missingPermissions = new ArrayList<>(); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_SCAN}, 1); + missingPermissions.add(Manifest.permission.BLUETOOTH_SCAN); } if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_CONNECT}, 1); + missingPermissions.add(Manifest.permission.BLUETOOTH_CONNECT); + } + if (!missingPermissions.isEmpty()) { + ActivityCompat.requestPermissions(this, missingPermissions.toArray(new String[0]), 1); } } diff --git a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/ChartActivity.java b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/ChartActivity.java index ec909adf..b75112a7 100644 --- a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/ChartActivity.java +++ b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/ChartActivity.java @@ -18,11 +18,14 @@ package com.fr3ts0n.ecu.gui.androbd; +import android.Manifest; import android.annotation.SuppressLint; import android.app.ActionBar; import android.app.Activity; +import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.Paint.Align; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -34,6 +37,9 @@ import android.view.WindowManager; import android.widget.EditText; import android.widget.ListAdapter; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; + import com.fr3ts0n.ecu.EcuDataPv; import com.fr3ts0n.ecu.prot.obd.ObdProt; @@ -70,6 +76,9 @@ public class ChartActivity extends Activity */ public static final String POSITIONS = "POSITIONS"; + /** Permission request code for WRITE_EXTERNAL_STORAGE (screenshot, API 23-28) */ + private static final int REQUEST_WRITE_STORAGE = 1; + /** Map to uniquely collect PID numbers */ private final TreeSet pidNumbers = new TreeSet<>(); @@ -248,7 +257,16 @@ public class ChartActivity extends Activity if (itemId == R.id.share) { new ExportTask(this).execute(sensorData); } else if (itemId == R.id.snapshot) { - Screenshot.takeScreenShot(this, getWindow().peekDecorView()); + // WRITE_EXTERNAL_STORAGE is required on API 23-28 for the screenshot + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q + && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_WRITE_STORAGE); + } else { + Screenshot.takeScreenShot(this, getWindow().peekDecorView()); + } } return super.onOptionsItemSelected(item); } @@ -272,6 +290,22 @@ public class ChartActivity extends Activity super.onDestroy(); } + /** + * Take screenshot once WRITE_EXTERNAL_STORAGE permission is granted (API 23-28). + */ + @Override + public void onRequestPermissionsResult(int requestCode, + @NonNull String[] permissions, + @NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == REQUEST_WRITE_STORAGE + && grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Screenshot.takeScreenShot(this, getWindow().peekDecorView()); + } + } + private final Timer refreshTimer = new Timer(); /** diff --git a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/MainActivity.java b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/MainActivity.java index 1342d878..180309df 100644 --- a/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/MainActivity.java +++ b/androbd/src/main/java/com/fr3ts0n/ecu/gui/androbd/MainActivity.java @@ -84,6 +84,7 @@ import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; @@ -163,6 +164,8 @@ public class MainActivity extends PluginManager private static final int REQUEST_SETTINGS = 5; private static final int REQUEST_CONNECT_DEVICE_USB = 6; private static final int REQUEST_GRAPH_DISPLAY_DONE = 7; + private static final int REQUEST_BT_PERMISSIONS = 8; + private static final int REQUEST_NOTIFICATIONS = 9; /** * app exit parameters */ @@ -641,6 +644,13 @@ public class MainActivity extends PluginManager CommService.medium = CommService.MEDIUM.USB; } + // Request POST_NOTIFICATIONS permission on API 33+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_NOTIFICATIONS); + } + } + initSelectedMode(); // Bind to background OBD service for continuous monitoring @@ -666,22 +676,28 @@ public class MainActivity extends PluginManager mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); log.fine("Adapter: " + mBluetoothAdapter); - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { - log.warning("permission.BLUETOOTH_SCAN missing"); - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_SCAN}, 1); + // Collect missing BT/location permissions and request them all at once + List missingPermissions = new ArrayList<>(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // API 31+: new Bluetooth permissions replace location for scanning + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { + log.warning("permission.BLUETOOTH_SCAN missing"); + missingPermissions.add(Manifest.permission.BLUETOOTH_SCAN); + } + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { + log.warning("permission.BLUETOOTH_CONNECT missing"); + missingPermissions.add(Manifest.permission.BLUETOOTH_CONNECT); + } + } else { + // API 23-30: location permission required for BT/BLE scan + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + log.warning("permission.ACCESS_FINE_LOCATION missing"); + missingPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION); + } } - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - log.warning("permission.ACCESS_FINE_LOCATION missing"); - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1); - } - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - log.warning("permission.ACCESS_FINE_LOCATION missing"); - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); - } - - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { - log.warning("permission.BLUETOOTH_CONNECT missing"); - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_CONNECT}, 1); + if (!missingPermissions.isEmpty()) { + ActivityCompat.requestPermissions(this, missingPermissions.toArray(new String[0]), REQUEST_BT_PERMISSIONS); + return; } // If BT is not on, request that it be enabled. @@ -732,6 +748,30 @@ public class MainActivity extends PluginManager super.onStart(); } + /** + * Handle permission request results. + * Retries BT/BLE mode initialisation once the required permissions are granted. + */ + @Override + public void onRequestPermissionsResult(int requestCode, + @androidx.annotation.NonNull String[] permissions, + @androidx.annotation.NonNull int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == REQUEST_BT_PERMISSIONS) { + boolean allGranted = true; + for (int result : grantResults) { + if (result != PackageManager.PERMISSION_GRANTED) { + allGranted = false; + break; + } + } + if (allGranted) { + initSelectedMode(); + } + } + } + @Override protected void onPause() { super.onPause();