当前位置: 首页 > article >正文

【AndroidAPP】权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE],USB设备访问权限系统报错

一、问题原因

在这里插入图片描述

1.安卓安全性变更

Android 12+ 的安全性变更,Google 引入了更严格的 PendingIntent 安全管理,强制要求开发者明确指定 PendingIntent 的可变性(Mutable)或不可变性(Immutable)。
但是,在从 Android 14+ (API 34) 开始,FLAG_MUTABLE 和隐式 Intent 的组合会被禁止。因此,在使用静态的广播请求的时候,FLAG_MUTABLE多余,且违反安全规则。

2.关键点

关键在于安卓 14+ 版本的安全策略变化,导致无法再继续使用 FLAG_MUTABLE。
在这里插入图片描述

二、解决办法

1.代码示例

先给出代码示例,供大家参考,然后解释关键点在哪。

MainActivity.kt(Kotlin class)代码示例:

package com.example.serialportdebugapp

import android.app.PendingIntent
import android.content.Intent
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    private lateinit var statusTextView: TextView
    private lateinit var logTextView: TextView
    private lateinit var checkPortButton: Button
    private lateinit var usbManager: UsbManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化视图
        statusTextView = findViewById(R.id.statusTextView)
        logTextView = findViewById(R.id.logTextView)
        checkPortButton = findViewById(R.id.checkPortButton)

        // 获取 USB 管理器
        usbManager = getSystemService(USB_SERVICE) as UsbManager

        // 按钮点击事件
        checkPortButton.setOnClickListener {
            detectUsbDevices()
        }
    }

    /**
     * 检测 USB 设备
     */
    private fun detectUsbDevices() {
        val deviceList = usbManager.deviceList
        if (deviceList.isEmpty()) {
            logTextView.append("No USB devices found\n")
            return
        }

        for ((_, device) in deviceList) {
            logTextView.append("Detected device: ${device.deviceName}\n")
            requestPermission(device)
        }
    }

    /**
     * 请求 USB 权限
     */
    private fun requestPermission(device: UsbDevice) {
        val intent = PendingIntent.getBroadcast(
            this, 0, Intent("com.android.example.USB_PERMISSION"),
            PendingIntent.FLAG_IMMUTABLE
        )
        usbManager.requestPermission(device, intent)
    }
}

UsbBroadcastReceiver.kt(Kotlin class)代码示例:

package com.example.serialportdebugapp

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.util.Log

class UsbBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        if (action == "com.android.example.USB_PERMISSION") {
            synchronized(this) {
                val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    device?.let {
                        Log.d("UsbBroadcastReceiver", "Permission granted for device: ${device.deviceName}")
                    }
                } else {
                    Log.d("UsbBroadcastReceiver", "Permission denied for device: ${device?.deviceName}")
                }
            }
        }
    }
}

AndroidManifest.xml 代码示例(总配置文件)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SerialPortDebugApp">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 广播接收器,处理 USB 权限 -->
        <receiver android:name=".UsbBroadcastReceiver" android:exported="false">
            <intent-filter>
                <action android:name="com.android.example.USB_PERMISSION" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

build.gradle.kts(:app) 相关依赖代码示例

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}

android {
    namespace = "com.example.serialportdebugapp"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.example.serialportdebugapp"
        minSdk = 26
        targetSdk = 35
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
    buildFeatures {
        compose = true
    }
}

dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    implementation(libs.androidx.appcompat)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)
    implementation(libs.purejavacomm)
}

libs.versions.toml 代码示例:

[versions]
agp = "8.7.2"
kotlin = "2.0.0"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.8.0"
composeBom = "2024.04.01"
appcompat = "1.7.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
purejavacomm = { group = "com.github.purejavacomm", name = "purejavacomm", version = "1.0.2.RELEASE" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

activity_main.xml 代码示例 (APP界面,UI,用于测试USB串口调试APP的界面UI设计)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/statusTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="USB Status: Not connected"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/logTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Log output:\n"
        android:padding="8dp"
        android:scrollbars="vertical" />

    <Button
        android:id="@+id/checkPortButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Check USB Devices" />
</LinearLayout>

2.关键点

关键点在于,MainActivity.kt 中的 这段代码:

    /**
     * 请求 USB 权限
     */
    private fun requestPermission(device: UsbDevice) {
        val intent = PendingIntent.getBroadcast(
            this, 0, Intent("com.android.example.USB_PERMISSION"),
            PendingIntent.FLAG_IMMUTABLE
        )
        usbManager.requestPermission(device, intent)
    }

能够看到,在使用 PendingIntent 的时候,我们告知了“FLAG_IMMUTABLE”,这是关键,回到文章最开始的时候我们说到过的:
这是 Android 14+ 的特性,我们只能使用 FLAG_IMMUTABLE,也就是不可变的 PendingIntent。

只要这个写对了,权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE] 的 BUG基本就会被解决。

3.关于 Intent 和 PendingIntent:

Intent 是 Android 中的一种消息对象,用于描述应用程序要执行的操作。
作用是用来启动 活动(Activity)、服务(Service) 或 广播(Broadcast)。
可以携带数据,以便被启动的组件可以接收到并使用这些数据。

分为显示和隐式:

在这里插入图片描述

常见的 Intent 的用途:

在这里插入图片描述
PendingIntent 是一种特殊类型的 Intent,可以在 未来 的某个时间由系统或其他应用触发。
它充当一个 “授权”,允许其他应用或系统在您的应用上下文中执行操作。

通常用于将操作 “延迟执行”,而不是立即执行。
适用于一些 异步场景,例如:通知(Notification)的点击事件。定时任务。广播接收器。

说白了,它就好像嵌入式和VUE中的 “监听”,是用来等待消息的,而不是主动出击。
而且,重要的是:Intent 是瞬发的,使用后就销毁。
但是 PendingIntent 是持续的,会一直存在到 被触发、被取消 为止。

Intent 和 PendingIntent 的对比

在这里插入图片描述

PendingIntent 的 3 种类型

在这里插入图片描述


http://www.kler.cn/a/460270.html

相关文章:

  • 物联网开发利器:基于web的强大的可拖拽组态软件
  • 《机器学习》从入门到实战——逻辑回归
  • 深入剖析MySQL数据库架构:核心组件、存储引擎与优化策略(一)
  • git环境配置用户与秘钥
  • RC充电电路仿真与分析
  • 【机器学习 | 数据挖掘】时间序列算法
  • C语言一维数组与指针运算
  • 《计算机组成及汇编语言原理》阅读笔记:p133-p159
  • WPF的下拉复选框多选,数据来源数据库的表
  • 【人工智能机器学习基础篇】——深入详解深度学习之神经网络基础:理解前馈神经网络与反向传播算法
  • 医疗数仓配置Flume
  • 使用maven-mvnd替换maven大大提升编译打包速度
  • sublime 文件高亮设置
  • vim编辑器实用设置
  • VirtualBox新版本报错 Invalid installation directory解决方案
  • C#封送类
  • Tesseract-OCR 文字识别
  • 【Spring】Spring DI(依赖注入)详解—自动装配—byType实现原理
  • 智元与汇川加码,机器人如何利好电机市场?
  • Sigrity System SI SerialLink模式进行HDMI2协议仿真分析操作指导-TP1
  • AI安全的挑战:如何让人工智能变得更加可信
  • 【从零开始入门unity游戏开发之——C#篇41】C#迭代器(Iterator)——自定义类实现 foreach 操作
  • 图像处理-Ch7-小波函数
  • 开源大数据平台E-MapReduce
  • 【广州计算机学会、广州互联网协会联合主办 | ACM独立出版 | 高录用】第四届大数据、信息与计算机网络国际学术会议(BDICN 2025)
  • 【电路理论四】正弦电流电路