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);
}
相关推荐
太空漫步112 小时前
android社畜模拟器
android
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
凯文的内存6 小时前
android 定制mtp连接外设的设备名称
android·media·mtp·mtpserver
天若子6 小时前
Android今日头条的屏幕适配方案
android
林的快手8 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
望佑8 小时前
Tmp detached view should be removed from RecyclerView before it can be recycled
android
xvch10 小时前
Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符
android·kotlin
人民的石头14 小时前
Android系统开发 给system/app传包报错
android
yujunlong391914 小时前
android,flutter 混合开发,通信,传参
android·flutter·混合开发·enginegroup
rkmhr_sef15 小时前
万字详解 MySQL MGR 高可用集群搭建
android·mysql·adb