Persistent USB Permission in android app upon reattachment

80 views Asked by At

I have an application running in Android 11 that I am packaging with my own device. No other applications will ever be installed on this device. Also, given the age of the hardware, the device will never receive any Android updates.

I need to provide UART communication for my app to work as intended. Whenever I connect a USB cable, I get a pop up asking to grant permissions to the USB port and it works perfectly once those permissions are granted.

The challenge: Whenever the USB cable is disconnected and reconnected, I keep getting the USB permission pop up, which is, while a great thing from security standpoint and is generally considered the best practice, is proving to be a little annoying considering my use case.

Is there a way for me to simulate user granting the USB port permission without displaying the popup? Note: I was able to suppress the popup, and store the granted permissions in app's local storage.

Here is my code : Manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.usb.host" />
 <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.***themename***"
        android:directBootAware="true"
        tools:targetApi="31">
<activity
            android:name=".MainActivity"
            android:screenOrientation="landscape"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
<receiver
            android:name=".USBReceiver.UsbDeviceReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.packagename.USB_PERMISSION" />
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
            </intent-filter>
            <meta-data
                android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </receiver>
</application>
</manifest>

UsbDeviceReceiver

public class UsbDeviceReceiver extends BroadcastReceiver {

    private static final String ACTION_USB_PERMISSION = "com.example.packagename.USB_PERMISSION";
    private static final String ACTION_USB_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
    private static final String ACTION_USB_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        if (ACTION_USB_ATTACHED.equals(action)) {
            // Handle USB device attachment
            showToast(context, "USB Device Attached");


                Log.d("USB PERMISSION","has permission:- "+usbManager.hasPermission(device));
                // Check if permission is already granted for this device
                synchronized (this) {
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if (device != null) {
                            Log.d("USB PERMISSION", "permission exists");
                            // Permission granted, open the device
                            SerialPortManagerSingleton.getInstance(context, null).openSerialPort((UsbManager) context.getSystemService(Context.USB_SERVICE));
                        }
                    } else {
                        Log.d("USB PERMISSION", "permission does not exist");
                        // Permission denied
                        PendingIntent permissionIntent = PendingIntent.getBroadcast(
                                context,
                                0,
                                new Intent(ACTION_USB_PERMISSION),
                                PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0)
                        );
                        usbManager.requestPermission(device, permissionIntent);
                    }
                }

        } else if (ACTION_USB_DETACHED.equals(action)) {
            // Handle USB device detachment
            showToast(context, "USB Device Detached");
            SerialPortManagerSingleton.releaseInstance();
        } else if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if (device != null) {
                        Log.d("USB PERMISSION", "granted permission");
                        Log.d("USB PERMISSION","has permission:- "+usbManager.hasPermission(device));
                        // Permission granted, open the device
                        SerialPortManagerSingleton.getInstance(context, null).openSerialPort((UsbManager) context.getSystemService(Context.USB_SERVICE));
                    }
                } else {
                    Log.d("USB PERMISSION", "Permission denied for device " + device);
                    // Handle permission denial
                }
            }
        }
    }
    private void showToast(Context context, String message) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }
}

SerialPortMangerSingleton

public class SerialPortManagerSingleton {
    private static SerialPortManager instance;
    private static SerialPortManager.CombinedSerialListener savedListener;

    public static synchronized SerialPortManager getInstance(Context appContext, SerialPortManager.CombinedSerialListener listener) {
        if (instance == null) {
//            savedListener = listener;
            Log.d("saved","listener "+savedListener);
            instance = new SerialPortManager(savedListener);
            instance.openSerialPort((UsbManager) appContext.getSystemService(Context.USB_SERVICE));
        } else {
            if (listener != null) {
                savedListener = listener;
                Log.d("saved","listener set"+savedListener);
                instance.setListener(savedListener);
            } else if (savedListener != null) {
                instance.setListener(savedListener);
            }
        }
        return instance;
    }

    public static synchronized void releaseInstance() {
        if (instance != null) {
            instance.closeSerialPort();
            instance = null;
//            savedListener = null;
        }
    }
}

to provide a brief explanation about above codes :

  • whenever the usb is detached/attached/permission granted, it triggers the usbReceiver broadcast and performs the tasks written in the provided code. Upon attachment it checks if the permission is already present, if true, it starts opening the serial port and on false it asks the permissions and once permission is granrted it does the same.

However upon every reattachment, it asks for permission shown in the picturepop up whenever usb is attached

  • once i click on ok button, then it opens the serialport and everything works fine but when i reattach the usb it again popup the same that means it doesnot remember the permission across detachment and attachment. -If i try to open the serial port without giving permission for reattachment, then it throws error saying
UsbManager              pid-11465                            E  exception in UsbManager.openDevice
                                                                                                    java.lang.SecurityException: User has not given 10165/com.example.packagename permission to access device /dev/bus/usb/003/002

Any inputs are appreciated.

0

There are 0 answers