【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 种类型

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/945936.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Inno Setup生成exe安装包

Inno Setup生成exe安装包 第一步&#xff1a;创建一个带向导的脚本文件 第二步&#xff1a;直接 Next&#xff0c;不要创建空的脚本文件 第三步&#xff1a;填写相关的应用程序信息 第四步&#xff1a;指定应用程序的安装目录相关的信息 第五步&#xff1a;选择可执行程序和相…

数据库MHA

MHA 什么是MHA -------- MASTER HIGH AVAILABILITY 建立在主从复制基础之上的故障切换到软件系统 主从复制的单点问题&#xff1a; 当主从复制当中&#xff0c;主服务器发生故障&#xff0c;会自动切换到一台从服务器&#xff0c;然后把从服务器升格为主&#xff0c;继续主…

vue2 - Day04 - 插槽、路由

插槽、路由 一、插槽&#xff08;solt&#xff09;1.1 概念1.2 基本用法1.3 分类1.3.1 默认插槽&#xff08;Default Slot&#xff09;例子&#xff1a; 1.3.2 具名插槽&#xff08;Named Slots&#xff09;语法&#xff1a; 1.3.3 作用域插槽&#xff08;Scoped Slots&#xf…

微信小程序:定义页面标题,动态设置页面标题,json

1、常规设置页面标题 正常微信小程序中&#xff0c;设置页面标题再json页面中进行设置&#xff0c;例如 {"usingComponents": {},"navigationBarTitleText": "标题","navigationBarBackgroundColor": "#78b7f7","navi…

【数据可视化-10】国防科技大学录取分数线可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

Spring Boot教程之四十一:在 Spring Boot 中调用或使用外部 API

如何在 Spring Boot 中调用或使用外部 API&#xff1f; Spring Boot 建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。它现在越来越受到开发人员的青睐&#xff0c;因为它是一个快速的生产就绪环境&#xff0c;使开发人员能够直接专注于逻辑&#xff0c;而不必费力配置…

L25.【LeetCode笔记】 三步问题的四种解法(含矩阵精彩解法!)

目录 1.题目 2.三种常规解法 方法1:递归做 ​编辑 方法2:改用循环做 初写的代码 提交结果 分析 修改后的代码 提交结果 for循环的其他写法 提交结果 方法3:循环数组 提交结果 3.方法4:矩阵 算法 代码实践 1.先计算矩阵n次方 2.后将矩阵n次方嵌入递推式中 提…

面试题解,JVM的运行时数据区

一、请简述JVM运行时数据区的组成结构及各部分作用 总览 从线程持有的权限来看 线程私有区 虚拟机栈 虚拟机栈是一个栈结构&#xff0c;由许多个栈帧组成&#xff0c;一个方法分配一个栈帧&#xff0c;线程每执行一个方法时都会有一个栈帧入栈&#xff0c;方法执行结束后栈帧…

代码随想录算法【Day7】

DAY7 454.四数相加II 特点&#xff1a; 1.只用返回元组的个数&#xff0c;而不用返回具体的元组 2.可以不用去重 暴力思路&#xff1a;遍历&#xff0c;这样时间复杂度会达到O(n^4) 标准思路&#xff1a;用哈希法&#xff08;场景&#xff1a;在一个集合里面判断一个元素…

网络渗透测试实验四:CTF实践

1.实验目的和要求 实验目的:通过对目标靶机的渗透过程,了解CTF竞赛模式,理解CTF涵盖的知识范围,如MISC、PPC、WEB等,通过实践,加强团队协作能力,掌握初步CTF实战能力及信息收集能力。熟悉网络扫描、探测HTTP web服务、目录枚举、提权、图像信息提取、密码破解等相关工具…

[羊城杯 2024]不一样的数据库_2

题目描述&#xff1a; 压缩包6 (1).zip需要解压密码&#xff1a; 尝试用ARCHPR工具爆破一下&#xff1a; &#xff08;字典可自行在github上查找&#xff09; 解压密码为&#xff1a;753951 解压得到13.png和Kee.kdbx文件&#xff1a; 二维码图片看上去只缺了正常的三个角&…

JSON结构快捷转XML结构API集成指南

JSON结构快捷转XML结构API集成指南 引言 在当今的软件开发世界中&#xff0c;数据交换格式的选择对于系统的互操作性和效率至关重要。JSON&#xff08;JavaScript Object Notation&#xff09;和XML&#xff08;eXtensible Markup Language&#xff09;是两种广泛使用的数据表…

小程序租赁系统构建指南与市场机会分析

内容概要 在当今竞争激烈的市场环境中&#xff0c;小程序租赁系统正崭露头角&#xff0c;成为企业转型与创新的重要工具。通过这个系统&#xff0c;商户能够快速推出自己的小程序&#xff0c;无需从头开发&#xff0c;节省了大量时间和资金。让我们来看看这个系统的核心功能吧…

单词统计详解---pyhton

有一个.txt的文本文件&#xff0c;对齐单词进行统计&#xff0c;并显示单词重复做多的10个单词 思路&#xff1a; 1将文本文件进行逐行处理&#xff0c;并进行空格分割处理 2新建一个字典&#xff0c;使用get方法将单词一次添加到字典中&#xff0c;并用sorted方法进行排序。…

如何逐步操作vCenter修改DNS服务器?

在vSphere 7中有一个新功能&#xff0c;它允许管理员更改vCenter Server Appliance的FQDN和IP。因此本文将介绍如何轻松让vCenter修改DNS服务器。 vCenter修改DNS以及修改vCenter IP地址 与在部署 vCenter Server Appliance 后&#xff0c;您可以根据需要修改其 DNS 设置和 IP…

[创业之路-225]:《华为闭环战略管理》-4-华为的商业智慧:在价值链中探索取舍之道与企业边界

目录 一、在价值链中探索取舍之道与企业边界 价值链的深刻洞察 取舍之道&#xff1a;有所为&#xff0c;有所不为 垂直整合与横向整合的平衡 企业边界与活动边界的界定 采购与外包的智慧运用 结语 二、企业外部价值流&#xff1a;上游、中游、下游、终端 上游&#xf…

鸿蒙1.2:第一个应用

1、create Project&#xff0c;选择Empty Activity 2、配置项目 project name 为项目名称&#xff0c;建议使用驼峰型命名 Bundle name 为项目包名 Save location 为保存位置 Module name 为模块名称&#xff0c;即运行时需要选择的模块名称&#xff0c;见下图 查看模块名称&…

python小项目:使用多剪贴板自动回复消息

使用多剪贴板自动回复消息 一、效果展示二、实现步骤2.1 编写python脚本2.2 批处理脚本2.3 运行脚本 三、用到知识3.1 sys.argv3.2 pyperclip3.2.1 主要功能3.2.2 跨平台支持3.2.3 安装方法3.2.4 基本用法3.2.5 高级用法3.2.6 注意事项 一、效果展示 说明&#xff1a;在windows…

Qt监控系统放大招/历经十几年迭代完善/多屏幕辅屏预览/多层级设备树/网络登录和回放

一、前言说明 近期对视频监控系统做了比较大的更新升级&#xff0c;主要就是三点&#xff0c;第一点就是增加了辅屏预览&#xff0c;这个也是好多个客户需要的功能&#xff0c;海康的iVMS-4200客户端就有这个功能&#xff0c;方便在多个屏幕打开不同的视频进行查看&#xff0c…

基于Spring Boot + Vue3实现的在线汽车保养维修预约管理系统源码+文档

前言 基于Spring Boot Vue3实现的在线汽车保养维修预约管理系统是一种前后端分离架构的应用&#xff0c;它结合了Java后端开发框架Spring Boot和现代JavaScript前端框架Vue.js 3.0的优势。这样的系统可以为汽车服务站提供一个高效的平台来管理客户的预约请求 技术选型 系统…