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_MULTIPLE
和ACTION_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);
}