Android 蓝牙无法发送或接收某些类型文件

Android 蓝牙应用使得用户能够在蓝牙设备之间进行文件传输。用户可以通过蓝牙连接两台设备,并在它们之间传输文件,如照片、音乐、视频等。这对于用户来说是非常便利的,无需使用数据线或互联网连接,可以直接在附近的设备之间进行文件共享。

文章目录

  • [1. 蓝牙无法发送某些类型文件](#1. 蓝牙无法发送某些类型文件)
  • [2. 蓝牙无法接收某些类型文件](#2. 蓝牙无法接收某些类型文件)
  • [3. 蓝牙权限问题](#3. 蓝牙权限问题)

从 Andorid 13 后蓝牙模块位置从 packages/apps/Bluetooth/变成了 packages/modules/Bluetooth

bash 复制代码
server@dev-fj-srv:/work/AndrodU/packages/modules/Bluetooth/android/app$ tree -L 1
.
|-- Android.bp
|-- AndroidManifest.xml
|-- OWNERS
|-- app.iml
|-- certs
|-- jni
|-- lib
|-- proguard.flags
|-- res
|-- services
|-- src
|-- tests
`-- tools

8 directories, 5 files

1. 蓝牙无法发送某些类型文件

原生蓝牙的ACTION_SEND_MULTIPLEACTION_SEND二种action分享类型做了限制,所以会导致部分类型的文件无法通过蓝牙分享,为了解除这种限制,可以将数据类型设置成*/*,即<data android:mimeType="*/*" />

packages/modules/Bluetooth/android/app/AndroidManifest.xml

xml 复制代码
<activity android:name="com.android.bluetooth.opp.BluetoothOppLauncherActivity"
     android:process="@string/process"
     android:theme="@style/opp_launcher_activity"
     android:label="@string/bt_share_picker_label"
     android:enabled="false"
     android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <!-- @{ support sending all types of files.
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
        <data android:mimeType="audio/*"/>
        <data android:mimeType="text/x-vcard"/>
        <data android:mimeType="text/x-vcalendar"/>
        <data android:mimeType="text/calendar"/>
        <data android:mimeType="text/plain"/>
        <data android:mimeType="text/html"/>
        <data android:mimeType="text/xml"/>
        <data android:mimeType="application/zip"/>
        <data android:mimeType="application/vnd.ms-excel"/>
        <data android:mimeType="application/msword"/>
        <data android:mimeType="application/vnd.ms-powerpoint"/>
        <data android:mimeType="application/pdf"/>
        <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/>
        <data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document"/>
        <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation"/>
        <data android:mimeType="application/x-hwp"/>
        -->
        <data android:mimeType="*/*" />
        <!-- @} -->
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <!-- @{ support sending all types of files.
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
        <data android:mimeType="x-mixmedia/*"/>
        <data android:mimeType="text/x-vcard"/>
        -->
        <data android:mimeType="*/*" />
        <!-- @} -->
    </intent-filter>
    <intent-filter>
        <action android:name="android.btopp.intent.action.OPEN"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="vnd.android.cursor.item/vnd.android.btopp"/>
    </intent-filter>
</activity>

2. 蓝牙无法接收某些类型文件

log 报错分析

bash 复制代码
04-20 19:27:10.928 23480 12218 D BtOppObexClient: Start!
04-20 19:27:11.031 23480 12224 D BtOppObexClient: Create ClientSession with transport com.android.bluetooth.BluetoothObexTransport@c894e88
04-20 19:27:11.227 23480 12224 D BtOppObexClient: OBEX session created
04-20 19:27:11.808 23480 12224 I BtOppObexClient: Remote reject, Response code is 207
04-20 19:27:11.810 23480 12224 I BtOppObexClient: Remote reject file type application/vnd.android.package-archive
04-20 19:27:11.810 23480 12224 I BtOppObexClient: Response error code is 207
04-20 19:27:11.824 23480 12224 D BtOppObexClient: Client thread waiting for next share, sleep for 500
04-20 19:27:11.825 23480 12218 D BtOppObexClient: Stop!
04-20 19:27:11.917 23480 12224 D BtOppObexClient: OBEX session disconnected
04-20 19:27:12.932 23480 12231 D BtOppObexClient: Stop!

BtOppObexClient: Remote reject file type application/vnd.android.package-archive 根据log信息找到代码位置:src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java

java 复制代码
@VisibleForTesting
int sendFile(BluetoothOppSendFileInfo fileInfo) {
...
		} else if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
		    Log.i(TAG, "Remote reject file type " + fileInfo.mMimetype);
		    status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
		} 
...
}

src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java 中的 public int onPut(Operation op)

java 复制代码
// Reject anything outside the "acceptlist" plus unspecified MIME Types.
if (mimeType == null || (!isAcceptlisted && !Constants.mimeTypeMatches(mimeType,
        Constants.ACCEPTABLE_SHARE_INBOUND_TYPES))) {
    if (D) {
        Log.w(TAG, "mimeType is null or in unacceptable list, reject the transfer");
    }
    return ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE;
}

此处定义了常量Constants.ACCEPTABLE_SHARE_INBOUND_TYPES,来过滤可接收的文件类型的mimeType

java 复制代码
/**
 * The MIME type(s) of we could accept from other device.
 * This is in essence a "acceptlist" of acceptable types.
 * Today, restricted to images, audio, video and certain text types.
 */
static final String[] ACCEPTABLE_SHARE_INBOUND_TYPES = new String[]{
        "image/*",
        "video/*",
        "audio/*",
        "text/x-vcard",
        "text/x-vcalendar",
        "text/calendar",
        "text/plain",
        "text/html",
        "text/xml",
        "application/epub+zip",
        "application/zip",
        "application/vnd.ms-excel",
        "application/msword",
        "application/vnd.ms-powerpoint",
        "application/pdf",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "application/x-hwp",
};

因此,如果需要接收所有文件类型,可以修改此ACCEPTABLE_SHARE_INBOUND_TYPES

java 复制代码
static final String[] ACCEPTABLE_SHARE_INBOUND_TYPES = new String[]{
	"*/*"
};

3. 蓝牙权限问题

部分应用的图片无法通过蓝牙分享,蓝牙通知显示文件传输失败:未知文件,无法正确处理请求。log输出报错如下:

bash 复制代码
12-02 02:12:22.290  5046  6302 E DatabaseUtils: Writing exception to parcel
12-02 02:12:22.290  5046  6302 E DatabaseUtils: java.lang.SecurityException: com.android.bluetooth has no access to content://media/external/images/media/1000000037
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.enforceCallingPermissionInternal(MediaProvider.java:10014)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.enforceCallingPermission(MediaProvider.java:9911)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.checkAccess(MediaProvider.java:10035)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.openFileAndEnforcePathPermissionsHelper(MediaProvider.java:8294)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.openFileCommon(MediaProvider.java:7901)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.openTypedAssetFileCommon(MediaProvider.java:7969)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at com.android.providers.media.MediaProvider.openTypedAssetFile(MediaProvider.java:7913)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:562)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:327)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at android.os.Binder.execTransactInternal(Binder.java:1285)
12-02 02:12:22.290  5046  6302 E DatabaseUtils: 	at android.os.Binder.execTransact(Binder.java:1244)

log表明蓝牙无法获取此媒体文件,缺少权限,由于Android 13后新增了媒体文件的细分权限,蓝牙的AndroidManifest.xml中没有添加这些细分权限,遇到的images文件出现的此问题,添加READ_MEDIA_IMAGES权限后测试成功。

xml 复制代码
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

STATUS_OBEX_DATA_ERROR:由于在 OBEX 级别接收或处理数据时出现错误,因此无法完成此传输。

java 复制代码
/**
 * Get status description according to status code.
 */
public static String getStatusDescription(Context context, int statusCode, String deviceName) {
    String ret;
    if (statusCode == BluetoothShare.STATUS_PENDING) {
        ret = context.getString(R.string.status_pending);
    } else if (statusCode == BluetoothShare.STATUS_RUNNING) {
        ret = context.getString(R.string.status_running);
    } else if (statusCode == BluetoothShare.STATUS_SUCCESS) {
        ret = context.getString(R.string.status_success);
    } else if (statusCode == BluetoothShare.STATUS_NOT_ACCEPTABLE) {
        ret = context.getString(R.string.status_not_accept);
    } else if (statusCode == BluetoothShare.STATUS_FORBIDDEN) {
        ret = context.getString(R.string.status_forbidden);
    } else if (statusCode == BluetoothShare.STATUS_CANCELED) {
        ret = context.getString(R.string.status_canceled);
    } else if (statusCode == BluetoothShare.STATUS_FILE_ERROR) {
        ret = context.getString(R.string.status_file_error);
    } else if (statusCode == BluetoothShare.STATUS_ERROR_NO_SDCARD) {
        int id = deviceHasNoSdCard()
                ? R.string.status_no_sd_card_nosdcard
                : R.string.status_no_sd_card_default;
        ret = context.getString(id);
    } else if (statusCode == BluetoothShare.STATUS_CONNECTION_ERROR) {
        ret = context.getString(R.string.status_connection_error);
    } else if (statusCode == BluetoothShare.STATUS_ERROR_SDCARD_FULL) {
        int id = deviceHasNoSdCard() ? R.string.bt_sm_2_1_nosdcard : R.string.bt_sm_2_1_default;
        ret = context.getString(id);
    } else if ((statusCode == BluetoothShare.STATUS_BAD_REQUEST) || (statusCode
            == BluetoothShare.STATUS_LENGTH_REQUIRED) || (statusCode
            == BluetoothShare.STATUS_PRECONDITION_FAILED) || (statusCode
            == BluetoothShare.STATUS_UNHANDLED_OBEX_CODE) || (statusCode
            == BluetoothShare.STATUS_OBEX_DATA_ERROR)) {
        ret = context.getString(R.string.status_protocol_error);
    } else {
        ret = context.getString(R.string.status_unknown_error);
    }
    return ret;
}

无法查询到传输的文件信息,所以显示为未知文件。

java 复制代码
if (info.mFileName == null) {
    info.mFileName = context.getString(R.string.unknown_file);
}
相关推荐
雨白1 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹3 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空5 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭5 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日6 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安6 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑6 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟10 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡12 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0012 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体