Android音频之车载录音权限

 

Android音频之车载录音权限

android.permission.RECORD_AUDIO权限

android.permission.RECORD_AUDIO 是 Android 系统中用于访问设备麦克风并捕获音频数据的权限。该权限属于 危险权限(Dangerous Permission),需要用户明确授权才能使用。


一、权限声明

在 Android 应用的 AndroidManifest.xml 文件中,需要显式声明该权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

二、权限类别

  1. 危险权限
    • RECORD_AUDIO 属于危险权限,需要通过运行时权限请求获得用户的授权。
    • 在 Android 6.0 (API 23) 及以上版本,声明该权限后,应用必须在运行时动态申请权限。
  2. 权限分组
    • 该权限属于 麦克风权限组android.permission-group.MICROPHONE)。

三、使用场景

1. 典型功能

  • 音频录制:录制用户的语音或环境音。
  • 音频处理:实时音频处理,如降噪、语音识别等。
  • 多媒体应用:如录音机、视频录制、视频通话、直播应用等。

2. 与相关API结合使用

  • MediaRecorder:录制音频或视频。
  • AudioRecord:低级音频录制接口,用于实时音频处理。
  • SpeechRecognizer:语音识别接口,通常需要录音权限。
  • MediaProjection:用于录制屏幕时捕获音频。

四、权限申请

在 Android 6.0 (API 23) 及以上版本,运行时需要动态申请权限。

应用自申请

// 1. 检查权限 在使用录音功能前,检查是否已经获得录音权限:
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
        != PackageManager.PERMISSION_GRANTED) {
    // 权限未被授予
    // 2. 请求权限 向用户请求录音权限:
    ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE);
}

// 3. 权限结果回调 处理用户权限请求的结果:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限已被授予
        } else {
            // 权限被拒绝
        }
    }
}

五、系统限制

1. 录音权限的作用

在 Android 系统中,即使应用获得了 RECORD_AUDIO 权限,系统仍会对音频数据的访问进行一定限制:

  • 如果应用未获得权限,录音接口(如 AudioRecordMediaRecorder)的返回数据可能为零值数据。
  • 在部分场景下,系统可能通过 音频策略(AudioPolicy)来限制录音功能,例如屏蔽后台录音等。

2. 多权限交互

RECORD_AUDIO 权限可能与以下权限配合使用:

  • android.permission.MODIFY_AUDIO_SETTINGS:允许修改音频设置。
  • android.permission.CAPTURE_AUDIO_OUTPUT:捕获设备音频输出,但通常仅限于系统应用。
  • android.permission.BLUETOOTH:通过蓝牙设备录音时可能需要。

3. 后台录音限制

  • 从 Android 10(API 29)开始,后台录音被系统进一步限制,除非应用具有特殊权限(如 FOREGROUND_SERVICE)。
  • 应用必须运行在前台(显示通知)才能录音。

六、典型问题

1. 权限未声明

  • 错误:java.lang.SecurityException: Permission Denial: requires android.permission.RECORD_AUDIO
  • 解决方法:在 AndroidManifest.xml 中添加权限声明。

2. 权限未授予

  • 错误:SecurityException: App does not have permission to record audio
  • 解决方法:在运行时动态申请权限。

3. 音频数据为零

  • 原因:
    • 应用未获得录音权限。
    • 系统通过音频策略屏蔽了音频输入(如麦克风被禁用)。
  • 解决方法:确认权限状态并确保麦克风功能可用。

七、隐私保护和敏感权限政策

Android 系统对 RECORD_AUDIO 权限有严格的隐私保护措施:

  1. 权限提示
    • 当应用申请录音权限时,系统会提示用户,该权限可访问麦克风捕获环境声音。
  2. 录音指示器
    • 从 Android 12(API 31)开始,系统在屏幕顶部会显示绿色的麦克风使用指示器,提醒用户某些应用正在访问麦克风。
  3. 权限开关
    • 用户可以在系统设置中随时禁用麦克风权限。

八、系统管控动态权限

JAVA 代码示例

    public void grantRecordAudioPermission(String packageName) {
        try {
            String permission = android.Manifest.permission.RECORD_AUDIO;
            // 使用 PackageManager 授予权限
            mContext.getPackageManager().grantRuntimePermission(packageName, permission, UserHandle.SYSTEM);
    
            // 验证权限是否成功授予
            int permissionStatus = mContext.getPackageManager().checkPermission(permission, packageName);
            if (permissionStatus == PackageManager.PERMISSION_GRANTED) {
                Log.d("PermissionControl", "录音权限已授予给: " + packageName);
            } else {
                Log.e("PermissionControl", "录音权限授予失败!");
            }
        } catch (Exception e) {
            Log.e("PermissionControl", "权限授予时出错: " + e.getMessage());
        }
    }

    public void revokeRecordAudioPermission(String packageName) {
        try {
            String permission = android.Manifest.permission.RECORD_AUDIO;
            // 使用 PackageManager 撤销权限
            mContext.getPackageManager().revokeRuntimePermission(packageName, permission, UserHandle.SYSTEM);
    
            // 验证权限是否成功撤销
            int permissionStatus = mContext.getPackageManager().checkPermission(permission, packageName);
            if (permissionStatus == PackageManager.PERMISSION_DENIED) {
                Log.d("PermissionControl", "录音权限已撤销: " + packageName);
            } else {
                Log.e("PermissionControl", "录音权限撤销失败!");
            }
        } catch (Exception e) {
            Log.e("PermissionControl", "权限撤销时出错: " + e.getMessage());
        }
    }

    String packageName = "com.example.audiolp";
    grantRecordAudioPermission(packageName);
    revokeRecordAudioPermission(packageName);

在上例中,com.example.audiolp 应用没有录音权限的时候,在创建录音器的时候会报错。如下:

12-27 17:24:44.226   546   823 E ServiceUtilities: Request requires android.permission.RECORD_AUDIO
12-27 17:24:44.226   546   823 E AudioPolicyIntefaceImpl: getInputForAttr permission denied: recording not allowed for uid 10035 pid 12765
12-27 17:24:44.226   546   823 E AudioFlinger: createRecord() getInputForAttr return error -1

具体代码位置在 frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp 文件中 AudioPolicyService::getInputForAttr()函数中, recordingAllowed()处会报错,从而启动录音失败。

应用在成功录音之后,再把权限关闭revokeRecordAudioPermission, audioserver会收到状态通知,设置 setRecordSilenced(portId, silenced),并停止录音,。如下:

12-27 17:51:07.888   546   823 I AF::OpRecordAudioMonitor: OP_RECORD_AUDIO missing, silencing record uid10035 pack:com.example.audiolp
12-27 17:27:27.276   546  1524 E AudioPolicyService: audioflinger2 updateUidStates_l allowCapture=0
12-27 17:27:27.276   546  1524 V APM_AudioPolicyManager: setAppState(portId:93, state:0)
12-27 17:27:27.328   546   822 V APM_AudioPolicyManager: stopInput portId 93
12-27 17:27:27.333   546   822 V APM_AudioPolicyManager: resetInputDevice() releaseAudioPatch returned 0
12-27 17:27:27.334   546   822 V APM_AudioPolicyManager: releaseInput portId 93
12-27 17:27:27.334   546   822 V APM_AudioPolicyManager: releaseInput 118
12-27 17:27:27.334   546   822 V APM_AudioPolicyManager: closeInput(118)
12-27 17:27:27.539   546   822 V APM_AudioPolicyManager: releaseInput exit

具体代码位置在 frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp 文件中 AudioPolicyService::updateUidStates_l()函数中, AudioPolicyService::setAppState_l()

adb查看应用权限

adb shell dumpsys package com.example.audiolp
adb shell dumpsys package com.chinatsp.audiolp |grep RECORD_AUDIO