Merge "Allocate CallbacksObj Before Initializing A2DP JNI Interface"
am: b1b9ae5d4b
Change-Id: I2f4cb3851fd7941bb4192aa6eded60403d67201d
diff --git a/Android.mk b/Android.mk
index 09cbaf4..65f08b6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,8 +22,8 @@
LOCAL_CERTIFICATE := platform
LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
-LOCAL_JAVA_LIBRARIES := javax.obex telephony-common libprotobuf-java-micro
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard bluetooth.mapsapi sap-api-java-static android-support-v4
+LOCAL_JAVA_LIBRARIES := javax.obex telephony-common libprotobuf-java-micro services.net
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard bluetooth.mapsapi sap-api-java-static android-support-v4 services.net
LOCAL_PROTOC_OPTIMIZE_TYPE := micro
LOCAL_REQUIRED_MODULES := bluetooth.default
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ea6e39b..0c71e21 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -30,9 +30,9 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
- <!-- WRITE_CONTACTS is used for test cases only -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.NFC_HANDOVER_STATUS" />
@@ -45,11 +45,13 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<uses-permission android:name="android.permission.MANAGE_USERS"/>
+ <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="com.google.android.gallery3d.permission.GALLERY_PROVIDER"/>
<uses-permission android:name="com.android.gallery3d.permission.GALLERY_PROVIDER"/>
<uses-permission android:name="android.permission.RECEIVE_SMS" />
@@ -74,7 +76,9 @@
android:persistent="false"
android:label="@string/app_name"
android:supportsRtl="true"
- android:usesCleartextTraffic="false">
+ android:usesCleartextTraffic="false"
+ android:directBootAware="true"
+ android:defaultToDeviceProtectedStorage="true">
<uses-library android:name="javax.obex" />
<provider android:name=".opp.BluetoothOppProvider"
android:authorities="com.android.bluetooth.opp"
@@ -295,7 +299,7 @@
</service>
<service
android:process="@string/process"
- android:name = ".a2dp.A2dpSinkService"
+ android:name = ".a2dpsink.A2dpSinkService"
android:enabled="@bool/profile_supported_a2dp_sink">
<intent-filter>
<action android:name="android.bluetooth.IBluetoothA2dpSink" />
@@ -303,6 +307,15 @@
</service>
<service
android:process="@string/process"
+ android:name=".a2dpsink.mbs.A2dpMediaBrowserService"
+ android:exported="true"
+ android:enabled="@bool/profile_supported_a2dp_sink">
+ <intent-filter>
+ <action android:name="android.media.browse.MediaBrowserService" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
android:name = ".avrcp.AvrcpControllerService"
android:enabled="@bool/profile_supported_avrcp_controller">
<intent-filter>
@@ -333,7 +346,7 @@
<action android:name="android.bluetooth.IBluetoothPan" />
</intent-filter>
</service>
- <service
+ <service
android:process="@string/process"
android:name = ".hfpclient.HeadsetClientService"
android:enabled="@bool/profile_supported_hfpclient">
@@ -341,5 +354,36 @@
<action android:name="android.bluetooth.IBluetoothHeadsetClient" />
</intent-filter>
</service>
+ <service
+ android:process="@string/process"
+ android:name=".hfpclient.connserv.HfpClientConnectionService"
+ android:permission="android.permission.BIND_CONNECTION_SERVICE"
+ android:enabled="@bool/hfp_client_connection_service_enabled">
+ <intent-filter>
+ <!-- Mechanism for Telecom stack to connect -->
+ <action android:name="android.telecom.ConnectionService" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".pbapclient.PbapClientService"
+ android:enabled="@bool/profile_supported_pbapclient">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothPbapClient" />
+ </intent-filter>
+ </service>
+ <!-- Authenticator for PBAP account. -->
+ <service
+ android:process="@string/process"
+ android:name=".AuthenticationService"
+ android:exported="true"
+ android:enabled="@bool/profile_supported_pbapclient">
+ <intent-filter>
+ <action android:name="android.accounts.AccountAuthenticator" />
+ </intent-filter>
+ <meta-data
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/authenticator" />
+ </service>
</application>
</manifest>
diff --git a/AndroidManifest_test.xml b/AndroidManifest_test.xml
index ae21b88..8529ba5 100644
--- a/AndroidManifest_test.xml
+++ b/AndroidManifest_test.xml
@@ -46,7 +46,7 @@
</receiver>
<activity android:name=".opp.BluetoothOppLauncherActivity"
android:process="@string/process"
- android:theme="@android:style/Theme.Material.Light.Dialog"
+ android:theme="@*android:style/Theme.Material.DayNight.Dialog"
android:label="@string/bt_share_picker_label">
<intent-filter>
<action android:name="android.intent.action.SEND" />
@@ -76,7 +76,7 @@
</activity>
<activity android:name=".opp.BluetoothOppBtEnablingActivity"
android:process="@string/process"
- android:theme="@android:style/Theme.Material.Light.Dialog">
+ android:theme="@*android:style/Theme.Material.DayNight.Dialog">
</activity>
<activity android:name=".opp.BluetoothOppIncomingFileConfirmActivity"
android:process="@string/process">
@@ -87,7 +87,7 @@
<activity android:name=".pbap.BluetoothPbapActivity"
android:process="@string/process"
android:label=" "
- android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
+ android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
diff --git a/jni/com_android_bluetooth_a2dp.cpp b/jni/com_android_bluetooth_a2dp.cpp
index 0323eb1..2b4cb56 100644
--- a/jni/com_android_bluetooth_a2dp.cpp
+++ b/jni/com_android_bluetooth_a2dp.cpp
@@ -98,7 +98,6 @@
bta2dp_connection_state_callback,
bta2dp_audio_state_callback,
NULL, /* audio_config_cb */
- NULL /* audio_focus_request_cb */
};
static void classInitNative(JNIEnv* env, jclass clazz) {
diff --git a/jni/com_android_bluetooth_a2dp_sink.cpp b/jni/com_android_bluetooth_a2dp_sink.cpp
index f72ed7e..82110d3 100644
--- a/jni/com_android_bluetooth_a2dp_sink.cpp
+++ b/jni/com_android_bluetooth_a2dp_sink.cpp
@@ -29,7 +29,6 @@
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onAudioConfigChanged;
-static jmethodID method_onAudioFocusRequested;
static const btav_interface_t *sBluetoothA2dpInterface = NULL;
static jobject mCallbacksObj = NULL;
@@ -117,34 +116,11 @@
sCallbackEnv->DeleteLocalRef(addr);
}
-static void bta2dp_audio_focus_request_callback(bt_bdaddr_t *bd_addr) {
- jbyteArray addr;
-
- ALOGI("%s", __FUNCTION__);
-
- if (!checkCallbackThread()) { \
- ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
- return; \
- }
- addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
- if (!addr) {
- ALOGE("Fail to new jbyteArray bd addr for connection state");
- checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
- return;
- }
-
- sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioFocusRequested, addr);
- checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
- sCallbackEnv->DeleteLocalRef(addr);
-}
-
static btav_callbacks_t sBluetoothA2dpCallbacks = {
sizeof(sBluetoothA2dpCallbacks),
bta2dp_connection_state_callback,
bta2dp_audio_state_callback,
bta2dp_audio_config_callback,
- bta2dp_audio_focus_request_callback
};
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -157,9 +133,6 @@
method_onAudioConfigChanged =
env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
- method_onAudioFocusRequested =
- env->GetMethodID(clazz, "onAudioFocusRequested", "([B)V");
-
ALOGI("%s: succeeds", __FUNCTION__);
}
@@ -263,8 +236,15 @@
if (!sBluetoothA2dpInterface) return;
sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state);
+}
+
+static void informAudioTrackGainNative(JNIEnv *env, jobject object, jfloat gain) {
+ if (!sBluetoothA2dpInterface) return;
+
+ sBluetoothA2dpInterface->set_audio_track_gain((float) gain);
}
+
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
{"initNative", "()V", (void *) initNative},
@@ -272,11 +252,12 @@
{"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
{"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
{"informAudioFocusStateNative", "(I)V", (void *) informAudioFocusStateNative},
+ {"informAudioTrackGainNative", "(F)V", (void *) informAudioTrackGainNative},
};
int register_com_android_bluetooth_a2dp_sink(JNIEnv* env)
{
- return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpSinkStateMachine",
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dpsink/A2dpSinkStateMachine",
sMethods, NELEM(sMethods));
}
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 5f5581e..bba1ba4 100755
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -50,6 +50,11 @@
static jmethodID method_releaseWakeLock;
static jmethodID method_energyInfo;
+static struct {
+ jclass clazz;
+ jmethodID constructor;
+} android_bluetooth_UidTraffic;
+
static const bt_interface_t *sBluetoothInterface = NULL;
static const btsock_interface_t *sBluetoothSocketInterface = NULL;
static JNIEnv *callbackEnv = NULL;
@@ -451,18 +456,35 @@
}
static void energy_info_recv_callback(bt_activity_energy_info *p_energy_info,
- bt_uid_traffic_t* /* uid_data */)
+ bt_uid_traffic_t* uid_data)
{
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
+ jsize len = 0;
+ for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) {
+ len++;
+ }
+
+ jobjectArray array = callbackEnv->NewObjectArray(len, android_bluetooth_UidTraffic.clazz, NULL);
+ jsize i = 0;
+ for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) {
+ jobject uidObj = callbackEnv->NewObject(android_bluetooth_UidTraffic.clazz,
+ android_bluetooth_UidTraffic.constructor,
+ (jint) data->app_uid, (jlong) data->rx_bytes,
+ (jlong) data->tx_bytes);
+ callbackEnv->SetObjectArrayElement(array, i++, uidObj);
+ callbackEnv->DeleteLocalRef(uidObj);
+ }
+
callbackEnv->CallVoidMethod(sJniAdapterServiceObj, method_energyInfo, p_energy_info->status,
p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time,
- p_energy_info->idle_time, p_energy_info->energy_used);
+ p_energy_info->idle_time, p_energy_info->energy_used, array);
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
+ callbackEnv->DeleteLocalRef(array);
}
static bt_callbacks_t sBluetoothCallbacks = {
@@ -615,6 +637,11 @@
int err;
hw_module_t* module;
+
+ jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic");
+ android_bluetooth_UidTraffic.constructor = env->GetMethodID(jniUidTrafficClass,
+ "<init>", "(IJJ)V");
+
jclass jniCallbackClass =
env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
sJniCallbacksField = env->GetFieldID(clazz, "mJniCallbacks",
@@ -646,7 +673,7 @@
method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z");
method_acquireWakeLock = env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z");
method_releaseWakeLock = env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z");
- method_energyInfo = env->GetMethodID(clazz, "energyInfoCallback", "(IIJJJJ)V");
+ method_energyInfo = env->GetMethodID(clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V");
char value[PROPERTY_VALUE_MAX];
property_get("bluetooth.mock_stack", value, "");
@@ -672,6 +699,9 @@
static bool initNative(JNIEnv* env, jobject obj) {
ALOGV("%s:",__FUNCTION__);
+ android_bluetooth_UidTraffic.clazz = (jclass) env->NewGlobalRef(
+ env->FindClass("android/bluetooth/UidTraffic"));
+
sJniAdapterServiceObj = env->NewGlobalRef(obj);
sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
@@ -711,6 +741,8 @@
env->DeleteGlobalRef(sJniCallbacksObj);
env->DeleteGlobalRef(sJniAdapterServiceObj);
+ env->DeleteGlobalRef(android_bluetooth_UidTraffic.clazz);
+ android_bluetooth_UidTraffic.clazz = NULL;
return JNI_TRUE;
}
@@ -1252,6 +1284,27 @@
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
+static void interopDatabaseClearNative(JNIEnv *env, jobject obj) {
+ ALOGV("%s()", __FUNCTION__);
+ if (!sBluetoothInterface) return;
+ sBluetoothInterface->interop_database_clear();
+}
+
+static void interopDatabaseAddNative(JNIEnv *env, jobject obj, int feature,
+ jbyteArray address, int length) {
+ ALOGV("%s()", __FUNCTION__);
+ if (!sBluetoothInterface) return;
+
+ jbyte *addr = env->GetByteArrayElements(address, NULL);
+ if (addr == NULL) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+
+ sBluetoothInterface->interop_database_add(feature, (bt_bdaddr_t *)addr, length);
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void *) classInitNative},
@@ -1281,7 +1334,9 @@
{"alarmFiredNative", "()V", (void *) alarmFiredNative},
{"readEnergyInfo", "()I", (void*) readEnergyInfo},
{"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V", (void*) dumpNative},
- {"factoryResetNative", "()Z", (void*)factoryResetNative}
+ {"factoryResetNative", "()Z", (void*)factoryResetNative},
+ {"interopDatabaseClearNative", "()V", (void*) interopDatabaseClearNative},
+ {"interopDatabaseAddNative", "(I[BI)V", (void*) interopDatabaseAddNative}
};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env)
diff --git a/res/drawable-hdpi/bt_incomming_file_notification.png b/res/drawable-hdpi/bt_incomming_file_notification.png
deleted file mode 100644
index ff72837..0000000
--- a/res/drawable-hdpi/bt_incomming_file_notification.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bt_incomming_file_notification.png b/res/drawable-mdpi/bt_incomming_file_notification.png
deleted file mode 100644
index d53e0eb..0000000
--- a/res/drawable-mdpi/bt_incomming_file_notification.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/bt_incomming_file_notification.png b/res/drawable-xhdpi/bt_incomming_file_notification.png
deleted file mode 100644
index 3762470..0000000
--- a/res/drawable-xhdpi/bt_incomming_file_notification.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bt_incomming_file_notification.xml b/res/drawable/bt_incomming_file_notification.xml
new file mode 100644
index 0000000..ed45b3b
--- /dev/null
+++ b/res/drawable/bt_incomming_file_notification.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M13.5 8.1l-6.2-6.1v8.9l-3.8-3.5-.9 1 4 3.6-3.9 3.7.9 1 3.8-3.5v8.8l6.2-6.1-3.9-4.3 3.8-3.5zm-4.5-2.6l2.6 2.6-2.6 2.2v-4.8zm2.6 10.4l-2.6 2.6v-5.5l2.6 2.9zm5.6-7.5h-1.7l2.7-3.6 2.7 3.6h-1.7v2.7h-2v-2.7zm0 4.6h2v2.7h1.7l-2.7 3.6-2.7-3.6h1.7v-2.7z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/res/drawable/ic_accept.xml b/res/drawable/ic_accept.xml
new file mode 100644
index 0000000..5ed8b4e
--- /dev/null
+++ b/res/drawable/ic_accept.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/res/drawable/ic_decline.xml b/res/drawable/ic_decline.xml
new file mode 100644
index 0000000..6c7d83a
--- /dev/null
+++ b/res/drawable/ic_decline.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 98b1118..85a9942 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aanvaar"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Daar was \'n uittelling terwyl \'n inkomende lêer van \"<xliff:g id="SENDER">%1$s</xliff:g>\" aanvaar is"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-deel: Inkomende lêer"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Wil jy hierdie lêer ontvang?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Inkomende lêer vanaf \'n ander toestel. Bevestig dat jy hierdie lêer wil ontvang."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Inkomende lêer"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> is gereed om <xliff:g id="FILE">%2$s</xliff:g> te stuur"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-deling: Ontvang <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-deling: Het \"<xliff:g id="FILE">%1$s</xliff:g>\" ontvang"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-deling: Lêer <xliff:g id="FILE">%1$s</xliff:g> nie ontvang nie"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Programikoon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth-boodskapdeelinstellings"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Kan nie rekening kies nie. 0 gleuwe oor"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-oudio is gekoppel"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-oudio is ontkoppel"</string>
</resources>
diff --git a/res/values-af/strings_pbap_client.xml b/res/values-af/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-af/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index c5a7501..1f5c9c4 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ተቀበል"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"እሺ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"ከ \"<xliff:g id="SENDER">%1$s</xliff:g>\" ገቢ መልዕክት ፋይል እየተቀበለ ሳለ ጊዜ አልቆ ነበር።"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"የብሉቱዝ መጋሪያ፡ ገቢ ፋይል"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ይህን ፋይል መቀበል ይፈልጋሉ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ከሌላ መሣሪያ የሚገባ ፋይል አለ፣ ይህን ፋይል ለመቀበል መፈለግዎን ያረጋግጡ።"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ገቢ ፋይል"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g>ን ለመላክ ዝግጁ ነው"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ብሉቱዝ ማጋሪያ፡ <xliff:g id="FILE">%1$s</xliff:g> እየተቀበለ"</string>
<string name="notification_received" msgid="3324588019186687985">"ብሉቱዝ ማጋሪያ፡ <xliff:g id="FILE">%1$s</xliff:g> ደርሷል"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ብሉቱዝ ማጋሪያ፡ ፋይል<xliff:g id="FILE">%1$s</xliff:g> አልደረሰም"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"የመተግበሪያ አዶ"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"የብሉቱዝ የመልዕክት ማጋሪያ ቅንብሮች"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"መለያን መምረጥ አይቻልም። 0 መክተቻዎች ይቀራሉ"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"የብሉቱዝ ኦዲዮ ተገናኝቷል"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"የብሉቱዝ ኦዲዮ ግንኙነት ተቋርጧል"</string>
</resources>
diff --git a/res/values-am/strings_pbap_client.xml b/res/values-am/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-am/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index c5e4e3a..6d78bc6 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"قبول"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"موافق"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"انتهت المهلة أثناء قبول ملف وراد من \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"مشاركة البلوتوث: ملف وارد"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"هل تريد تلقي هذا الملف؟"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"هناك ملف وارد من جهاز آخر. عليك تأكيد أنك تريد استلام هذا الملف."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ملف وارد"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> جاهز لإرسال <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"مشاركة البلوتوث: يتم استلام <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"مشاركة البلوتوث: تم استلام <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"مشاركة البلوتوث: لم يتم استلام الملف <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -136,5 +135,7 @@
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"المنافذ المتبقية:"</string>
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"رمز التطبيق"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"إعدادات مشاركة الرسائل عبر بلوتوث"</string>
- <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"يتعذر تحديد الحساب. عدد المنافذ المتبقية: 0"</string>
+ <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"يتعذر تحديد الحساب. عدد المنافذ المتبقية: ۰"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"تم الاتصال بالبث الصوتي عبر البلوتوث."</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"انقطع الاتصال بالبث الصوتي عبر البلوتوث."</string>
</resources>
diff --git a/res/values-ar/strings_pbap_client.xml b/res/values-ar/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ar/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index ddca25f..facad2f 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Qəbul edirəm"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" adlı istifadəçidən gələn faylı qəbul edərkən gecikmə baş verdi"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth paylaşım: Gələn fayl"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Bu faylı qəbul etmək istəyirsiniz?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Başqa cihazdan fayl gəlir. Qəbul etmə istəyinizi təsdiq edin."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Gələn fayl"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> faylını göndərməyə hazırdır"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth paylaşım: <xliff:g id="FILE">%1$s</xliff:g> qəbul edilir"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth paylaşım: <xliff:g id="FILE">%1$s</xliff:g> qəbul edildi"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth paylaşım: <xliff:g id="FILE">%1$s</xliff:g> faylı qəbul edilmədi"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Tətbiq ikonası"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth Mesaj Paylaşma Ayarları"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Hesab seçmək olmur. 0 slot qalıb"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth audio bağlantısı yaradıldı"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio ilə bağlantı kəsildi"</string>
</resources>
diff --git a/res/values-az-rAZ/strings_pbap_client.xml b/res/values-az-rAZ/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-az-rAZ/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..9c2de53
--- /dev/null
+++ b/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Pristup menadžeru preuzimanja."</string>
+ <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Omogućava aplikaciji da pristupa menadžeru za deljenje preko Bluetooth-a i da ga koristi za prenos datoteka."</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Pristup Bluetooth uređaja sa bele liste."</string>
+ <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Dozvoljava aplikaciji da privremeno stavi Bluetooth uređaj na belu listu, što omogućava tom uređaju da šalje datoteke ovom uređaju bez potvrde korisnika."</string>
+ <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+ <string name="unknown_device" msgid="9221903979877041009">"Nepoznati uređaj"</string>
+ <string name="unknownNumber" msgid="4994750948072751566">"Nepoznato"</string>
+ <string name="airplane_error_title" msgid="2683839635115739939">"Režim rada u avionu"</string>
+ <string name="airplane_error_msg" msgid="8698965595254137230">"Ne možete da koristite Bluetooth u režimu rada u avionu."</string>
+ <string name="bt_enable_title" msgid="8657832550503456572"></string>
+ <string name="bt_enable_line1" msgid="7203551583048149">"Da biste mogli da koristite Bluetooth usluge, najpre morate da uključite Bluetooth."</string>
+ <string name="bt_enable_line2" msgid="4341936569415937994">"Želite li odmah da uključite Bluetooth?"</string>
+ <string name="bt_enable_cancel" msgid="1988832367505151727">"Otkaži"</string>
+ <string name="bt_enable_ok" msgid="3432462749994538265">"Uključi"</string>
+ <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Prenos datoteke"</string>
+ <string name="incoming_file_confirm_content" msgid="2752605552743148036">"Želite li da prihvatite dolaznu datoteku?"</string>
+ <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Odbij"</string>
+ <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Prihvati"</string>
+ <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Potvrdi"</string>
+ <string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Došlo je do vremenskog ograničenja tokom prijema dolazne datoteke od „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Dolazna datoteka"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> je spreman/na da pošalje <xliff:g id="FILE">%2$s</xliff:g>"</string>
+ <string name="notification_receiving" msgid="4674648179652543984">"Bluetooth deljenje: prijem <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_received" msgid="3324588019186687985">"Bluetooth deljenje: primljeno <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth deljenje: datoteka <xliff:g id="FILE">%1$s</xliff:g> nije primljena"</string>
+ <string name="notification_sending" msgid="3035748958534983833">"Bluetooth deljenje: slanje <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_sent" msgid="9218710861333027778">"Bluetooth deljenje: poslato <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_sent_complete" msgid="302943281067557969">"Dovršeno je 100%"</string>
+ <string name="notification_sent_fail" msgid="6696082233774569445">"Bluetooth deljenje: datoteka <xliff:g id="FILE">%1$s</xliff:g> nije poslata"</string>
+ <string name="download_title" msgid="3353228219772092586">"Prenos datoteke"</string>
+ <string name="download_line1" msgid="4926604799202134144">"Od: „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
+ <string name="download_line2" msgid="5876973543019417712">"Datoteka: <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="download_line3" msgid="4384821622908676061">"Veličina datoteke: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+ <string name="download_line4" msgid="8535996869722666525"></string>
+ <string name="download_line5" msgid="3069560415845295386">"Primanje datoteke..."</string>
+ <string name="download_cancel" msgid="9177305996747500768">"Zaustavi"</string>
+ <string name="download_ok" msgid="5000360731674466039">"Sakrij"</string>
+ <string name="incoming_line1" msgid="2127419875681087545">"Od"</string>
+ <string name="incoming_line2" msgid="3348994249285315873">"Naziv datoteke"</string>
+ <string name="incoming_line3" msgid="7954237069667474024">"Veličina"</string>
+ <string name="download_fail_line1" msgid="3846450148862894552">"Datoteka nije primljena"</string>
+ <string name="download_fail_line2" msgid="8950394574689971071">"Datoteka: <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="download_fail_line3" msgid="3451040656154861722">"Razlog: <xliff:g id="REASON">%1$s</xliff:g>"</string>
+ <string name="download_fail_ok" msgid="1521733664438320300">"Potvrdi"</string>
+ <string name="download_succ_line5" msgid="4509944688281573595">"Datoteka je primljena"</string>
+ <string name="download_succ_ok" msgid="7053688246357050216">"Otvori"</string>
+ <string name="upload_line1" msgid="2055952074059709052">"Kome: „<xliff:g id="RECIPIENT">%1$s</xliff:g>“"</string>
+ <string name="upload_line3" msgid="4920689672457037437">"Tip datoteke: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
+ <string name="upload_line5" msgid="7759322537674229752">"Slanje datoteke..."</string>
+ <string name="upload_succ_line5" msgid="5687317197463383601">"Datoteka je poslata"</string>
+ <string name="upload_succ_ok" msgid="7705428476405478828">"Potvrdi"</string>
+ <string name="upload_fail_line1" msgid="7899394672421491701">"Datoteka nije poslata na <xliff:g id="RECIPIENT">%1$s</xliff:g>."</string>
+ <string name="upload_fail_line1_2" msgid="2108129204050841798">"Datoteka: <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="upload_fail_ok" msgid="5807702461606714296">"Pokušaj ponovo"</string>
+ <string name="upload_fail_cancel" msgid="9118496285835687125">"Zatvori"</string>
+ <string name="bt_error_btn_ok" msgid="5965151173011534240">"Potvrdi"</string>
+ <string name="unknown_file" msgid="6092727753965095366">"Nepoznata datoteka"</string>
+ <string name="unknown_file_desc" msgid="480434281415453287">"Nema aplikacija za obradu ovog tipa datoteke. \n"</string>
+ <string name="not_exist_file" msgid="3489434189599716133">"Nema datoteke"</string>
+ <string name="not_exist_file_desc" msgid="4059531573790529229">"Datoteka ne postoji. \n"</string>
+ <string name="enabling_progress_title" msgid="436157952334723406">"Sačekajte…"</string>
+ <string name="enabling_progress_content" msgid="4601542238119927904">"Uključivanje Bluetooth-a…"</string>
+ <string name="bt_toast_1" msgid="972182708034353383">"Datoteka će biti primljena. Proveravajte tok na tabli sa obaveštenjima."</string>
+ <string name="bt_toast_2" msgid="8602553334099066582">"Nije moguće primiti datoteku."</string>
+ <string name="bt_toast_3" msgid="6707884165086862518">"Zaustavljen prijem datoteke od pošiljaoca „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
+ <string name="bt_toast_4" msgid="4678812947604395649">"Slanje datoteke primaocu „<xliff:g id="RECIPIENT">%1$s</xliff:g>“"</string>
+ <string name="bt_toast_5" msgid="2846870992823019494">"Slanje<xliff:g id="NUMBER">%1$s</xliff:g> datoteka primaocu „<xliff:g id="RECIPIENT">%2$s</xliff:g>“"</string>
+ <string name="bt_toast_6" msgid="1855266596936622458">"Zaustavljeno slanje primaocu „<xliff:g id="RECIPIENT">%1$s</xliff:g>“"</string>
+ <string name="bt_sm_2_1" product="nosdcard" msgid="352165168004521000">"Nema dovoljno prostora u USB memoriji da bi se sačuvala datoteka pošiljaoca „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
+ <string name="bt_sm_2_1" product="default" msgid="1989018443456803630">"Nema dovoljno prostora na SD kartici da bi se sačuvala datoteka pošiljaoca „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
+ <string name="bt_sm_2_2" msgid="2965243265852680543">"Potreban prostor: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+ <string name="ErrorTooManyRequests" msgid="8578277541472944529">"Previše zahteva se obrađuje. Pokušajte ponovo kasnije."</string>
+ <string name="status_pending" msgid="2503691772030877944">"Prenos datoteke još nije počeo."</string>
+ <string name="status_running" msgid="6562808920311008696">"Prenos datoteke je u toku."</string>
+ <string name="status_success" msgid="239573225847565868">"Prenos datoteke je dovršen."</string>
+ <string name="status_not_accept" msgid="1695082417193780738">"Sadržaj nije podržan."</string>
+ <string name="status_forbidden" msgid="613956401054050725">"Ciljni uređaj je zabranio prenos."</string>
+ <string name="status_canceled" msgid="6664490318773098285">"Korisnik je otkazao prenos."</string>
+ <string name="status_file_error" msgid="3671917770630165299">"Problem sa skladištem."</string>
+ <string name="status_no_sd_card" product="nosdcard" msgid="1112125377088421469">"Nema USB memorije."</string>
+ <string name="status_no_sd_card" product="default" msgid="5760944071743325592">"Nema SD kartice. Umetnite SD karticu da biste sačuvali prenete datoteke."</string>
+ <string name="status_connection_error" msgid="947681831523219891">"Povezivanje nije uspelo."</string>
+ <string name="status_protocol_error" msgid="3245444473429269539">"Nije moguće ispravno obraditi zahtev."</string>
+ <string name="status_unknown_error" msgid="8156660554237824912">"Nepoznata greška."</string>
+ <string name="btopp_live_folder" msgid="7967791481444474554">"Primljeno preko Bluetooth-a"</string>
+ <string name="download_success" msgid="7036160438766730871">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> Primljeno u celosti."</string>
+ <string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> Slanje je dovršeno."</string>
+ <string name="inbound_history_title" msgid="6940914942271327563">"Dolazni prenosi"</string>
+ <string name="outbound_history_title" msgid="4279418703178140526">"Odlazni prenosi"</string>
+ <string name="no_transfers" msgid="3482965619151865672">"Istorija prenosa je prazna."</string>
+ <string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"Sve stavke će biti izbrisane sa liste."</string>
+ <string name="outbound_noti_title" msgid="8051906709452260849">"Deljenje preko Bluetooth-a: poslate datoteke"</string>
+ <string name="inbound_noti_title" msgid="4143352641953027595">"Deljenje preko Bluetooth-a: primljene datoteke"</string>
+ <plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
+ <item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> neuspešna.</item>
+ <item quantity="few"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> neuspešne.</item>
+ <item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> neuspešnih.</item>
+ </plurals>
+ <plurals name="noti_caption_success" formatted="false" msgid="1572472450257645181">
+ <item quantity="one"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> uspešna, %2$s</item>
+ <item quantity="few"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> uspešne, %2$s</item>
+ <item quantity="other"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> uspešnih, %2$s</item>
+ </plurals>
+ <string name="transfer_menu_clear_all" msgid="790017462957873132">"Obriši listu"</string>
+ <string name="transfer_menu_open" msgid="3368984869083107200">"Otvori"</string>
+ <string name="transfer_menu_clear" msgid="5854038118831427492">"Obriši sa liste"</string>
+ <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"Brisanje"</string>
+ <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"Sačuvaj"</string>
+ <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"Otkaži"</string>
+ <string name="bluetooth_map_settings_intro" msgid="6793938602201480648">"Izaberite naloge koje želite da delite preko Bluetooth-a. I dalje morate da prihvatite bilo kakav pristup nalozima pri povezivanju."</string>
+ <string name="bluetooth_map_settings_count" msgid="4557473074937024833">"Preostalih mesta:"</string>
+ <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikacije"</string>
+ <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Podešavanja Bluetooth deljenja poruka"</string>
+ <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Nije moguće izabrati nalog. Nema preostalih mesta"</string>
+</resources>
diff --git a/res/values-b+sr+Latn/strings_pbap.xml b/res/values-b+sr+Latn/strings_pbap.xml
new file mode 100644
index 0000000..4896295
--- /dev/null
+++ b/res/values-b+sr+Latn/strings_pbap.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Unesite ključ sesije za %1$s"</string>
+ <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Potreban je ključ sesije za Bluetooth"</string>
+ <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Isteklo je vreme za prihvatanje veze sa uređajem %1$s"</string>
+ <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Isteklo je vreme za unos ključa sesije pomoću %1$s"</string>
+ <string name="auth_notif_ticker" msgid="1575825798053163744">"Zahtev za potvrdu identiteta preko Obex protokola"</string>
+ <string name="auth_notif_title" msgid="7599854855681573258">"Ključ sesije"</string>
+ <string name="auth_notif_message" msgid="6667218116427605038">"Unesite ključ sesije za %1$s"</string>
+ <string name="defaultname" msgid="4821590500649090078">"Oprema za automobil"</string>
+ <string name="unknownName" msgid="2841414754740600042">"Nepoznato ime"</string>
+ <string name="localPhoneName" msgid="2349001318925409159">"Moje ime"</string>
+ <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-b+sr+Latn/strings_sap.xml b/res/values-b+sr+Latn/strings_sap.xml
new file mode 100644
index 0000000..7e36fb9
--- /dev/null
+++ b/res/values-b+sr+Latn/strings_sap.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="bluetooth_sap_notif_title" msgid="6877860822993195074">"Pristup SIM kartici preko Bluetooth-a"</string>
+ <string name="bluetooth_sap_notif_ticker" msgid="6807778527893726699">"Pristup SIM kartici preko Bluetooth-a"</string>
+ <string name="bluetooth_sap_notif_message" msgid="7138657801087500690">"Želite li da pošaljete klijentu zahtev za prekid veze?"</string>
+ <string name="bluetooth_sap_notif_disconnecting" msgid="819150843490233288">"Čeka se da klijent prekine vezu"</string>
+ <string name="bluetooth_sap_notif_disconnect_button" msgid="3678476872583356919">"Prekini vezu"</string>
+ <string name="bluetooth_sap_notif_force_disconnect_button" msgid="8144086340185532030">"Prinudno prekini vezu"</string>
+</resources>
diff --git a/res/values-b+sr+Latn/test_strings.xml b/res/values-b+sr+Latn/test_strings.xml
new file mode 100644
index 0000000..6e728d8
--- /dev/null
+++ b/res/values-b+sr+Latn/test_strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="hello" msgid="1740533743008967039">"Zdravo svima, proba"</string>
+ <string name="app_name" msgid="1203877025577761792">"Deljenje preko Bluetooth-a"</string>
+ <string name="insert_record" msgid="1450997173838378132">"Umetni zapis"</string>
+ <string name="update_record" msgid="2480425402384910635">"Potvrdi zapis"</string>
+ <string name="ack_record" msgid="6716152390978472184">"Ack zapis"</string>
+ <string name="deleteAll_record" msgid="4383349788485210582">"Izbriši sve zapise"</string>
+ <string name="ok_button" msgid="6519033415223065454">"Potvrdi"</string>
+ <string name="delete_record" msgid="4645040331967533724">"Izbriši zapis"</string>
+ <string name="start_server" msgid="9034821924409165795">"Pokreni TCP server"</string>
+ <string name="notify_server" msgid="4369106744022969655">"Obavesti TCP server"</string>
+</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 1883359..4af7439 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Приемане"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Времето за изчакване изтече при приемането на входящ файл от „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Споделяне чрез Bluetooth: Вх. файл"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Искате ли да получите този файл?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Входящ файл от друго устройство. Потвърдете, че искате да го получите."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Входящ файл"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> има готовност да изпрати „<xliff:g id="FILE">%2$s</xliff:g>“"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Споделяне чрез Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> се получава"</string>
<string name="notification_received" msgid="3324588019186687985">"Споделяне чрез Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> се получи"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Споделяне чрез Bluetooth: Файлът <xliff:g id="FILE">%1$s</xliff:g> не е получен"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Икона на приложението"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Настройки за споделяне на съобщения през Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Не може да се избере профил. Остават 0 слота"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Установена е аудиовръзка през Bluetooth"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Аудиовръзката през Bluetooth е прекратена"</string>
</resources>
diff --git a/res/values-bg/strings_pbap_client.xml b/res/values-bg/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-bg/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 4e45cc6..05332b7 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -18,16 +18,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permlab_bluetoothShareManager" msgid="311492132450338925">"ডাউনলোড ম্যানেজার অ্যাক্সেস করুন।"</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"অ্যাপ্লিকেশানটিকে BluetoothShare ম্যানেজার অ্যাক্সেস করতে ও ফাইলগুলি স্থানান্তর করতে এটি ব্যবহার করার অনুমতি দেয়।"</string>
- <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"শ্বেততালিকাভুক্ত Bluetooth ডিভাইসের অ্যাক্সেস।"</string>
- <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"অ্যাপ্লিকেশানটিকে অস্থায়ীভাবে কোনো Bluetooth ডিভাইসকে শ্বেততালিকায় অন্তর্ভুক্ত করার অনুমতি দেয়, যা সেই ডিভাইসটিকে এই ডিভাইসে ব্যবহারকারীর নিশ্চিতকরণ ছাড়া ফাইল পাঠানোর অনুমতি দেয়।"</string>
- <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"শ্বেততালিকাভুক্ত ব্লুটুথ ডিভাইসের অ্যাক্সেস।"</string>
+ <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"অ্যাপ্লিকেশানটিকে অস্থায়ীভাবে কোনো ব্লুটুথ ডিভাইসকে শ্বেততালিকায় অন্তর্ভুক্ত করার অনুমতি দেয়, যা সেই ডিভাইসটিকে এই ডিভাইসে ব্যবহারকারীর নিশ্চিতকরণ ছাড়া ফাইল পাঠানোর অনুমতি দেয়।"</string>
+ <string name="bt_share_picker_label" msgid="6268100924487046932">"ব্লুটুথ"</string>
<string name="unknown_device" msgid="9221903979877041009">"অজানা ডিভাইস"</string>
<string name="unknownNumber" msgid="4994750948072751566">"অজানা"</string>
<string name="airplane_error_title" msgid="2683839635115739939">"বিমান মোড"</string>
- <string name="airplane_error_msg" msgid="8698965595254137230">"আপনি বিমান মোডে Bluetooth ব্যবহার করতে পারবেন না।"</string>
+ <string name="airplane_error_msg" msgid="8698965595254137230">"আপনি বিমান মোডে ব্লুটুথ ব্যবহার করতে পারবেন না।"</string>
<string name="bt_enable_title" msgid="8657832550503456572"></string>
- <string name="bt_enable_line1" msgid="7203551583048149">"Bluetooth পরিষেবাগুলি ব্যবহার করার জন্য আপনাকে অবশ্যই প্রথমে Bluetooth চালু করতে হবে।"</string>
- <string name="bt_enable_line2" msgid="4341936569415937994">"এখন Bluetooth চালু করবেন?"</string>
+ <string name="bt_enable_line1" msgid="7203551583048149">"ব্লুটুথ পরিষেবাগুলি ব্যবহার করার জন্য আপনাকে অবশ্যই প্রথমে ব্লুটুথ চালু করতে হবে।"</string>
+ <string name="bt_enable_line2" msgid="4341936569415937994">"এখন ব্লুটুথ চালু করবেন?"</string>
<string name="bt_enable_cancel" msgid="1988832367505151727">"বাতিল করুন"</string>
<string name="bt_enable_ok" msgid="3432462749994538265">"চালু করুন"</string>
<string name="incoming_file_confirm_title" msgid="8139874248612182627">"ফাইল স্থানান্তর"</string>
@@ -36,16 +36,15 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"স্বীকার করুন"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ঠিক আছে"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" এর থেকে ইনকামিং ফাইল গ্রহণ করার সময় অতিবাহিত হয়ে গেছে।"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth share: ইনকামিং ফাইল"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"আপনি কি এই ফাইলটি পেতে চান?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"অন্য ডিভাইস থেকে ইনকামিং ফাইল। আপনি যে ফাইলটি পেতে চান তা নিশ্চিত করুন।"</string>
- <string name="notification_receiving" msgid="4674648179652543984">"Bluetooth share: <xliff:g id="FILE">%1$s</xliff:g> প্রাপ্ত করা হচ্ছে"</string>
- <string name="notification_received" msgid="3324588019186687985">"Bluetooth share: <xliff:g id="FILE">%1$s</xliff:g> প্রাপ্ত করা হয়েছে"</string>
- <string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth share: <xliff:g id="FILE">%1$s</xliff:g> ফাইল প্রাপ্ত করা হয়নি"</string>
- <string name="notification_sending" msgid="3035748958534983833">"Bluetooth share: <xliff:g id="FILE">%1$s</xliff:g> পাঠানো হচ্ছে"</string>
- <string name="notification_sent" msgid="9218710861333027778">"Bluetooth share: <xliff:g id="FILE">%1$s</xliff:g> পাঠানো হয়েছে"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"আগত ফাইল"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> পাঠানোর জন্য প্রস্তুত"</string>
+ <string name="notification_receiving" msgid="4674648179652543984">"ব্লুটুথ share: <xliff:g id="FILE">%1$s</xliff:g> প্রাপ্ত করা হচ্ছে"</string>
+ <string name="notification_received" msgid="3324588019186687985">"ব্লুটুথ share: <xliff:g id="FILE">%1$s</xliff:g> প্রাপ্ত করা হয়েছে"</string>
+ <string name="notification_received_fail" msgid="3619350997285714746">"ব্লুটুথ share: <xliff:g id="FILE">%1$s</xliff:g> ফাইল প্রাপ্ত করা হয়নি"</string>
+ <string name="notification_sending" msgid="3035748958534983833">"ব্লুটুথ share: <xliff:g id="FILE">%1$s</xliff:g> পাঠানো হচ্ছে"</string>
+ <string name="notification_sent" msgid="9218710861333027778">"ব্লুটুথ share: <xliff:g id="FILE">%1$s</xliff:g> পাঠানো হয়েছে"</string>
<string name="notification_sent_complete" msgid="302943281067557969">"১০০% সম্পূর্ণ"</string>
- <string name="notification_sent_fail" msgid="6696082233774569445">"Bluetooth share: <xliff:g id="FILE">%1$s</xliff:g> ফাইল পাঠানো হয়নি"</string>
+ <string name="notification_sent_fail" msgid="6696082233774569445">"ব্লুটুথ share: <xliff:g id="FILE">%1$s</xliff:g> ফাইল পাঠানো হয়নি"</string>
<string name="download_title" msgid="3353228219772092586">"ফাইল স্থানান্তর"</string>
<string name="download_line1" msgid="4926604799202134144">"প্রেরক: \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
<string name="download_line2" msgid="5876973543019417712">"ফাইল: <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -78,7 +77,7 @@
<string name="not_exist_file" msgid="3489434189599716133">"কোনো ফাইল নেই"</string>
<string name="not_exist_file_desc" msgid="4059531573790529229">"ফাইলটির অস্তিত্ব নেই। \n"</string>
<string name="enabling_progress_title" msgid="436157952334723406">"দয়া করে অপেক্ষা করুন..."</string>
- <string name="enabling_progress_content" msgid="4601542238119927904">"Bluetooth চালু করা হচ্ছে..."</string>
+ <string name="enabling_progress_content" msgid="4601542238119927904">"ব্লুটুথ চালু করা হচ্ছে..."</string>
<string name="bt_toast_1" msgid="972182708034353383">"ফাইল প্রাপ্ত করা হবে। বিজ্ঞপ্তি প্যানেলে প্রগতি চেক করুন।"</string>
<string name="bt_toast_2" msgid="8602553334099066582">"ফাইল প্রাপ্ত করা যাবে না।"</string>
<string name="bt_toast_3" msgid="6707884165086862518">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" এর থেকে ফাইল পাওয়া বন্ধ করা হয়েছে"</string>
@@ -101,15 +100,15 @@
<string name="status_connection_error" msgid="947681831523219891">"সংযোগ অসফল।"</string>
<string name="status_protocol_error" msgid="3245444473429269539">"অনুরোধ সঠিকভাবে পরিচালনা করা যাবে না।"</string>
<string name="status_unknown_error" msgid="8156660554237824912">"অজানা ত্রুটি৷"</string>
- <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth প্রাপ্তি"</string>
+ <string name="btopp_live_folder" msgid="7967791481444474554">"ব্লুটুথ প্রাপ্তি"</string>
<string name="download_success" msgid="7036160438766730871">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> প্রাপ্ত করা সম্পূর্ণ।"</string>
<string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> পাঠানো সম্পূর্ণ।"</string>
<string name="inbound_history_title" msgid="6940914942271327563">"অন্তরমূখী স্থানান্তরগুলি"</string>
<string name="outbound_history_title" msgid="4279418703178140526">"বহির্গামী স্থানান্তরগুলি"</string>
<string name="no_transfers" msgid="3482965619151865672">"স্থানান্তরের ইতিহাস খালি।"</string>
<string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"তালিকা থেকে সমস্ত আইটেম সাফ করা হবে।"</string>
- <string name="outbound_noti_title" msgid="8051906709452260849">"Bluetooth share: পাঠানো ফাইলগুলি"</string>
- <string name="inbound_noti_title" msgid="4143352641953027595">"Bluetooth share: প্রাপ্ত করা ফাইলগুলি"</string>
+ <string name="outbound_noti_title" msgid="8051906709452260849">"ব্লুটুথ share: পাঠানো ফাইলগুলি"</string>
+ <string name="inbound_noti_title" msgid="4143352641953027595">"ব্লুটুথ share: প্রাপ্ত করা ফাইলগুলি"</string>
<plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
<item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g>টি অসফল হয়েছে৷</item>
<item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g>টি অসফল হয়েছে৷</item>
@@ -124,9 +123,11 @@
<string name="transfer_clear_dlg_title" msgid="2953444575556460386">"সাফ করুন"</string>
<string name="bluetooth_map_settings_save" msgid="7635491847388074606">"সংরক্ষণ করুন"</string>
<string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"বাতিল করুন"</string>
- <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"আপনি Bluetooth এর মাধ্যমে যে অ্যাকাউন্টগুলি শেয়ার করতে চান সেগুলি নির্বাচন করুন। সংযোগের সময়ে আপনাকে এখনো অ্যাকাউন্টের যে কোনো অ্যাক্সেস গ্রহণ করতে হবে।"</string>
+ <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"আপনি ব্লুটুথ এর মাধ্যমে যে অ্যাকাউন্টগুলি শেয়ার করতে চান সেগুলি নির্বাচন করুন। সংযোগের সময়ে আপনাকে এখনো অ্যাকাউন্টের যে কোনো অ্যাক্সেস গ্রহণ করতে হবে।"</string>
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"যে স্লটগুলি বাকি আছে:"</string>
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"অ্যাপ্লিকেশান আইকন"</string>
- <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth মারফত বার্তা শেয়ার করার সেটিংস"</string>
+ <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ব্লুটুথ মারফত বার্তা শেয়ার করার সেটিংস"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"অ্যাকাউন্ট নির্বাচন করা যাচ্ছে না। ০টি স্লট বাকি আছে"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ব্লুটুথ অডিও সংযুক্ত হয়েছে"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ব্লুটুথ অডিওর সংযোগ বিচ্ছিন্ন হয়েছে"</string>
</resources>
diff --git a/res/values-bn-rBD/strings_pbap.xml b/res/values-bn-rBD/strings_pbap.xml
index 3b3a061..7411733 100644
--- a/res/values-bn-rBD/strings_pbap.xml
+++ b/res/values-bn-rBD/strings_pbap.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"%1$s এর জন্য সেশন কী টাইপ করুন"</string>
- <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth সেশন কী প্রয়োজন"</string>
+ <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"ব্লুটুথ সেশন কী প্রয়োজন"</string>
<string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"%1$s এর মাধ্যমে সংযোগ স্বীকার করার সময় অতিবাহিত হয়ে গেছে"</string>
<string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"%1$s এর মাধ্যেমে সেশন কী ইনপুট করার সময় অতিবাহিত হয়ে গেছে"</string>
<string name="auth_notif_ticker" msgid="1575825798053163744">"Obex প্রমাণীকরণ অনুরোধ"</string>
diff --git a/res/values-bn-rBD/strings_pbap_client.xml b/res/values-bn-rBD/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-bn-rBD/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-bn-rBD/strings_sap.xml b/res/values-bn-rBD/strings_sap.xml
index 274998f..23def2a 100644
--- a/res/values-bn-rBD/strings_sap.xml
+++ b/res/values-bn-rBD/strings_sap.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="bluetooth_sap_notif_title" msgid="6877860822993195074">"Bluetooth SIM -এ অ্যাক্সেস করুন"</string>
- <string name="bluetooth_sap_notif_ticker" msgid="6807778527893726699">"Bluetooth SIM -এ অ্যাক্সেস করুন"</string>
+ <string name="bluetooth_sap_notif_title" msgid="6877860822993195074">"ব্লুটুথ SIM -এ অ্যাক্সেস করুন"</string>
+ <string name="bluetooth_sap_notif_ticker" msgid="6807778527893726699">"ব্লুটুথ SIM -এ অ্যাক্সেস করুন"</string>
<string name="bluetooth_sap_notif_message" msgid="7138657801087500690">"ক্লায়েন্ট সংযোগ বিচ্ছিন্ন করার অনুরোধ জানাবেন?"</string>
<string name="bluetooth_sap_notif_disconnecting" msgid="819150843490233288">"সংযোগ বিচ্ছিন্ন করতে ক্লায়েন্টের জন্য অপেক্ষা করা"</string>
<string name="bluetooth_sap_notif_disconnect_button" msgid="3678476872583356919">"সংযোগ বিচ্ছিন্ন করুন"</string>
diff --git a/res/values-bn-rBD/test_strings.xml b/res/values-bn-rBD/test_strings.xml
index bc9c95a..46b3470 100644
--- a/res/values-bn-rBD/test_strings.xml
+++ b/res/values-bn-rBD/test_strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="hello" msgid="1740533743008967039">"হ্যালো ওয়ার্ল্ড, TestActivity"</string>
- <string name="app_name" msgid="1203877025577761792">"Bluetooth শেয়ার করা"</string>
+ <string name="app_name" msgid="1203877025577761792">"ব্লুটুথ শেয়ার করা"</string>
<string name="insert_record" msgid="1450997173838378132">"রেকর্ড ঢোকান"</string>
<string name="update_record" msgid="2480425402384910635">"রেকর্ড নিশ্চিত করুন"</string>
<string name="ack_record" msgid="6716152390978472184">"Ack রেকর্ড"</string>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
new file mode 100644
index 0000000..a7c21d8
--- /dev/null
+++ b/res/values-bs-rBA/strings.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Pristupite upravitelju za preuzimanja."</string>
+ <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Dozvoljava aplikaciji da pristupa BluetoothShare upravitelju i koristi ga za prenošenje fajlova."</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Dozvoli pristup bluetooth uređaju."</string>
+ <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Dozvoljava aplikaciji da privremeno stavi Bluetooth uređaj na spisak dopuštenih fajlova, omogućavajući tom uređaju da šalje fajlove na ovaj uređaj bez potvrde korisnika."</string>
+ <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+ <string name="unknown_device" msgid="9221903979877041009">"Nepoznat uređaj"</string>
+ <string name="unknownNumber" msgid="4994750948072751566">"Nepoznato"</string>
+ <string name="airplane_error_title" msgid="2683839635115739939">"Način rada u avionu"</string>
+ <string name="airplane_error_msg" msgid="8698965595254137230">"Ne možete koristiti Bluetooth u načinu rada u avionu."</string>
+ <string name="bt_enable_title" msgid="8657832550503456572"></string>
+ <string name="bt_enable_line1" msgid="7203551583048149">"Da biste koristili Bluetooth usluge, prvo morate uključiti Bluetooth."</string>
+ <string name="bt_enable_line2" msgid="4341936569415937994">"Želite uključiti Bluetooth sada?"</string>
+ <string name="bt_enable_cancel" msgid="1988832367505151727">"Otkaži"</string>
+ <string name="bt_enable_ok" msgid="3432462749994538265">"Uključi"</string>
+ <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Prenošenje fajla"</string>
+ <string name="incoming_file_confirm_content" msgid="2752605552743148036">"Prihvatiti dolazni fajl?"</string>
+ <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Odbij"</string>
+ <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Prihvati"</string>
+ <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Uredu"</string>
+ <string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Isteklo je vrijeme prilikom prihvatanja dolaznog fajla koji šalje \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Dolazni fajl"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> sada može poslati <xliff:g id="FILE">%2$s</xliff:g>"</string>
+ <string name="notification_receiving" msgid="4674648179652543984">"Bluetooth dijeljenje: Prima se fajl <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_received" msgid="3324588019186687985">"Bluetooth dijeljenje: Primljen fajl <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth dijeljenje: Fajl <xliff:g id="FILE">%1$s</xliff:g> nije primljen"</string>
+ <string name="notification_sending" msgid="3035748958534983833">"Bluetooth dijeljenje: Šalje se fajl <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_sent" msgid="9218710861333027778">"Bluetooth dijeljenje: Poslan fajl <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="notification_sent_complete" msgid="302943281067557969">"Dovršeno 100%"</string>
+ <string name="notification_sent_fail" msgid="6696082233774569445">"Bluetooth dijeljenje: Fajl <xliff:g id="FILE">%1$s</xliff:g> nije poslan"</string>
+ <string name="download_title" msgid="3353228219772092586">"Prenošenje fajla"</string>
+ <string name="download_line1" msgid="4926604799202134144">"Šalje: \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
+ <string name="download_line2" msgid="5876973543019417712">"Fajl: <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="download_line3" msgid="4384821622908676061">"Veličina fajla: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+ <string name="download_line4" msgid="8535996869722666525"></string>
+ <string name="download_line5" msgid="3069560415845295386">"Primanje fajla…"</string>
+ <string name="download_cancel" msgid="9177305996747500768">"Zaustavi"</string>
+ <string name="download_ok" msgid="5000360731674466039">"Sakrij"</string>
+ <string name="incoming_line1" msgid="2127419875681087545">"Šalje"</string>
+ <string name="incoming_line2" msgid="3348994249285315873">"Naziv fajla"</string>
+ <string name="incoming_line3" msgid="7954237069667474024">"Veličina"</string>
+ <string name="download_fail_line1" msgid="3846450148862894552">"Fajl nije primljen"</string>
+ <string name="download_fail_line2" msgid="8950394574689971071">"Fajl: <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="download_fail_line3" msgid="3451040656154861722">"Razlog: <xliff:g id="REASON">%1$s</xliff:g>"</string>
+ <string name="download_fail_ok" msgid="1521733664438320300">"Uredu"</string>
+ <string name="download_succ_line5" msgid="4509944688281573595">"Fajl primljen"</string>
+ <string name="download_succ_ok" msgid="7053688246357050216">"Otvori"</string>
+ <string name="upload_line1" msgid="2055952074059709052">"Prima: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
+ <string name="upload_line3" msgid="4920689672457037437">"Vrsta fajla: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
+ <string name="upload_line5" msgid="7759322537674229752">"Slanje fajla…"</string>
+ <string name="upload_succ_line5" msgid="5687317197463383601">"Fajl poslan"</string>
+ <string name="upload_succ_ok" msgid="7705428476405478828">"Uredu"</string>
+ <string name="upload_fail_line1" msgid="7899394672421491701">"Fajl kojeg prima \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" nije poslan."</string>
+ <string name="upload_fail_line1_2" msgid="2108129204050841798">"Fajl: <xliff:g id="FILE">%1$s</xliff:g>"</string>
+ <string name="upload_fail_ok" msgid="5807702461606714296">"Pokušajte ponovo"</string>
+ <string name="upload_fail_cancel" msgid="9118496285835687125">"Zatvori"</string>
+ <string name="bt_error_btn_ok" msgid="5965151173011534240">"Uredu"</string>
+ <string name="unknown_file" msgid="6092727753965095366">"Nepoznat fajl"</string>
+ <string name="unknown_file_desc" msgid="480434281415453287">"Nema aplikacije za rukovanje ovom vrstom fajla. \n"</string>
+ <string name="not_exist_file" msgid="3489434189599716133">"Nema fajla"</string>
+ <string name="not_exist_file_desc" msgid="4059531573790529229">"Fajl ne postoji. \n"</string>
+ <string name="enabling_progress_title" msgid="436157952334723406">"Pričekajte…"</string>
+ <string name="enabling_progress_content" msgid="4601542238119927904">"Uključuje se Bluetooth…"</string>
+ <string name="bt_toast_1" msgid="972182708034353383">"Fajl će biti primljen. Pratite napredak u Ploči s obavještenjima."</string>
+ <string name="bt_toast_2" msgid="8602553334099066582">"Nije moguće primiti fajl."</string>
+ <string name="bt_toast_3" msgid="6707884165086862518">"Zaustavljeno primanje fajla kojeg šalje \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
+ <string name="bt_toast_4" msgid="4678812947604395649">"Slanje fajla kojeg prima \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
+ <string name="bt_toast_5" msgid="2846870992823019494">"Slanje <xliff:g id="NUMBER">%1$s</xliff:g> fajl(ov)a, prima \"<xliff:g id="RECIPIENT">%2$s</xliff:g>\""</string>
+ <string name="bt_toast_6" msgid="1855266596936622458">"Zaustavljeno slanje fajla kojeg prima \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
+ <string name="bt_sm_2_1" product="nosdcard" msgid="352165168004521000">"Nema dovoljno prostora na USB pohrani da se sačuva fajl koji šalje \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
+ <string name="bt_sm_2_1" product="default" msgid="1989018443456803630">"Nema dovoljno prostora na SD kartici da se sačuva fajl koji šalje \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
+ <string name="bt_sm_2_2" msgid="2965243265852680543">"Potrebni prostor: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+ <string name="ErrorTooManyRequests" msgid="8578277541472944529">"Obrađuje se previše zahtjeva. Pokušajte ponovo kasnije."</string>
+ <string name="status_pending" msgid="2503691772030877944">"Prenošenje fajla još nije započelo."</string>
+ <string name="status_running" msgid="6562808920311008696">"Prenošenje fajla je u toku."</string>
+ <string name="status_success" msgid="239573225847565868">"Prenošenje fajla je uspješno dovršeno."</string>
+ <string name="status_not_accept" msgid="1695082417193780738">"Sadržaj nije podržan."</string>
+ <string name="status_forbidden" msgid="613956401054050725">"Ciljni uređaj je zabranio prenošenje."</string>
+ <string name="status_canceled" msgid="6664490318773098285">"Korisnik je otkazao prenošenje."</string>
+ <string name="status_file_error" msgid="3671917770630165299">"Problem s pohranom."</string>
+ <string name="status_no_sd_card" product="nosdcard" msgid="1112125377088421469">"Nema USB pohrane."</string>
+ <string name="status_no_sd_card" product="default" msgid="5760944071743325592">"Nema SD kartice. Umetnite SD karticu kako biste sačuvali prenesene fajlove."</string>
+ <string name="status_connection_error" msgid="947681831523219891">"Povezivanje nije uspjelo."</string>
+ <string name="status_protocol_error" msgid="3245444473429269539">"Nije moguće pravilno obraditi zahtjev."</string>
+ <string name="status_unknown_error" msgid="8156660554237824912">"Nepoznata greška."</string>
+ <string name="btopp_live_folder" msgid="7967791481444474554">"Primljeno preko Bluetootha"</string>
+ <string name="download_success" msgid="7036160438766730871">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> Primanje završeno."</string>
+ <string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> Slanje dovršeno."</string>
+ <string name="inbound_history_title" msgid="6940914942271327563">"Dolazna prenošenja"</string>
+ <string name="outbound_history_title" msgid="4279418703178140526">"Odlazna prenošenja"</string>
+ <string name="no_transfers" msgid="3482965619151865672">"Historija prenošenja je prazna."</string>
+ <string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"Sve stavke će biti izbrisane sa spiska."</string>
+ <string name="outbound_noti_title" msgid="8051906709452260849">"Bluetooth dijeljenje: Poslani fajlovi"</string>
+ <string name="inbound_noti_title" msgid="4143352641953027595">"Bluetooth dijeljenje: Primljeni fajlovi"</string>
+ <plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
+ <item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> neuspješan.</item>
+ <item quantity="few"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> neuspješna.</item>
+ <item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> neuspješnih.</item>
+ </plurals>
+ <plurals name="noti_caption_success" formatted="false" msgid="1572472450257645181">
+ <item quantity="one"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> uspješan, %2$s</item>
+ <item quantity="few"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> uspješna, %2$s</item>
+ <item quantity="other"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> uspješnih, %2$s</item>
+ </plurals>
+ <string name="transfer_menu_clear_all" msgid="790017462957873132">"Obriši spisak"</string>
+ <string name="transfer_menu_open" msgid="3368984869083107200">"Otvori"</string>
+ <string name="transfer_menu_clear" msgid="5854038118831427492">"Obriši sa spiska"</string>
+ <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"Obriši"</string>
+ <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"Sačuvaj"</string>
+ <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"Otkaži"</string>
+ <string name="bluetooth_map_settings_intro" msgid="6793938602201480648">"Odaberite račune koje želite dijeliti preko Bluetooth veze. I dalje morate prihvatiti pristupanje računima prilikom povezivanja."</string>
+ <string name="bluetooth_map_settings_count" msgid="4557473074937024833">"Preostalo utora:"</string>
+ <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikacije"</string>
+ <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Postavke za dijeljenje Bluetooth poruka"</string>
+ <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Nije moguće odabrati račun. Preostalo je 0 utora"</string>
+</resources>
diff --git a/res/values-bs-rBA/strings_pbap.xml b/res/values-bs-rBA/strings_pbap.xml
new file mode 100644
index 0000000..3030c23
--- /dev/null
+++ b/res/values-bs-rBA/strings_pbap.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Unesite ključ sesije za uređaj %1$s"</string>
+ <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Neophodan je ključ za Bluetooth sesiju"</string>
+ <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Isteklo je vrijeme za prihvatanje veze sa uređajem %1$s"</string>
+ <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Isteklo je vrijeme za unošenje ključa sesije sa uređajem %1$s"</string>
+ <string name="auth_notif_ticker" msgid="1575825798053163744">"Zahtjev za Obex provjeru vjerodostojnosti"</string>
+ <string name="auth_notif_title" msgid="7599854855681573258">"Ključ sesije"</string>
+ <string name="auth_notif_message" msgid="6667218116427605038">"Unesite ključ sesije za %1$s"</string>
+ <string name="defaultname" msgid="4821590500649090078">"Komplet za automobil"</string>
+ <string name="unknownName" msgid="2841414754740600042">"Nepoznato ime"</string>
+ <string name="localPhoneName" msgid="2349001318925409159">"Moje ime"</string>
+ <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-bs-rBA/strings_sap.xml b/res/values-bs-rBA/strings_sap.xml
new file mode 100644
index 0000000..ab906d4
--- /dev/null
+++ b/res/values-bs-rBA/strings_sap.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="bluetooth_sap_notif_title" msgid="6877860822993195074">"Bluetooth pristup SIM-u"</string>
+ <string name="bluetooth_sap_notif_ticker" msgid="6807778527893726699">"Bluetooth pristup SIM-u"</string>
+ <string name="bluetooth_sap_notif_message" msgid="7138657801087500690">"Tražiti od klijenta da prekine vezu?"</string>
+ <string name="bluetooth_sap_notif_disconnecting" msgid="819150843490233288">"Čeka se da klijent prekine vezu"</string>
+ <string name="bluetooth_sap_notif_disconnect_button" msgid="3678476872583356919">"Prekini vezu"</string>
+ <string name="bluetooth_sap_notif_force_disconnect_button" msgid="8144086340185532030">"Prisilno prekini vezu"</string>
+</resources>
diff --git a/res/values-bs-rBA/test_strings.xml b/res/values-bs-rBA/test_strings.xml
new file mode 100644
index 0000000..0d40a91
--- /dev/null
+++ b/res/values-bs-rBA/test_strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+ <string name="app_name" msgid="1203877025577761792">"Bluetooth dijeljenje"</string>
+ <string name="insert_record" msgid="1450997173838378132">"Umetni zapis"</string>
+ <string name="update_record" msgid="2480425402384910635">"Potvrdi zapis"</string>
+ <string name="ack_record" msgid="6716152390978472184">"Evidencija Ack"</string>
+ <string name="deleteAll_record" msgid="4383349788485210582">"Izbriši svu evidenciju"</string>
+ <string name="ok_button" msgid="6519033415223065454">"Uredu"</string>
+ <string name="delete_record" msgid="4645040331967533724">"Izbriši evidenciju"</string>
+ <string name="start_server" msgid="9034821924409165795">"Pokreni TCP server"</string>
+ <string name="notify_server" msgid="4369106744022969655">"Obavijesti TCP server"</string>
+</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index bdbd084..53ce817 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepta"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"D\'acord"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"S\'ha esgotat el temps d\'espera mentre s\'acceptava un fitxer entrant de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth: fitxer entrant"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Vols rebre aquest fitxer?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Està entrant un fitxer d\'un altre dispositiu. Confirma que vols rebre el fitxer."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Fitxer entrant"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ja pot enviar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth: s\'està rebent <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> rebut"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> no rebut"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icona d\'aplicació"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Configuració per compartir missatges mitjançant el Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"No es pot seleccionar el compte. No queda cap espai."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Àudio per Bluetooth connectat"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Àudio per Bluetooth desconnectat"</string>
</resources>
diff --git a/res/values-ca/strings_pbap_client.xml b/res/values-ca/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ca/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 670276b..42363ca 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Přijmout"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Při příjmu příchozího souboru od uživatele <xliff:g id="SENDER">%1$s</xliff:g> vypršel časový limit."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Sdílení Bluetooth: Příchozí soubor"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Chcete tento soubor přijmout?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Potvrďte prosím příjem příchozího souboru z jiného zařízení."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Příchozí soubor"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> – odeslání souboru <xliff:g id="FILE">%2$s</xliff:g> je připraveno"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Sdílení Bluetooth: Příjem souboru <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Sdílení Bluetooth: Soubor <xliff:g id="FILE">%1$s</xliff:g> byl přijat"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Sdílení Bluetooth: Soubor <xliff:g id="FILE">%1$s</xliff:g> nebyl přijat"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikace"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Nastavení sdílení zpráv přes Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Účet nelze vybrat. Nezbývají žádné sloty."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth Audio – připojeno"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth Audio – odpojeno"</string>
</resources>
diff --git a/res/values-cs/strings_pbap_client.xml b/res/values-cs/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-cs/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index ebd49f4..2050d7b 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepter"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Der opstod timeout ved modtagelse af indgående fil fra \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-deling: indgående fil"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"VIl du modtage denne fil?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Indgående fil fra en anden enhed. Bekræft, at du vil modtage denne fil."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Indgående fil"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> er klar til at sende <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-deling: Modtager <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-deling: Modtog <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-deling: Filen <xliff:g id="FILE">%1$s</xliff:g> blev ikke modtaget"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Appens ikon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Indstillinger for beskeddeling via Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Kontoen kan ikke vælges. Der er ikke flere pladser tilbage"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-lyden blev tilsluttet"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-lyden blev afbrudt"</string>
</resources>
diff --git a/res/values-da/strings_pbap_client.xml b/res/values-da/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-da/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index d5f3cbc..5e6e6d1 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Akzeptieren"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Die Zeit zum Empfang der eingehenden Datei von \"<xliff:g id="SENDER">%1$s</xliff:g>\" ist abgelaufen."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-Freigabe: Eingehende Datei"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Möchtest du diese Datei empfangen?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Ein anderes Gerät versucht, eine Datei zu übertragen. Bestätige, dass du diese Datei empfangen möchtest."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Eingehende Datei"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> kann jetzt <xliff:g id="FILE">%2$s</xliff:g> senden."</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-Freigabe: <xliff:g id="FILE">%1$s</xliff:g> wird empfangen"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-Freigabe: <xliff:g id="FILE">%1$s</xliff:g> wurde empfangen"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-Freigabe: <xliff:g id="FILE">%1$s</xliff:g> wurde nicht empfangen"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"App-Symbol"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Einstellungen zur Bluetooth-Nachrichtenfreigabe"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Konto kann nicht ausgewählt werden. 0 Plätze frei."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-Audio verbunden"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-Audio-Verbindung aufgehoben"</string>
</resources>
diff --git a/res/values-de/strings_pbap_client.xml b/res/values-de/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-de/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ee43147..7c90422 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Αποδοχή"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Σημειώθηκε διακοπή κατά την αποδοχή ενός εισερχόμενου αρχείου από τον αποστολέα \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Κοινή χρήση μέσω Bluetooth: Εισερχόμενο αρχείο"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Θέλετε να λάβετε αυτό το αρχείο;"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Εισερχόμενο αρχείο από άλλη συσκευή. Επιβεβαιώστε ότι θέλετε να λάβετε αυτό το αρχείο."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Εισερχόμενο αρχείο"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Ο χρήστης <xliff:g id="SENDER">%1$s</xliff:g> πρόκειται να στείλει το αρχείο <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Κοινή χρήση μέσω Bluetooth: Λήψη του <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Κοινή χρήση μέσω Bluetooth: Ελήφθη το <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Κοινή χρήση μέσω Bluetooth: Το αρχείο <xliff:g id="FILE">%1$s</xliff:g> δεν ελήφθη"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Εικονίδιο εφαρμογής"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Ρυθμίσεις κοινής χρήσης μηνυμάτων μέσω Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Δεν είναι δυνατή η επιλογή λογαριασμού. Απομένουν 0 υποδοχές"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Ο ήχος Bluetooth συνδέθηκε"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Ο ήχος Bluetooth αποσυνδέθηκε"</string>
</resources>
diff --git a/res/values-el/strings_pbap_client.xml b/res/values-el/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-el/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f3fabad..735b078 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accept"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"There was a timeout while accepting an incoming file from \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth share: Incoming file"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Do you want to receive this file?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Incoming file from another device. Confirm that you want to receive this file."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Incoming file"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> is ready to send <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth share: Receiving <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth share: Received <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth share: File <xliff:g id="FILE">%1$s</xliff:g> not received"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Application Icon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth Message Sharing Settings"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Cannot select account. 0 slots left"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth audio connected"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
</resources>
diff --git a/res/values-en-rAU/strings_pbap_client.xml b/res/values-en-rAU/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-en-rAU/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f3fabad..735b078 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accept"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"There was a timeout while accepting an incoming file from \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth share: Incoming file"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Do you want to receive this file?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Incoming file from another device. Confirm that you want to receive this file."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Incoming file"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> is ready to send <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth share: Receiving <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth share: Received <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth share: File <xliff:g id="FILE">%1$s</xliff:g> not received"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Application Icon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth Message Sharing Settings"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Cannot select account. 0 slots left"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth audio connected"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
</resources>
diff --git a/res/values-en-rGB/strings_pbap_client.xml b/res/values-en-rGB/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-en-rGB/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f3fabad..735b078 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accept"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"There was a timeout while accepting an incoming file from \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth share: Incoming file"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Do you want to receive this file?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Incoming file from another device. Confirm that you want to receive this file."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Incoming file"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> is ready to send <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth share: Receiving <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth share: Received <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth share: File <xliff:g id="FILE">%1$s</xliff:g> not received"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Application Icon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth Message Sharing Settings"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Cannot select account. 0 slots left"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth audio connected"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
</resources>
diff --git a/res/values-en-rIN/strings_pbap_client.xml b/res/values-en-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-en-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 10c1b31..1d544f2 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceptar"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Aceptar"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Tiempo de espera agotado al aceptar un archivo entrante de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Compartir por Bluetooth: Archivo entrante"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"¿Quieres recibir este archivo?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Hay un archivo entrante de otro dispositivo. Confirma que quieres recibirlo."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Archivo entrante"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> está listo para enviar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth: recibiendo <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> recibido"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth: no se recibió el archivo <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ícono de la aplicación"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Configuración de mensajes compartidos por Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"No puedes seleccionar la cuenta. No quedan espacios."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio Bluetooth conectado"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth desconectado"</string>
</resources>
diff --git a/res/values-es-rUS/strings_pbap_client.xml b/res/values-es-rUS/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-es-rUS/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 454d87c..34a496a 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceptar"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Aceptar"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Se ha agotado el tiempo para aceptar el archivo entrante de \"<xliff:g id="SENDER">%1$s</xliff:g>\"."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth: archivo entrante"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"¿Quieres recibir este archivo?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Otro dispositivo quiere enviarte un archivo. Confirma que quieres recibirlo."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Archivo entrante"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ya puede enviar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth: recibiendo <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Compartir con Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> recibido"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> no recibido"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icono de aplicación"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Ajustes de mensajes compartidos por Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"No se puede seleccionar la cuenta. No quedan ranuras"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio por Bluetooth conectado"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio por Bluetooth desconectado"</string>
</resources>
diff --git a/res/values-es/strings_pbap_client.xml b/res/values-es/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-es/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 2e79811..539113a 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Nõustun"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Esines ajalõpp sissetuleva faili aktsepteerimisel saatjalt „<xliff:g id="SENDER">%1$s</xliff:g>”"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetoothi jagamine: sissetulev fail"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Kas soovite selle faili vastu võtta?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Sissetulev fail teisest seadmest. Kinnitage, et soovite selle faili vastu võtta."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Sissetulev fail"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Saatja <xliff:g id="SENDER">%1$s</xliff:g> on faili <xliff:g id="FILE">%2$s</xliff:g> saatmiseks valmis"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetoothi jagamine: faili <xliff:g id="FILE">%1$s</xliff:g> vastuvõtmine"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetoothi jagamine: <xliff:g id="FILE">%1$s</xliff:g> vastu võetud"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetoothi jagamine: faili <xliff:g id="FILE">%1$s</xliff:g> pole saadud"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Rakenduse ikoon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetoothi sõnumi jagamise seaded"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Kontot ei saa valida. Pole ühtegi vaba kohta"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetoothi heli on ühendatud"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetoothi heli ühendus on katkestatud"</string>
</resources>
diff --git a/res/values-et-rEE/strings_pbap_client.xml b/res/values-et-rEE/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-et-rEE/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index d9f37c6..d00723d 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -36,16 +36,15 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Onartu"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Ados"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" igorlearen sarrerako fitxategia onartzeko denbora-muga gainditu da"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth bidezko partekatzea: sarrerako fitxategia"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Fitxategi hau jaso nahi duzu?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Sarrerako fitxategi bat bidali dizute beste gailu batetik. Berretsi fitxategia jaso nahi duzun."</string>
- <string name="notification_receiving" msgid="4674648179652543984">"Bluetooth bidezko partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia jasotzen"</string>
- <string name="notification_received" msgid="3324588019186687985">"Bluetooth bidezko partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia jaso da"</string>
- <string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth bidezko partekatzea: ez da <xliff:g id="FILE">%1$s</xliff:g> fitxategia jaso"</string>
- <string name="notification_sending" msgid="3035748958534983833">"Bluetooth bidezko partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia bidaltzen"</string>
- <string name="notification_sent" msgid="9218710861333027778">"Bluetooth bidezko partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia bidali da"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Sarrerako fitxategia"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> prest dago <xliff:g id="FILE">%2$s</xliff:g> bidaltzeko"</string>
+ <string name="notification_receiving" msgid="4674648179652543984">"Bluetooth bidez partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia jasotzen"</string>
+ <string name="notification_received" msgid="3324588019186687985">"Bluetooth bidez partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia jaso da"</string>
+ <string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth bidez partekatzea: ez da jaso <xliff:g id="FILE">%1$s</xliff:g> fitxategia"</string>
+ <string name="notification_sending" msgid="3035748958534983833">"Bluetooth bidez partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia bidaltzen"</string>
+ <string name="notification_sent" msgid="9218710861333027778">"Bluetooth bidez partekatzea: <xliff:g id="FILE">%1$s</xliff:g> fitxategia bidali da"</string>
<string name="notification_sent_complete" msgid="302943281067557969">"% 100ean osatuta"</string>
- <string name="notification_sent_fail" msgid="6696082233774569445">"Bluetooth bidezko partekatzea: ez da <xliff:g id="FILE">%1$s</xliff:g> fitxategia bidali"</string>
+ <string name="notification_sent_fail" msgid="6696082233774569445">"Bluetooth bidez partekatzea: ez da bidali <xliff:g id="FILE">%1$s</xliff:g> fitxategia"</string>
<string name="download_title" msgid="3353228219772092586">"Fitxategi-transferentzia"</string>
<string name="download_line1" msgid="4926604799202134144">"Igorlea: \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
<string name="download_line2" msgid="5876973543019417712">"Fitxategia: <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -108,8 +107,8 @@
<string name="outbound_history_title" msgid="4279418703178140526">"Irteerako transferentziak"</string>
<string name="no_transfers" msgid="3482965619151865672">"Transferentzia-historia hutsik dago."</string>
<string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"Elementu guztiak zerrendatik garbituko dira."</string>
- <string name="outbound_noti_title" msgid="8051906709452260849">"Bluetooth bidezko partekatzea: fitxategiak bidali dira"</string>
- <string name="inbound_noti_title" msgid="4143352641953027595">"Bluetooth bidezko partekatzea: jasotako fitxategiak"</string>
+ <string name="outbound_noti_title" msgid="8051906709452260849">"Bluetooth bidez partekatzea: fitxategiak bidali dira"</string>
+ <string name="inbound_noti_title" msgid="4143352641953027595">"Bluetooth bidez partekatzea: fitxategiak jaso dira"</string>
<plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
<item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> hutsegite.</item>
<item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_0">%1$d</xliff:g> hutsegite.</item>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Aplikazioaren ikonoa"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth bidez mezuak partekatzeko ezarpenak"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Ezin da hautatu kontua. Ez da erretenik geratzen."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Konektatu da Bluetooth bidezko audioa"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Deskonektatu da Bluetooth bidezko audioa"</string>
</resources>
diff --git a/res/values-eu-rES/strings_pbap_client.xml b/res/values-eu-rES/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-eu-rES/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 2a63e3b..9e390dd 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -18,8 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permlab_bluetoothShareManager" msgid="311492132450338925">"دسترسی به Download Manager."</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"به برنامه برای دسترسی به مدیر BluetoothShare و استفاده از آن برای انتقال فایلها اجازه میدهد."</string>
- <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"دسترسی دستگاه موجود در لیست سفید بلوتوث."</string>
- <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"به برنامه اجازه میدهد تا موقتاً یک دستگاه بلوتوث را در لیست سفید وارد کند، و به آن دستگاه اجازه میدهد بدون تأیید کاربر به این دستگاه فایل ارسال کند."</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"دسترسی دستگاه موجود در فهرست سفید بلوتوث."</string>
+ <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"به برنامه اجازه میدهد تا موقتاً یک دستگاه بلوتوث را در فهرست سفید وارد کند، و به آن دستگاه اجازه میدهد بدون تأیید کاربر به این دستگاه فایل ارسال کند."</string>
<string name="bt_share_picker_label" msgid="6268100924487046932">"بلوتوث"</string>
<string name="unknown_device" msgid="9221903979877041009">"دستگاه ناشناس"</string>
<string name="unknownNumber" msgid="4994750948072751566">"ناشناس"</string>
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"پذیرفتن"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"تأیید"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"هنگام پذیرش یک فایل ورودی از \"<xliff:g id="SENDER">%1$s</xliff:g>\" وقفه زمانی پیش آمد"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"اشتراک بلوتوث: فایل ورودی"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"آیا میخواهید این فایل را دریافت کنید؟"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"فایل ورودی از یک دستگاه دیگر. لطفاً تأیید کنید که میخواهید این فایل را دریافت کنید."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"فایل ورودی"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> آماده ارسال <xliff:g id="FILE">%2$s</xliff:g> است"</string>
<string name="notification_receiving" msgid="4674648179652543984">"اشتراک بلوتوث: در حال دریافت <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"اشتراک بلوتوث: <xliff:g id="FILE">%1$s</xliff:g> دریافت شد"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"اشتراک بلوتوث: فایل <xliff:g id="FILE">%1$s</xliff:g>دریافت نشد"</string>
@@ -107,7 +106,7 @@
<string name="inbound_history_title" msgid="6940914942271327563">"انتقال های ورودی"</string>
<string name="outbound_history_title" msgid="4279418703178140526">"انتقال های خروجی"</string>
<string name="no_transfers" msgid="3482965619151865672">"سابقه انتقال خالی است."</string>
- <string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"همهٔ موارد از لیست پاک میشوند."</string>
+ <string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"همهٔ موارد از فهرست پاک میشوند."</string>
<string name="outbound_noti_title" msgid="8051906709452260849">"اشتراک بلوتوث: فایلهای ارسال شده"</string>
<string name="inbound_noti_title" msgid="4143352641953027595">"اشتراک بلوتوث: فایلهای دریافت شده"</string>
<plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
@@ -118,9 +117,9 @@
<item quantity="one"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> مورد موفق، %2$s</item>
<item quantity="other"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> مورد موفق، %2$s</item>
</plurals>
- <string name="transfer_menu_clear_all" msgid="790017462957873132">"پاک کردن لیست"</string>
+ <string name="transfer_menu_clear_all" msgid="790017462957873132">"پاک کردن فهرست"</string>
<string name="transfer_menu_open" msgid="3368984869083107200">"باز کردن"</string>
- <string name="transfer_menu_clear" msgid="5854038118831427492">"پاک کردن از لیست"</string>
+ <string name="transfer_menu_clear" msgid="5854038118831427492">"پاک کردن از فهرست"</string>
<string name="transfer_clear_dlg_title" msgid="2953444575556460386">"پاک کردن"</string>
<string name="bluetooth_map_settings_save" msgid="7635491847388074606">"ذخیره"</string>
<string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"لغو"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"نماد برنامه"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"تنظیمات اشتراکگذاری پیام بلوتوث"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"انتخاب حساب امکانپذیر نیست. ۰ شیار باقی مانده است"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"بلوتوث صوتی متصل شد"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ارتباط بلوتوث صوتی قطع شد"</string>
</resources>
diff --git a/res/values-fa/strings_pbap.xml b/res/values-fa/strings_pbap.xml
index bc68a89..945d442 100644
--- a/res/values-fa/strings_pbap.xml
+++ b/res/values-fa/strings_pbap.xml
@@ -5,7 +5,7 @@
<string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"به کلید جلسه بلوتوث نیاز است"</string>
<string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"هنگام پذیرش اتصال به %1$s وقفه زمانی ایجاد شده است"</string>
<string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"هنگام ورود کلید جلسه با %1$s وقفه زمانی ایجاد شده است"</string>
- <string name="auth_notif_ticker" msgid="1575825798053163744">"درخواست تأیید اعتبار Obex"</string>
+ <string name="auth_notif_ticker" msgid="1575825798053163744">"درخواست راستیآزمایی Obex"</string>
<string name="auth_notif_title" msgid="7599854855681573258">"کلید جلسه"</string>
<string name="auth_notif_message" msgid="6667218116427605038">"تایپ کلید جلسه برای %1$s"</string>
<string name="defaultname" msgid="4821590500649090078">"کیت خودرو"</string>
diff --git a/res/values-fa/strings_pbap_client.xml b/res/values-fa/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-fa/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 2200229..5f01f6f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Hyväksy"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Yhteys aikakatkaistiin vastaanotettaessa tiedostoa lähettäjältä <xliff:g id="SENDER">%1$s</xliff:g>"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-jako: Saapuva tiedosto"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Haluatko vastaanottaa tämän tiedoston?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Toinen laite lähettää tiedostoa. Vahvista, että haluat vastaanottaa tämän tiedoston."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Saapuva tiedosto"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> on valmis lähettämään kohteen <xliff:g id="FILE">%2$s</xliff:g>."</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-jako: vastaanotetaan tiedostoa <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-jako: <xliff:g id="FILE">%1$s</xliff:g> vastaanotettu"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-jako: tiedostoa <xliff:g id="FILE">%1$s</xliff:g> ei vastaanotettu"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Sovelluskuvake"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetoothin viestinjakoasetukset"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Tilin valinta epäonnistui. 0 paikkaa jäljellä"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-ääni liitetty"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-ääni katkaistu"</string>
</resources>
diff --git a/res/values-fi/strings_pbap_client.xml b/res/values-fi/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-fi/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c303c18..fa0b330 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepter"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Expiration du délai de réception du fichier de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Partage Bluetooth : réception de fichier"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Voulez-vous recevoir ce fichier?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Fichier entrant provenant d\'un autre appareil. Confirmez que vous souhaitez recevoir ce fichier."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Fichier entrant"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> est prêt à envoyer le fichier « <xliff:g id="FILE">%2$s</xliff:g> »"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Partage Bluetooth : réception de <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Partage Bluetooth : <xliff:g id="FILE">%1$s</xliff:g> reçu(s)"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Partage Bluetooth : fichier <xliff:g id="FILE">%1$s</xliff:g> non reçu"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icône de l\'application"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Paramètres de partage des messages par Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Impossible de sélectionner le compte : aucun espace libre."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio Bluetooth connecté"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth déconnecté"</string>
</resources>
diff --git a/res/values-fr-rCA/strings_pbap_client.xml b/res/values-fr-rCA/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-fr-rCA/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index c30a996..3bfbd76 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepter"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Expiration du délai de réception du fichier de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Partage Bluetooth : réception de fichier"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Voulez-vous recevoir ce fichier ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Fichier entrant provenant d\'un autre appareil. Confirmez que vous souhaitez recevoir ce fichier."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Fichier entrant"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> est prêt à envoyer le fichier \"<xliff:g id="FILE">%2$s</xliff:g>\"."</string>
<string name="notification_receiving" msgid="4674648179652543984">"Partage Bluetooth : réception de <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Partage Bluetooth : <xliff:g id="FILE">%1$s</xliff:g> reçu"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Partage Bluetooth : fichier <xliff:g id="FILE">%1$s</xliff:g> non reçu"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icône de l\'application"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Paramètres de partage de la messagerie via le Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Impossible de sélectionner le compte : aucun espace libre."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio Bluetooth connecté"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth déconnecté"</string>
</resources>
diff --git a/res/values-fr/strings_pbap_client.xml b/res/values-fr/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-fr/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index ac07522..530ab22 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceptar"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Aceptar"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Superouse o tempo de espera mentres se aceptaba un ficheiro entrante de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Uso compartido por Bluetooth: ficheiro entrante"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Queres recibir este ficheiro?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Ficheiro entrante doutro dispositivo. Confirma que queres recibir este ficheiro."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Ficheiro entrante"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> está listo para enviar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Uso compartido por Bluetooth: recibindo <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Uso compartido por Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> recibido"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Uso compartido por Bluetooth: ficheiro <xliff:g id="FILE">%1$s</xliff:g> non recibido"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icona da aplicación"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Configuración de mensaxes compartidas por Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Non se pode seleccionar a conta. Quedan 0 rañuras"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Conectouse o audio por Bluetooth"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Desconectouse o audio por Bluetooth"</string>
</resources>
diff --git a/res/values-gl-rES/strings_pbap_client.xml b/res/values-gl-rES/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-gl-rES/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index e1e615d..5e3e24f 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -34,11 +34,10 @@
<string name="incoming_file_confirm_content" msgid="2752605552743148036">"આવનારી ફાઇલ સ્વીકારીએ?"</string>
<string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"નકારો"</string>
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"સ્વીકારો"</string>
- <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ઑકે"</string>
+ <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ઓકે"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" તરફની એક આવનારી ફાઇલ સ્વીકારતી વખતે સમયસમાપ્તિ થઈ હતી"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth શેર: આવનરી ફાઇલો"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"શું તમે આ ફાઇલ પ્રાપ્ત કરવા માંગો છો?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"બીજા ઉપકરણથી આવનારી ફાઇલ. પુષ્ટિ કરો કે તમે આ ફાઇલ પ્રાપ્ત કરવા માંગો છો."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"આવનારી ફાઇલ"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>, <xliff:g id="FILE">%2$s</xliff:g> મોકલવા માટે તૈયાર છે"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth શેર: <xliff:g id="FILE">%1$s</xliff:g> પ્રાપ્ત કરી રહ્યું છે"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth શેર: <xliff:g id="FILE">%1$s</xliff:g> પ્રાપ્ત"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth શેર: ફાઇલ <xliff:g id="FILE">%1$s</xliff:g> પ્રાપ્ત થઈ નથી"</string>
@@ -60,21 +59,21 @@
<string name="download_fail_line1" msgid="3846450148862894552">"ફાઇલ પ્રાપ્ત થઈ નથી"</string>
<string name="download_fail_line2" msgid="8950394574689971071">"ફાઇલ: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="download_fail_line3" msgid="3451040656154861722">"કારણ: <xliff:g id="REASON">%1$s</xliff:g>"</string>
- <string name="download_fail_ok" msgid="1521733664438320300">"ઑકે"</string>
+ <string name="download_fail_ok" msgid="1521733664438320300">"ઓકે"</string>
<string name="download_succ_line5" msgid="4509944688281573595">"ફાઇલ પ્રાપ્ત"</string>
<string name="download_succ_ok" msgid="7053688246357050216">"ખોલો"</string>
<string name="upload_line1" msgid="2055952074059709052">"પ્રતિ: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
<string name="upload_line3" msgid="4920689672457037437">"ફાઇલ પ્રકાર: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
<string name="upload_line5" msgid="7759322537674229752">"ફાઇલ મોકલી રહ્યું છે…"</string>
<string name="upload_succ_line5" msgid="5687317197463383601">"ફાઇલ મોકલી"</string>
- <string name="upload_succ_ok" msgid="7705428476405478828">"ઑકે"</string>
+ <string name="upload_succ_ok" msgid="7705428476405478828">"ઓકે"</string>
<string name="upload_fail_line1" msgid="7899394672421491701">"ફાઇલ \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" ને મોકલાઈ નહોતી."</string>
<string name="upload_fail_line1_2" msgid="2108129204050841798">"ફાઇલ: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="upload_fail_ok" msgid="5807702461606714296">"ફરી પ્રયાસ કરો"</string>
<string name="upload_fail_cancel" msgid="9118496285835687125">"બંધ કરો"</string>
- <string name="bt_error_btn_ok" msgid="5965151173011534240">"ઑકે"</string>
+ <string name="bt_error_btn_ok" msgid="5965151173011534240">"ઓકે"</string>
<string name="unknown_file" msgid="6092727753965095366">"અજાણી ફાઇલ"</string>
- <string name="unknown_file_desc" msgid="480434281415453287">"આ પ્રકારની ફાઇલ હેન્ડલ કરવા માટે કોઈ એપ્લિકેશન નથી. \n"</string>
+ <string name="unknown_file_desc" msgid="480434281415453287">"આ પ્રકારની ફાઇલ હેન્ડલ કરવા માટે કોઈ ઍપ્લિકેશન નથી. \n"</string>
<string name="not_exist_file" msgid="3489434189599716133">"કોઈ ફાઇલ નથી"</string>
<string name="not_exist_file_desc" msgid="4059531573790529229">"આ ફાઇલ અસ્તિત્વમાં નથી. \n"</string>
<string name="enabling_progress_title" msgid="436157952334723406">"કૃપા કરીને રાહ જુઓ..."</string>
@@ -126,7 +125,9 @@
<string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"રદ કરો"</string>
<string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"તમે Bluetooth મારફતે શેર કરવા માગતા હો તે એકાઉન્ટ્સ પસંદ કરો. તમારે હજી પણ કનેક્ટ કરતી વખતે એકાઉન્ટ્સ પરની કોઈપણ અૅક્સેસ સ્વીકારવી પડશે."</string>
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"સ્લોટ્સ બાકી:"</string>
- <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"એપ્લિકેશન આયકન"</string>
+ <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ઍપ્લિકેશન આયકન"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth સંદેશ શેરિંગ સેટિંગ્સ"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"એકાઉન્ટ પસંદ કરી શકાતું નથી. 0 સ્લોટ્સ બાકી"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth ઑડિઓ કનેક્ટ થયું"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth ઑડિઓ ડિસ્કનેક્ટ થયું"</string>
</resources>
diff --git a/res/values-gu-rIN/strings_pbap_client.xml b/res/values-gu-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-gu-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-gu-rIN/test_strings.xml b/res/values-gu-rIN/test_strings.xml
index 7ca72cc..63d58cc 100644
--- a/res/values-gu-rIN/test_strings.xml
+++ b/res/values-gu-rIN/test_strings.xml
@@ -7,7 +7,7 @@
<string name="update_record" msgid="2480425402384910635">"રેકોર્ડની પુષ્ટિ કરો"</string>
<string name="ack_record" msgid="6716152390978472184">"Ack રેકોર્ડ"</string>
<string name="deleteAll_record" msgid="4383349788485210582">"તમામ રેકોર્ડ કાઢી નાખો"</string>
- <string name="ok_button" msgid="6519033415223065454">"ઑકે"</string>
+ <string name="ok_button" msgid="6519033415223065454">"ઓકે"</string>
<string name="delete_record" msgid="4645040331967533724">"રેકોર્ડ કાઢી નાખો"</string>
<string name="start_server" msgid="9034821924409165795">"TCP સર્વર પ્રારંભ કરો"</string>
<string name="notify_server" msgid="4369106744022969655">"TCP સર્વરને સૂચિત કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index e370f75..ac0397a 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"स्वीकारें"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ठीक है"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" से आने वाली फ़ाइल स्वीकार करते समय समयबाह्य हुआ."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ब्लूटूथ शेयर: आने वाली फ़ाइल"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"क्या आप यह फ़ाइल प्राप्त करना चाहते हैं?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"दूसरे डिवाइस से कोई फ़ाइल आ रही है. सुनिश्चित करें कि आप यह फ़ाइल प्राप्त करना चाहते हैं."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"आवक फ़ाइल"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> भेजने के लिए तैयार है"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त कर रहा है"</string>
<string name="notification_received" msgid="3324588019186687985">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ब्लूटूथ शेयर: फ़ाइल <xliff:g id="FILE">%1$s</xliff:g> प्राप्त नहीं हुई"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ऐप्लिकेशन आइकन"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ब्लूटूथ संदेश साझाकरण सेटिंग"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"खाता नहीं चुना जा सकता. 0 स्लॉट शेष"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ब्लूटूथ ऑडियो कनेक्ट किया गया"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ब्लूटूथ ऑडियो डिस्कनेक्ट किया गया"</string>
</resources>
diff --git a/res/values-hi/strings_pbap_client.xml b/res/values-hi/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-hi/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index ed08875..7d2d4eb 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Prihvaćam"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"U redu"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Za vrijeme primanja datoteke koju šalje \"<xliff:g id="SENDER">%1$s</xliff:g>\" došlo je do privremenog prekida"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth dijeljenje: dolazna datoteka"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Želite li primiti ovu datoteku?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Dolazna datoteka s drugog uređaja. Potvrdite da želite primiti tu datoteku."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Dolazna datoteka"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> može poslati datoteku <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Dijeljenje Bluetoothom: Primanje datoteke <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Dijeljenje Bluetoothom: Primljena datoteka <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Dijeljenje Bluetoothom: Datoteka <xliff:g id="FILE">%1$s</xliff:g> nije primljena"</string>
@@ -131,4 +130,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikacije"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Postavke dijeljenja poruka putem Bluetootha"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Račun nije moguće odabrati. Nema više nijednog mjesta"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth Audio povezan"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Veza Bluetooth Audija prekinuta"</string>
</resources>
diff --git a/res/values-hr/strings_pbap_client.xml b/res/values-hr/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-hr/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 6ab7920..63df146 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Fogadás"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Időtúllépés történt \"<xliff:g id="SENDER">%1$s</xliff:g>\" feladótól érkező fájl fogadása során"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-megosztás: beérkező fájl"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Szeretné fogadni ezt a fájlt?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Fájl érkezik egy másik készülékről. Erősítse meg, hogy szeretné megkapni ezt a fájlt."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Beérkező fájl"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> készen áll a következő küldésére: <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth megosztás: <xliff:g id="FILE">%1$s</xliff:g> fogadása"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth megosztás: <xliff:g id="FILE">%1$s</xliff:g> fogadva"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth megosztás: <xliff:g id="FILE">%1$s</xliff:g> fájl fogadása nem sikerült"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Alkalmazás ikonja"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth-kapcsolaton keresztüli üzenetmegosztás beállításai"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"A fiók kiválasztása sikertelen. 0 hely maradt"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth audió csatlakoztatva"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audió leválasztva"</string>
</resources>
diff --git a/res/values-hu/strings_pbap_client.xml b/res/values-hu/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-hu/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 4c4927f..efef63b 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -23,8 +23,8 @@
<string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
<string name="unknown_device" msgid="9221903979877041009">"Անհայտ սարք"</string>
<string name="unknownNumber" msgid="4994750948072751566">"Անհայտ"</string>
- <string name="airplane_error_title" msgid="2683839635115739939">"Ինքնաթիռային ռեժիմ"</string>
- <string name="airplane_error_msg" msgid="8698965595254137230">"Դուք չեք կարող օգտվել Bluetooth-ից ինքնաթիռային ռեժիմում:"</string>
+ <string name="airplane_error_title" msgid="2683839635115739939">"Ինքնաթիռի ռեժիմ"</string>
+ <string name="airplane_error_msg" msgid="8698965595254137230">"Դուք չեք կարող օգտվել Bluetooth-ից Ինքնաթիռի ռեժիմում:"</string>
<string name="bt_enable_title" msgid="8657832550503456572"></string>
<string name="bt_enable_line1" msgid="7203551583048149">"Bluetooth ծառայություններից օգտվելու համար նախ պետք է միացնեք Bluetooth-ը:"</string>
<string name="bt_enable_line2" msgid="4341936569415937994">"Միացնե՞լ Bluetooth-ը հիմա:"</string>
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Ընդունել"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Լավ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"«<xliff:g id="SENDER">%1$s</xliff:g>»-ից մուտքային ֆայլի ընդունման ժամանակը սպառվեց"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth համօգտագործում՝ մուտքային ֆայլ"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Ցանկանո՞ւմ եք ստանալ այս ֆայլը:"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Առկա է մուտքային ֆայլ այլ սարքից: Հաստատեք, որ ցանկանում եք ստանալ այս ֆայլը:"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Մուտքային ֆայլ"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>-ը պատրաստ է ուղարկելու <xliff:g id="FILE">%2$s</xliff:g> ֆայլը"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth համօգտագործում՝ <xliff:g id="FILE">%1$s</xliff:g>-ը ստացվում է"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth համօգտագործում՝ ստացվեց <xliff:g id="FILE">%1$s</xliff:g>-ը"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth համօգտագործում՝ <xliff:g id="FILE">%1$s</xliff:g> ֆայլը չի ստացվել"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Հավելվածի պատկերակը"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Հաղորդագրությունների համօգտագործման Bluetooth-ի կարգավորումներ"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Հնարավոր չէ ընտրել հաշիվը: Տող չի մնացել"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth աուդիոն կապակցված է"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth աուդիոն անջատված է"</string>
</resources>
diff --git a/res/values-hy-rAM/strings_pbap_client.xml b/res/values-hy-rAM/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-hy-rAM/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 3ed9b12..fd81046 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Terima"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Oke"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Terjadi waktu tunggu saat menerima file masuk dari \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Berbagi bluetooth: File masuk"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Apakah Anda ingin menerima file ini?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"File masuk dari perangkat lain. Konfirmasikan bahwa Anda ingin menerima file ini."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"File masuk"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> siap mengirim <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Berbagi Bluetooth: Menerima <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Berbagi Bluetooth: Telah menerima <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Berbagi Bluetooth: File <xliff:g id="FILE">%1$s</xliff:g> tidak diterima"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikon Aplikasi"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Setelan Berbagi Pesan Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Tidak dapat memilih akun. Sisa 0 slot"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth audio terhubung"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio terputus"</string>
</resources>
diff --git a/res/values-in/strings_pbap_client.xml b/res/values-in/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-in/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 4e75618..c0646b2 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Samþykkja"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Í lagi"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Rann út á tíma við að samþykkja skrá sem „<xliff:g id="SENDER">%1$s</xliff:g>“ sendi"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-deiling: Skrá berst"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Viltu taka á móti þessari skrá?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Skrá berst frá öðru tæki. Staðfestu að þú viljir taka á móti þessari skrá."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Skrá á innleið"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> er tilbúin(n) að senda <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-deiling: Tekur á móti <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-deiling: Skráin <xliff:g id="FILE">%1$s</xliff:g> var móttekin"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-deiling: Skráin <xliff:g id="FILE">%1$s</xliff:g> var ekki móttekin"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Forritstákn"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Deilingarstillingar Bluetooth-skilaboða"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Ekki er hægt að velja reikning. Engin hólf eftir"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-hljóð tengt"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-hljóð aftengt"</string>
</resources>
diff --git a/res/values-is-rIS/strings_pbap_client.xml b/res/values-is-rIS/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-is-rIS/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 7645212..f922d0d 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accetta"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Si è verificato un timeout durante l\'accettazione di un file in arrivo da \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth: file in arrivo"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Vuoi ricevere questo file?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"File in arrivo da un altro dispositivo. Conferma che vuoi ricevere il file."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"File in arrivo"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> è pronto per inviare <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth: ricezione <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> ricevuto"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth: file <xliff:g id="FILE">%1$s</xliff:g> non ricevuto"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icona applicazione"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Impostazioni di condivisione dei messaggi Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Impossibile selezionare l\'account. Nessuno slot rimanente"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio Bluetooth connesso"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth disconnesso"</string>
</resources>
diff --git a/res/values-it/strings_pbap_client.xml b/res/values-it/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-it/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 016977e..0aa6ac3 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"קבל"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"אישור"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"תם הזמן הקצוב לקבלת קובץ נכנס מאת \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"שיתוף Bluetooth: קובץ נכנס"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"האם ברצונך לקבל קובץ זה?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"מכשיר אחר שולח אליך קובץ. אשר את קבלת הקובץ."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"קובץ שהתקבל"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> מוכן לשלוח את <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"שיתוף Bluetooth: מקבל את <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"שיתוף Bluetooth: התקבל <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"שיתוף Bluetooth: הקובץ <xliff:g id="FILE">%1$s</xliff:g> לא התקבל"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"סמל אפליקציה"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"הגדרות לשיתוף הודעות ב-Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"לא ניתן לבחור חשבון. נותרו 0 מקומות"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"אודיו Bluetooth מחובר"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"אודיו Bluetooth מנותק"</string>
</resources>
diff --git a/res/values-iw/strings_pbap_client.xml b/res/values-iw/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-iw/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 7e87dcf..e12a8d5 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -21,7 +21,7 @@
<string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Bluetooth端末によるアクセスを許可します。"</string>
<string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Bluetooth端末によるアクセスを一時的に許可して、ユーザーの確認を受けずにその端末からこの端末にファイルを送信することをアプリに許可します。"</string>
<string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
- <string name="unknown_device" msgid="9221903979877041009">"不明な携帯端末"</string>
+ <string name="unknown_device" msgid="9221903979877041009">"不明なモバイル端末"</string>
<string name="unknownNumber" msgid="4994750948072751566">"不明"</string>
<string name="airplane_error_title" msgid="2683839635115739939">"機内モード"</string>
<string name="airplane_error_msg" msgid="8698965595254137230">"機内モードではBluetoothを使用できません。"</string>
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"承諾"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"「<xliff:g id="SENDER">%1$s</xliff:g>」からのファイル受信中に接続がタイムアウトしました"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth共有: ファイル着信"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"このファイルを受信しますか?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"別の端末からファイルが着信しました。このファイルを受信するかどうか確認してください。"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"着信ファイル"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>さんが<xliff:g id="FILE">%2$s</xliff:g>を送信できるようになりました"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth共有: <xliff:g id="FILE">%1$s</xliff:g>を受信中"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth共有: <xliff:g id="FILE">%1$s</xliff:g>を受信済み"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth共有: ファイル<xliff:g id="FILE">%1$s</xliff:g>の受信に失敗"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"アプリアイコン"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetoothメッセージ共有の設定"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"アカウントを選択できません。残りスロット数が0です"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth オーディオは接続されています"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth オーディオは接続を解除されています"</string>
</resources>
diff --git a/res/values-ja/strings_pbap_client.xml b/res/values-ja/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ja/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 888b87c..12dffc8 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"მიღება"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"კარგი"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"„<xliff:g id="SENDER">%1$s</xliff:g>“-გან ფაილის შემომავალი ფაილის მიღების დრო ამოიწურა"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth გაზიარება: შემომავალი ფაილი"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"გსურთ ამ ფაილის მიღება?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"შემომავალი ფაილი სხვა მოწყობილობიდან. დაადასტურეთ, რომ გსურთ ფაილის მიღება."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"შემომავალი ფაილი"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> მზად არის <xliff:g id="FILE">%2$s</xliff:g>-ის გასაგზავნად"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth გაზიარება: <xliff:g id="FILE">%1$s</xliff:g> ფაილის მიღება"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth გაზიარება: მიღებულია <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth გაზიარება: ფაილი <xliff:g id="FILE">%1$s</xliff:g> არ მიღებულა"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"აპლიკაციის ხატულა"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth შეტყობინების გაზიარების პარამეტრები"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ანგარიშის არჩევა ვერ ხერხდება. დარჩენილია 0 სლოტი"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth აუდიო დაკავშირებულია"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth აუდიო გათიშულია"</string>
</resources>
diff --git a/res/values-ka-rGE/strings_pbap_client.xml b/res/values-ka-rGE/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ka-rGE/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index e726331..f806adb 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -28,7 +28,7 @@
<string name="bt_enable_title" msgid="8657832550503456572"></string>
<string name="bt_enable_line1" msgid="7203551583048149">"Bluetooth қызметтерін қолдану үшін, алдымен Bluetooth байланысын қосу қажет."</string>
<string name="bt_enable_line2" msgid="4341936569415937994">"Bluetooth байланысы қазір қосылсын ба?"</string>
- <string name="bt_enable_cancel" msgid="1988832367505151727">"Өшіру"</string>
+ <string name="bt_enable_cancel" msgid="1988832367505151727">"Жабу"</string>
<string name="bt_enable_ok" msgid="3432462749994538265">"Қосу"</string>
<string name="incoming_file_confirm_title" msgid="8139874248612182627">"Файл жіберу"</string>
<string name="incoming_file_confirm_content" msgid="2752605552743148036">"Кіріс файлды қабылдау керек пе?"</string>
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Қабылдау"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Жарайды"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" жіберген файлды қабылдау барысында уақыт аяқталды."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth бөлісу: Келген файл"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Бұл файлды алуды қалайсыз ба?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Басқа құрылғы арқылы келген файл. Бұл файлды қабылдауды қалайтыныңызды растаңыз."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Кіріс файл"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> жіберуге дайын"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth бөлісу: <xliff:g id="FILE">%1$s</xliff:g> файлын қабылдауда"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth бөлісу: <xliff:g id="FILE">%1$s</xliff:g> файлы қабылданды"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth бөлісу: <xliff:g id="FILE">%1$s</xliff:g> файлы қабылданбады"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Қолданба белгішесі"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth арқылы хабар бөлісу параметрлері"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Есептік жазбаны таңдау мүмкін емес. 0 слот қалды"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth дыбысы қосылды"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth дыбысы ажыратылды"</string>
</resources>
diff --git a/res/values-kk-rKZ/strings_pbap_client.xml b/res/values-kk-rKZ/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-kk-rKZ/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 7806e0b..fb9328f 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ទទួល"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"យល់ព្រម"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"អស់ពេលទទួលឯកសារចូលពី \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ការចែករំលែកប៊្លូធូស៖ ឯកសារចូល"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"តើអ្នកចង់ទទួលឯកសារនេះឬ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ឯកសារចូលពីឧបករណ៍ផ្សេងទៀត។ បញ្ជាក់ថាអ្នកចង់ទទួលបានឯកសារនេះ។"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ឯកសារចូល"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ត្រៀមរួចរាល់ក្នុងការផ្ញើ <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ការចែករំលែកប៊្លូធូស៖ ទទួល <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"ការចែករំលែកប៊្លូធូស៖ បានទទួល <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ការចែករំលែកប៊្លូធូស៖ មិនបានទទួលឯកសារ <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"រូបតំណាងកម្មវិធី"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"កំណត់ការចែករំលែកសារតាមប៊្លូធូស"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"មិនអាចជ្រើសគណនីទេ អស់រន្ធនៅសល់ហើយ។"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"សំឡេងប៊្លូធូសត្រូវបានភ្ជាប់"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"សំឡេងប៊្លូធូសត្រូវបានផ្តាច់"</string>
</resources>
diff --git a/res/values-km-rKH/strings_pbap_client.xml b/res/values-km-rKH/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-km-rKH/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 48e5445..3ad57a9 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ಸ್ವೀಕರಿಸಿ"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ಸರಿ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" ಅವರಿಂದ ಫೈಲ್ ಸ್ವೀಕರಿಸುವಾಗ ಕಾಲಾವಧಿ ಮುಕ್ತಾಯಗೊಂಡಿದೆ"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ಬ್ಲೂಟೂತ್ ಹಂಚಿಕೆ: ಒಳಬರುವ ಫೈಲ್"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ಈ ಫೈಲ್ ಅನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಬಯಸುವಿರಾ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ಮತ್ತೊಂದು ಸಾಧನದಿಂದ ಒಳಬರುವ ಫೈಲ್. ಈ ಫೈಲ್ ಸ್ವೀಕರಿಸಲು ನೀವು ಬಯಸುತ್ತೀರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿ."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ಒಳಬರುತ್ತಿರುವ ಫೈಲ್"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ಅವರು <xliff:g id="FILE">%2$s</xliff:g> ಫೈಲ್ ಕಳುಹಿಸಲು ಸಿದ್ಧವಾಗಿದ್ದಾರೆ"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ಬ್ಲೂಟೂತ್ ಹಂಚಿಕೆ: <xliff:g id="FILE">%1$s</xliff:g> ಸ್ವೀಕರಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="notification_received" msgid="3324588019186687985">"ಬ್ಲೂಟೂತ್ ಹಂಚಿಕೆ: <xliff:g id="FILE">%1$s</xliff:g> ಸ್ವೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ಬ್ಲೂಟೂತ್ ಹಂಚಿಕೆ: ಫೈಲ್ <xliff:g id="FILE">%1$s</xliff:g> ಸ್ವೀಕರಿಸಿಲ್ಲ"</string>
@@ -70,7 +69,7 @@
<string name="upload_succ_ok" msgid="7705428476405478828">"ಸರಿ"</string>
<string name="upload_fail_line1" msgid="7899394672421491701">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" ಇವರಿಗೆ ಫೈಲ್ ಕಳುಹಿಸಲಾಗಲಿಲ್ಲ."</string>
<string name="upload_fail_line1_2" msgid="2108129204050841798">"ಫೈಲ್: <xliff:g id="FILE">%1$s</xliff:g>"</string>
- <string name="upload_fail_ok" msgid="5807702461606714296">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸು"</string>
+ <string name="upload_fail_ok" msgid="5807702461606714296">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="upload_fail_cancel" msgid="9118496285835687125">"ಮುಚ್ಚು"</string>
<string name="bt_error_btn_ok" msgid="5965151173011534240">"ಸರಿ"</string>
<string name="unknown_file" msgid="6092727753965095366">"ಅಪರಿಚಿತ ಫೈಲ್"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ಅಪ್ಲಿಕೇಶನ್ ಐಕಾನ್"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ಬ್ಲೂಟೂತ್ ಸಂದೇಶ ಹಂಚಿಕೆ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ಖಾತೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. 0 ಸ್ಲಾಟ್ಗಳು ಉಳಿದಿವೆ"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೊ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೊ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ"</string>
</resources>
diff --git a/res/values-kn-rIN/strings_pbap_client.xml b/res/values-kn-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-kn-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index b9c4e58..b780341 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"수락"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"확인"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\'<xliff:g id="SENDER">%1$s</xliff:g>\'님이 보내는 파일을 수락하는 동안 제한 시간을 초과했습니다."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"블루투스 공유: 수신 파일"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"이 파일을 받으시겠습니까?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"다른 기기에서 파일을 전송 중입니다. 이 파일을 수신할 것인지 확인하세요."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"수신 파일"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>님이 <xliff:g id="FILE">%2$s</xliff:g>을(를) 보낼 준비가 되었습니다."</string>
<string name="notification_receiving" msgid="4674648179652543984">"블루투스 공유: <xliff:g id="FILE">%1$s</xliff:g> 받는 중"</string>
<string name="notification_received" msgid="3324588019186687985">"블루투스 공유: <xliff:g id="FILE">%1$s</xliff:g> 받음"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"블루투스 공유: <xliff:g id="FILE">%1$s</xliff:g> 파일이 수신되지 않았습니다."</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"애플리케이션 아이콘"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"블루투스 메시지 공유 설정"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"계정을 선택할 수 없습니다. 남은 슬롯이 없습니다."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"블루투스 오디오가 연결됨"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"블루투스 오디오가 연결 해제됨"</string>
</resources>
diff --git a/res/values-ko/strings_pbap_client.xml b/res/values-ko/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ko/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 21ec2ca..78f7ce2 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Кабыл алуу"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" жөнөткөн файлды алуу мөөнөтү өтүп кетти."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth бөлүшүү: Алынган файл"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Бул файлды алгыңыз келеби?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Башка түзмөктөн келген файл. Бул файлды алгыңыз келгенин ырастаңыз."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Кирүүчү файл"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> жөнөтүүгө даяр"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth бөлүшүү: <xliff:g id="FILE">%1$s</xliff:g> алынууда"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth бөлүшүү: <xliff:g id="FILE">%1$s</xliff:g> алынды"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth бөлүшүү: <xliff:g id="FILE">%1$s</xliff:g> алынган жок"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Колдонмонун сөлөкөтү"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth билдирүү бөлүшүү жөндөөлөрү"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Каттоо эсебин тандоо мүмкүн эмес. 0 көзөнөк калды"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth аудио туташты"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth аудио ажыратылды"</string>
</resources>
diff --git a/res/values-ky-rKG/strings_pbap_client.xml b/res/values-ky-rKG/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ky-rKG/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 2ac6722..6279cb0 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ຮັບເອົາ"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ຕົກລົງ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"ເກີດມີການໝົດເວລາໃນລະຫວ່າງທີ່ກຳລັງຮັບເອົາໄຟລ໌ທີ່ເຂົ້າມາຈາກ \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ແບ່ງປັນໃນ Bluetooth: ມີໄຟລ໌ເຂົ້າມາ"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ທ່ານຕ້ອງການທີ່ຮັບເອົາໄຟລ໌ນີ້ບໍ່?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ມີໄຟລ໌ກຳລັງເຂົ້າມາຈາກອຸປະກອນອື່ນ. ຢືນຢັນວ່າທ່ານຕ້ອງການຮັບເອົາໄຟລ໌ນີ້."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ໄຟລ໌ເຂົ້າມາ"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ພ້ອມທີ່ຈະສົ່ງ <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ແບ່ງປັນໃນ Bluetooth: ກຳລັງຮັບເອົາ <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"ແບ່ງປັນໃນ Bluetooth: ໄດ້ຮັບ <xliff:g id="FILE">%1$s</xliff:g> ແລ້ວ"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ແບ່ງປັນໃນ Bluetooth: ບໍ່ໄດ້ຮັບ <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ໄອຄອນແອັບພລິເຄຊັນ"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ການຕັ້ງຄ່າແບ່ງປັນຂໍ້ຄວາມ Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ບໍ່ສາມາດເລືອກບັນຊີໄດ້. ເຫຼືອ 0 ຊ່ອງ"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ເຊື່ອມຕໍ່ສຽງ Bluetooth ແລ້ວ"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ຕັດການເຊື່ອມຕໍ່ສຽງ Bluetooth ແລ້ວ"</string>
</resources>
diff --git a/res/values-lo-rLA/strings_pbap_client.xml b/res/values-lo-rLA/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-lo-rLA/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 9c6a34d..69cf285 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Pasiekti atsisiuntimo tvarkyklę."</string>
+ <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Pasiekti atsisiuntimo tvarkytuvę."</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Leidžiama programai pasiekti „BluetoothShare“ tvarkyklę ir naudoti ją failams perkelti."</string>
<string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Į baltąjį sąrašą įtraukto „Bluetooth“ įrenginio prieiga."</string>
<string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Leidžiama programai laikinai į baltąjį sąrašą įtraukti „Bluetooth“ įrenginį, suteikiant jam galimybę siųsti failus į šį įrenginį be naudotojo patvirtinimo."</string>
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Priimti"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Gerai"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Baigėsi laikas priimant gaunamą failą iš <xliff:g id="SENDER">%1$s</xliff:g>"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"„Bluetooth“ bendrinimas: gaunamas failas"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Ar norite gauti šį failą?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Gaunamas failas iš kito įrenginio. Patvirtinkite, kad norite gauti šį failą."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Gaunamas failas"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> pasiruošęs (-usi) siųsti <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"„Bluetooth“ bendrinimas: gaunamas <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"„Bluetooth“ bendrinimas: <xliff:g id="FILE">%1$s</xliff:g> gautas"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"„Bluetooth“ bendrinimas: <xliff:g id="FILE">%1$s</xliff:g> failas negautas"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Programos piktograma"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"„Bluetooth“ pranešimų bendrinimo nustatymai"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Negalima pasirinkti paskyros. Neliko jokių sričių"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"„Bluetooth“ garsas prijungtas"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"„Bluetooth“ garsas atjungtas"</string>
</resources>
diff --git a/res/values-lt/strings_pbap_client.xml b/res/values-lt/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-lt/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index aabdd4d..7a68240 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Piekrist"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Labi"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Saņemot ienākošu failu no saņēmēja <xliff:g id="SENDER">%1$s</xliff:g>, radās noildze."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth Share: ienākošais fails"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Vai vēlaties saņemt šo failu?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"No citas ierīces tiek sūtīts fails. Apstipriniet, ka vēlaties saņemt šo failu."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Ienākošais fails"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Sūtītājs <xliff:g id="SENDER">%1$s</xliff:g> ir gatavs nosūtīt failu <xliff:g id="FILE">%2$s</xliff:g>."</string>
<string name="notification_receiving" msgid="4674648179652543984">"Kopīgošana, izmantojot Bluetooth: notiek faila <xliff:g id="FILE">%1$s</xliff:g> saņemšana"</string>
<string name="notification_received" msgid="3324588019186687985">"Kopīgošana, izmantojot Bluetooth: saņemts fails <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Kopīgošana, izmantojot Bluetooth: fails <xliff:g id="FILE">%1$s</xliff:g> netika saņemts"</string>
@@ -131,4 +130,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Lietojumprogrammas ikona"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth ziņojumu kopīgošanas iestatījumi"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Nevar atlasīt kontu. Atlicis 0 slotu."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Izveidots savienojums ar Bluetooth audio"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Pārtraukts savienojums ar Bluetooth audio"</string>
</resources>
diff --git a/res/values-lv/strings_pbap_client.xml b/res/values-lv/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-lv/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 49d7f8a..550b62a 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Прифати"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Во ред"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Времето истече додека се прифаќаше дојдовната датотека од „<xliff:g id="SENDER">%1$s</xliff:g>“."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Сподели преку Bluetooth: Дојдовна датотека"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Сакате да ја примите оваа датотека?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Дојдовна датотека од друг уред. Потврдете дека сакате да ја примите оваа датотека."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Дојдовна датотека"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> е подготвен за испраќање на <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Сподели преку Bluetooth: Се прима <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Сподели преку Bluetooth: Примена <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Сподели преку Bluetooth: Датотеката <xliff:g id="FILE">%1$s</xliff:g> не е примена."</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Икона на апликацијата"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Поставки за споделување пораки преку Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Не може да се избере сметка. 0 слободни отвори"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Аудиото преку Bluetooth е поврзано"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Аудиото преку Bluetooth е исклучено"</string>
</resources>
diff --git a/res/values-mk-rMK/strings_pbap_client.xml b/res/values-mk-rMK/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-mk-rMK/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 7884501..7490390 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"സ്വീകരിക്കുക"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ശരി"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" എന്നയാളിൽ നിന്നും ഒരു ഇൻകമിംഗ് ഫയൽ സ്വീകരിക്കുമ്പോൾ ഒരു കാലഹരണപ്പെടൽ സംഭവിച്ചു"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ബ്ലൂടൂത്ത് പങ്കിടൽ: ഇൻകമിംഗ് ഫയൽ"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"നിങ്ങൾക്ക് ഈ ഫയൽ നേടണോ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"മറ്റൊരു ഉപകരണത്തിൽ നിന്നുള്ള ഇൻകമിംഗ് ഫയൽ. ഈ ഫയൽ നിങ്ങൾക്ക് നേടണമെങ്കിൽ സ്ഥിരീകരിക്കുക."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ഇൻകമിംഗ് ഫയൽ"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="FILE">%2$s</xliff:g> അയയ്ക്കാൻ <xliff:g id="SENDER">%1$s</xliff:g> തയ്യാറാണ്"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ബ്ലൂടൂത്ത് പങ്കിടൽ: <xliff:g id="FILE">%1$s</xliff:g> എന്ന ഫയൽ ലഭിക്കുന്നു"</string>
<string name="notification_received" msgid="3324588019186687985">"ബ്ലൂടൂത്ത് പങ്കിടൽ: <xliff:g id="FILE">%1$s</xliff:g> ലഭിച്ചു"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ബ്ലൂടൂത്ത് പങ്കിടൽ: <xliff:g id="FILE">%1$s</xliff:g> എന്ന ഫയൽ ലഭിച്ചില്ല."</string>
@@ -127,4 +126,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"അപ്ലിക്കേഷൻ ഐക്കൺ"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth സന്ദേശ പങ്കിടൽ ക്രമീകരണം"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"അക്കൗണ്ട് തിരഞ്ഞെടുക്കാനാകില്ല. 0 സ്ലോട്ടുകൾ ശേഷിക്കുന്നു"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth ഓഡിയോ കണക്റ്റുചെയ്തു"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth ഓഡിയോ വിച്ഛേദിച്ചു"</string>
</resources>
diff --git a/res/values-ml-rIN/strings_pbap_client.xml b/res/values-ml-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ml-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 038f476..e312bb7 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -34,11 +34,10 @@
<string name="incoming_file_confirm_content" msgid="2752605552743148036">"Ирж байгаа файлыг авах уу?"</string>
<string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Татгалзах"</string>
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Зөвшөөрөх"</string>
- <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Тийм"</string>
+ <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\"-с ирж байгаа файлыг зөвшөөрөх явцад хугацаа хэтэрсэн байна"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth хуваалцах: Ирж байгаа файл"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Та энэ файлыг хүлээн авахыг хүсэж байна уу?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Өөр төхөөрөмжөөс ирж байгаа файл. Та энэ файлыг хүлээн авахыг хүсэж байгаагаа баталгаажуулна уу."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Ирж буй файл"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> нь <xliff:g id="FILE">%2$s</xliff:g>-г илгээхэд бэлэн"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth хуваалцах: Хүлээн авч байна <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth хуваалцах: Хүлээн авсан <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth хуваалцах: Файл <xliff:g id="FILE">%1$s</xliff:g> хүлээж аваагүй"</string>
@@ -60,19 +59,19 @@
<string name="download_fail_line1" msgid="3846450148862894552">"Файлыг хүлээж аваагүй"</string>
<string name="download_fail_line2" msgid="8950394574689971071">"Файл: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="download_fail_line3" msgid="3451040656154861722">"Шалтгаан: <xliff:g id="REASON">%1$s</xliff:g>"</string>
- <string name="download_fail_ok" msgid="1521733664438320300">"Тийм"</string>
+ <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
<string name="download_succ_line5" msgid="4509944688281573595">"Файлыг хүлээж авсан"</string>
<string name="download_succ_ok" msgid="7053688246357050216">"Нээх"</string>
<string name="upload_line1" msgid="2055952074059709052">"Хэнд: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
<string name="upload_line3" msgid="4920689672457037437">"Файлын хэлбэр: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
<string name="upload_line5" msgid="7759322537674229752">"Файл илгээж байна…"</string>
<string name="upload_succ_line5" msgid="5687317197463383601">"Файлыг илгээсэн"</string>
- <string name="upload_succ_ok" msgid="7705428476405478828">"Тийм"</string>
+ <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
<string name="upload_fail_line1" msgid="7899394672421491701">"Файл \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" руу илгээгдсэнгүй."</string>
<string name="upload_fail_line1_2" msgid="2108129204050841798">"Файл: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="upload_fail_ok" msgid="5807702461606714296">"Дахин оролдоно уу"</string>
<string name="upload_fail_cancel" msgid="9118496285835687125">"Хаах"</string>
- <string name="bt_error_btn_ok" msgid="5965151173011534240">"Тийм"</string>
+ <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
<string name="unknown_file" msgid="6092727753965095366">"Үл мэдэгдэх файл"</string>
<string name="unknown_file_desc" msgid="480434281415453287">"Ийм төрлийн файлыг нээх апп байхгүй байна. \n"</string>
<string name="not_exist_file" msgid="3489434189599716133">"Файл байхгүй"</string>
@@ -92,7 +91,7 @@
<string name="status_pending" msgid="2503691772030877944">"Файл дамжуулалт хараахан эхлээгүй байна."</string>
<string name="status_running" msgid="6562808920311008696">"Файл дамжуулалт үргэлжилж байна."</string>
<string name="status_success" msgid="239573225847565868">"Файл дамжуулалт амжилттай дууслаа."</string>
- <string name="status_not_accept" msgid="1695082417193780738">"Контент дэмжигдэхгүй байна."</string>
+ <string name="status_not_accept" msgid="1695082417193780738">"Агуулга дэмжигдэхгүй байна."</string>
<string name="status_forbidden" msgid="613956401054050725">"Дамжуулалтыг хүлээн авагч төхөөрөмжөөс хориглосон."</string>
<string name="status_canceled" msgid="6664490318773098285">"Дамжуулалтыг хэрэглэгч цуцалсан."</string>
<string name="status_file_error" msgid="3671917770630165299">"Хадгалах сангийн асуудал."</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Аппликешний дүрс"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth мессеж хуваалцах тохиргоо"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Дансыг сонгох боломжгүй. 0 слот үлдсэн"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth аудиог холбосон"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth аудиог салгасан"</string>
</resources>
diff --git a/res/values-mn-rMN/strings_pbap_client.xml b/res/values-mn-rMN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-mn-rMN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-mn-rMN/test_strings.xml b/res/values-mn-rMN/test_strings.xml
index 43e8d2e..8a89e5a 100644
--- a/res/values-mn-rMN/test_strings.xml
+++ b/res/values-mn-rMN/test_strings.xml
@@ -7,7 +7,7 @@
<string name="update_record" msgid="2480425402384910635">"Бичлэгийг баталгаажуулах"</string>
<string name="ack_record" msgid="6716152390978472184">"Ack бичлэг"</string>
<string name="deleteAll_record" msgid="4383349788485210582">"Бүх бичлэгийг устгах"</string>
- <string name="ok_button" msgid="6519033415223065454">"Тийм"</string>
+ <string name="ok_button" msgid="6519033415223065454">"OK"</string>
<string name="delete_record" msgid="4645040331967533724">"Бичлэгийг устгах"</string>
<string name="start_server" msgid="9034821924409165795">"TCP серверийг эхлүүлэх"</string>
<string name="notify_server" msgid="4369106744022969655">"TCP серверт мэдэгдэх"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 61bd687..bd43ddd 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"स्वीकारा"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ठीक"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" कडील फाइल स्वीकार करताना वेळ संपली."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ब्लूटुथ शेअर: येणारी फाइल"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"आपण ही फाइल प्राप्त करू इच्छिता?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"दुसर्या डिव्हाइस वरील येणारी फाइल. ही फाइल आपण प्राप्त करू इच्छिता याची पुष्टी करा."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"येणारी फाईल"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="FILE">%2$s</xliff:g> पाठविण्यासाठी <xliff:g id="SENDER">%1$s</xliff:g> तयार आहे"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ब्लूटुथ शेअर: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त करीत आहे"</string>
<string name="notification_received" msgid="3324588019186687985">"ब्लूटुथ शेअर: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त केली"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ब्लूटुथ शेअर: <xliff:g id="FILE">%1$s</xliff:g> फाइल प्राप्त केली नाही"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"अनुप्रयोग चिन्ह"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ब्लूटुथ संदेश सामायिकरण सेटिंग्ज"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"खाते निवडू शकत नाही. 0 स्लॉट शिल्लक"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ब्लूटुथ ऑडिओ कनेक्ट केला"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ब्लूटुथ ऑडिओ डिस्कनेक्ट केला"</string>
</resources>
diff --git a/res/values-mr-rIN/strings_pbap_client.xml b/res/values-mr-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-mr-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 5a83a89..37971e2 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Terima"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Berlaku tamat masa semasa menerima fail masuk daripada \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Perkongsian Bluetooth: Fail masuk"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Adakah anda mahu menerima fail ini?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Fail masuk daripada peranti lain. Sila sahkan bahawa anda mahu menerima fail ini."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Fail masuk"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> sudah bersedia untuk menghantar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Perkongsian Bluetooth: Menerima <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Perkongsian Bluetooth: Diterima <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Perkongsian Bluetooth: Fail <xliff:g id="FILE">%1$s</xliff:g> tidak diterima"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikon Aplikasi"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Tetapan Perkongsian Mesej Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Tidak boleh memilih akaun. 0 slot yang tinggal"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio Bluetooth disambungkan"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth diputuskan sambungannya"</string>
</resources>
diff --git a/res/values-ms-rMY/strings_pbap_client.xml b/res/values-ms-rMY/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ms-rMY/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 985c616..a71b298 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -28,17 +28,16 @@
<string name="bt_enable_title" msgid="8657832550503456572"></string>
<string name="bt_enable_line1" msgid="7203551583048149">"ဘလူးတုသ်ဆားဗစ်ကိုအသုံးပြုရန် ပထမဦးစွာ ဘလူးတုသ်ကိုဖွင့်ပါ"</string>
<string name="bt_enable_line2" msgid="4341936569415937994">"ယခုပင် ဘလူးတုသ်ကိုဖွင့်မည်လား?"</string>
- <string name="bt_enable_cancel" msgid="1988832367505151727">"ထားတော့"</string>
+ <string name="bt_enable_cancel" msgid="1988832367505151727">"မလုပ်တော့ပါ"</string>
<string name="bt_enable_ok" msgid="3432462749994538265">"ဖွင့်မည်"</string>
<string name="incoming_file_confirm_title" msgid="8139874248612182627">"ဖိုင်လွှဲပြောင်းခြင်း"</string>
<string name="incoming_file_confirm_content" msgid="2752605552743148036">"ဝင်လာသည့် ဖိုင်ကို လက်ခံမလား?"</string>
<string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"လက်မခံပါ"</string>
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"လက်ခံရန်"</string>
- <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ကောင်းပြီ"</string>
+ <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"အိုကေ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\"မှ အဝင်ဖိုင်ကို လက်ခံနေစဥ် အချိန်ကုန်ဆုံးသွားပါပြီ"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ဘလူးတုသ် ဝေမျှမှု - အဝင်ဖိုင်"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ဤဖိုင်ကို လက်ခံမည်လား?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"အခြားစက်မှ ဝင်လာသော ဖိုင်ရှိသည်။ ထိုဖိုင်ကို လက်ခံရန် အတည်ပြုပါ။"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"အဝင် ဖိုင်"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> က <xliff:g id="FILE">%2$s</xliff:g>ကို ပို့ရန် အသင့်ပါ"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ဘလူးတုသ် ဝေမျှမှု - <xliff:g id="FILE">%1$s</xliff:g> လက်ခံနေသည်"</string>
<string name="notification_received" msgid="3324588019186687985">"ဘလူးတုသ် ဝေမျှမှု - ရရှိပြီး<xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ဘလူးတုသ် ဝေမျှမှု - ဖိုင် <xliff:g id="FILE">%1$s</xliff:g> မရရှိပါ"</string>
@@ -60,19 +59,19 @@
<string name="download_fail_line1" msgid="3846450148862894552">"ဖိုင် မရရှိပါ"</string>
<string name="download_fail_line2" msgid="8950394574689971071">"ဖိုင် - <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="download_fail_line3" msgid="3451040656154861722">"အကြောင်းပြချက် - <xliff:g id="REASON">%1$s</xliff:g>"</string>
- <string name="download_fail_ok" msgid="1521733664438320300">"ကောင်းပြီ"</string>
+ <string name="download_fail_ok" msgid="1521733664438320300">"အိုကေ"</string>
<string name="download_succ_line5" msgid="4509944688281573595">"လက်ခံရပြီးဖိုင်"</string>
<string name="download_succ_ok" msgid="7053688246357050216">"ဖွင့်ရန်"</string>
<string name="upload_line1" msgid="2055952074059709052">"သို့ - \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
<string name="upload_line3" msgid="4920689672457037437">"ဖိုင်အမျိုးအစား - <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
<string name="upload_line5" msgid="7759322537674229752">"ဖိုင်ပို့နေပါသည်"</string>
<string name="upload_succ_line5" msgid="5687317197463383601">"ဖိုင်ပို့ပြီး"</string>
- <string name="upload_succ_ok" msgid="7705428476405478828">"ကောင်းပြီ"</string>
+ <string name="upload_succ_ok" msgid="7705428476405478828">"အိုကေ"</string>
<string name="upload_fail_line1" msgid="7899394672421491701">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\"ထံသို့ ဖိုင်ကို မပို့ပါ"</string>
<string name="upload_fail_line1_2" msgid="2108129204050841798">"ဖိုင် - <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="upload_fail_ok" msgid="5807702461606714296">"ပြန်ကြိုးစားပါ"</string>
<string name="upload_fail_cancel" msgid="9118496285835687125">"ပိတ်ရန်"</string>
- <string name="bt_error_btn_ok" msgid="5965151173011534240">"ကောင်းပြီ"</string>
+ <string name="bt_error_btn_ok" msgid="5965151173011534240">"အိုကေ"</string>
<string name="unknown_file" msgid="6092727753965095366">"အမျိုးအမည် မသိသောဖိုင်"</string>
<string name="unknown_file_desc" msgid="480434281415453287">"ဤဖိုင်အမျိုးအစားကို ကိုင်တွယ်ရန် အပလီကေးရှင်းမရှိပါ\n"</string>
<string name="not_exist_file" msgid="3489434189599716133">"ဖိုင်မရှိပါ"</string>
@@ -118,15 +117,17 @@
<item quantity="other"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> အောင်မြင်ပါသည်၊ %2$s</item>
<item quantity="one"><xliff:g id="SUCCESSFUL_NUMBER_0">%1$d</xliff:g> အောင်မြင်ပါသည်၊ %2$s</item>
</plurals>
- <string name="transfer_menu_clear_all" msgid="790017462957873132">"စာရင်းကို ရှင်းလင်းရန်"</string>
+ <string name="transfer_menu_clear_all" msgid="790017462957873132">"စာရင်းကို ဖယ်ရှားရန်"</string>
<string name="transfer_menu_open" msgid="3368984869083107200">"ဖွင့်ရန်"</string>
<string name="transfer_menu_clear" msgid="5854038118831427492">"စာရင်းမှ ရှင်းပစ်မည်"</string>
- <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"ရှင်းလင်းရန်"</string>
+ <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"ဖယ်ရှားရန်"</string>
<string name="bluetooth_map_settings_save" msgid="7635491847388074606">"သိမ်းဆည်းရန်"</string>
- <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"ဖျက်သိမ်းရန်"</string>
+ <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"မလုပ်တော့ပါ"</string>
<string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"ဘလူးတုသ်မှ မျှဝေလိုသော အကောင့်များကို ရွေးပါ။ ချိတ်ဆက်သည့်အခါ အကောင့်များအား ဝင်ခွင့်ပြုဖို့ လက်ခံပေးရပါလိမ့်ဦးမည်။"</string>
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"ကျန်နေသည့် အပေါက်များ:"</string>
- <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"အပလီကေးရှင်း အိုင်ကွန်"</string>
+ <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"အက်ပ် အိုင်ကွန်"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ဘလူးတုသ် စာ မျှဝေရေး ဆက်တင်များ"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ရွေးထားသည့် အကောင့်ကို မရွေးနိုင်ပါ။ သုည အပေါက်များ ကျန်နေ"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ဘလူးတုသ်အသံ ချိတ်ဆက်ပြီးပါပြီ"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ဘလူးတုသ်အသံ မချိတ်ဆက်ထားပါ"</string>
</resources>
diff --git a/res/values-my-rMM/strings_pbap_client.xml b/res/values-my-rMM/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-my-rMM/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-my-rMM/test_strings.xml b/res/values-my-rMM/test_strings.xml
index b615a5d..02d200d 100644
--- a/res/values-my-rMM/test_strings.xml
+++ b/res/values-my-rMM/test_strings.xml
@@ -7,7 +7,7 @@
<string name="update_record" msgid="2480425402384910635">"မှတ်တမ်းအတည်ပြုရန်"</string>
<string name="ack_record" msgid="6716152390978472184">"အသိအမှတ်ပြု မှတ်တမ်း"</string>
<string name="deleteAll_record" msgid="4383349788485210582">"မှတ်တမ်းအားလုံးကို ဖျက်ရန်"</string>
- <string name="ok_button" msgid="6519033415223065454">"ကောင်းပြီ"</string>
+ <string name="ok_button" msgid="6519033415223065454">"အိုကေ"</string>
<string name="delete_record" msgid="4645040331967533724">"မှတ်တမ်းကို ဖျက်ရန်"</string>
<string name="start_server" msgid="9034821924409165795">"TCP ဆာဗာ စတင်ရန်"</string>
<string name="notify_server" msgid="4369106744022969655">"TCP ဆာဗာကို အကြောင်းကြားရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 17e6fc6..05e3821 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Godta"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Det oppstod et tidsavbrudd under mottak av fil fra «<xliff:g id="SENDER">%1$s</xliff:g>»"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-deling: Innkommende fil"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Vil du motta denne filen?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Innkommende fil fra en annen enhet. Bekreft at du ønsker å motta denne filen."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Innkommende fil"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> er klar til å sende <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-deling: Mottar <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-deling: <xliff:g id="FILE">%1$s</xliff:g> er mottatt"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-deling: Filen <xliff:g id="FILE">%1$s</xliff:g> er ikke mottatt"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Appikon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Innstillinger for meldingsdeling via Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Kan ikke velge kontoen. Det er 0 plasser igjen"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-lyd er tilkoblet"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-lyd er frakoblet"</string>
</resources>
diff --git a/res/values-nb/strings_pbap_client.xml b/res/values-nb/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-nb/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 6d5e253..8a8e677 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"स्वीकार्नुहोस्"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ठीक छ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" बाट आगमन फाइल स्वीकार्दा समय सकिएको थियो"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"ब्लुटुथ साझेदारी: आगमन फाइल"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"के तपाईँ यो फाइल प्राप्त गर्न चाहनु हुन्छ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"अर्को उपकरणबाट आगमन फाइल। यो फाइल तपाईँ प्राप्त गर्न चाहनु हुन्छ भन्ने कुरा निश्चित गर्नुहोस्।"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"आगमन फाइल"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ले <xliff:g id="FILE">%2$s</xliff:g> पठाउन तयार छ"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ब्लुटुथ साझेदारी:<xliff:g id="FILE">%1$s</xliff:g> प्राप्त गर्दै"</string>
<string name="notification_received" msgid="3324588019186687985">"ब्लुटुथ साझेदारी: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त भयो"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ब्लुटुथ साझेदारी: फाइल: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त भएन"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"अनुप्रयोग आइकन"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ब्लुटुथ सन्देश साझेदारी सेटिङहरू"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"खाता चयन गर्न सक्दैन। ० स्लटहरू बाँकी"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ब्लुटुथ सम्बन्धी अडियो यन्त्रलाई जडान गरियो"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ब्लुटुथ सम्बन्धी अडियो यन्त्रलाई विच्छेद गरियो"</string>
</resources>
diff --git a/res/values-ne-rNP/strings_pbap_client.xml b/res/values-ne-rNP/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ne-rNP/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index e63f024..21fff33 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepteren"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Er is een time-out opgetreden bij het accepteren van een inkomend bestand van \'<xliff:g id="SENDER">%1$s</xliff:g>\'"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Delen via Bluetooth: inkomend bestand"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Wilt je dit bestand ontvangen?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Er is een inkomend bestand van een ander apparaat beschikbaar. Bevestig dat je dit bestand wilt ontvangen."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Inkomend bestand"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> is gereed om <xliff:g id="FILE">%2$s</xliff:g> te verzenden"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Delen via Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> ontvangen"</string>
<string name="notification_received" msgid="3324588019186687985">"Delen via Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> ontvangen"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Delen via Bluetooth: bestand <xliff:g id="FILE">%1$s</xliff:g> niet ontvangen"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"App-pictogram"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Instellingen voor delen van berichten via Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Kan account niet selecteren. 0 plaatsen over"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-audio gekoppeld"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-audio ontkoppeld"</string>
</resources>
diff --git a/res/values-nl/strings_pbap_client.xml b/res/values-nl/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-nl/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 9627f2e..5fc7184 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permlab_bluetoothShareManager" msgid="311492132450338925">"ਡਾਊਨਲੋਡ ਪ੍ਰਬੰਧਕ ਤੱਕ ਪਹੁੰਚ।"</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"ਐਪ ਨੂੰ BluetoothShare ਪ੍ਰਬੰਧਕ ਤੱਕ ਪਹੁੰਚ ਅਤੇ ਫਾਈਲਾਂ ਟ੍ਰਾਂਸਫਰ ਕਰਨ ਲਈ ਇਸਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"ਵਾਈਟਲਿਸਟ bluetooth ਡਿਵਾਈਸ ਪਹੁੰਚ।"</string>
- <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"ਐਪ ਨੂੰ ਇਹ ਆਗਿਆ ਦਿੰਦੇ ਹੋਏ ਕਿ ਡਿਵਾਈਸ ਉਪਭੋਗਤਾ ਦੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਡਿਵਾਈਸ ਨੂੰ ਫਾਈਲਾਂ ਭੇਜੇ, ਇੱਕ Bluetooth ਡਿਵਾਈਸ ਨੂੰ ਅਸਥਾਈ ਤੌਰ ਤੇ ਵਾਈਟਲਿਸਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"ਵਾਈਟਲਿਸਟ bluetooth ਡੀਵਾਈਸ ਪਹੁੰਚ।"</string>
+ <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"ਐਪ ਨੂੰ ਇਹ ਆਗਿਆ ਦਿੰਦੇ ਹੋਏ ਕਿ ਡੀਵਾਈਸ ਉਪਭੋਗਤਾ ਦੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਫਾਈਲਾਂ ਭੇਜੇ, ਇੱਕ Bluetooth ਡੀਵਾਈਸ ਨੂੰ ਅਸਥਾਈ ਤੌਰ ਤੇ ਵਾਈਟਲਿਸਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
- <string name="unknown_device" msgid="9221903979877041009">"ਅਗਿਆਤ ਡਿਵਾਈਸ"</string>
+ <string name="unknown_device" msgid="9221903979877041009">"ਅਗਿਆਤ ਡੀਵਾਈਸ"</string>
<string name="unknownNumber" msgid="4994750948072751566">"ਅਗਿਆਤ"</string>
<string name="airplane_error_title" msgid="2683839635115739939">"ਏਅਰਪਲੇਨ ਮੋਡ"</string>
<string name="airplane_error_msg" msgid="8698965595254137230">"ਤੁਸੀਂ ਏਅਰਪਲੇਨ ਮੋਡ ਵਿੱਚ Bluetooth ਨਹੀਂ ਵਰਤ ਸਕਦੇ।"</string>
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ਠੀਕ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" ਦੀ ਇੱਕ ਇਨਕਮਿੰਗ ਫਾਈਲ ਸਵੀਕਾਰ ਕਰਨ ਵੇਲੇ ਇੱਕ ਟਾਈਮਆਊਟ ਹੋਇਆ ਸੀ।"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth ਸ਼ੇਅਰ: ਇਨਕਮਿੰਗ ਫਾਈਲ"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ਕੀ ਤੁਸੀਂ ਇਹ ਫਾਈਲ ਪ੍ਰਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ਦੂਜੀ ਡਿਵਾਈਸ ਤੋਂ ਇਨਕਮਿੰਗ ਫਾਈਲ। ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇਹ ਫਾਈਲ ਪ੍ਰਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ।"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ਇਨਕਮਿੰਗ ਫਾਈਲ"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> ਭੇਜਣ ਲਈ ਤਿਆਰ ਹੈ"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth ਸ਼ੇਅਰ: <xliff:g id="FILE">%1$s</xliff:g> ਪ੍ਰਾਪਤ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth ਸ਼ੇਅਰ: <xliff:g id="FILE">%1$s</xliff:g> ਪ੍ਰਾਪਤ ਕੀਤੀ"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth ਸ਼ੇਅਰ: ਫਾਈਲ <xliff:g id="FILE">%1$s</xliff:g> ਪ੍ਰਾਪਤ ਨਹੀਂ ਕੀਤੀ"</string>
@@ -93,7 +92,7 @@
<string name="status_running" msgid="6562808920311008696">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਜਾਰੀ ਹੈ।"</string>
<string name="status_success" msgid="239573225847565868">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਸਫਲਤਾਪੂਰਵਕ ਪੂਰੀ ਹੋਈ।"</string>
<string name="status_not_accept" msgid="1695082417193780738">"ਸਮੱਗਰੀ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।"</string>
- <string name="status_forbidden" msgid="613956401054050725">"ਟੀਚਾ ਡਿਵਾਈਸ ਵੱਲੋਂ ਟ੍ਰਾਂਸਫਰ ਵਰਜਿਤ।"</string>
+ <string name="status_forbidden" msgid="613956401054050725">"ਟੀਚਾ ਡੀਵਾਈਸ ਵੱਲੋਂ ਟ੍ਰਾਂਸਫਰ ਵਰਜਿਤ।"</string>
<string name="status_canceled" msgid="6664490318773098285">"ਉਪਭੋਗਤਾ ਵੱਲੋਂ ਟ੍ਰਾਂਸਫਰ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="status_file_error" msgid="3671917770630165299">"ਸਟੋਰੇਜ ਸਮੱਸਿਆ।"</string>
<string name="status_no_sd_card" product="nosdcard" msgid="1112125377088421469">"ਕੋਈ USB ਸਟੋਰੇਜ ਨਹੀਂ।"</string>
@@ -122,11 +121,13 @@
<string name="transfer_menu_open" msgid="3368984869083107200">"ਖੋਲ੍ਹੋ"</string>
<string name="transfer_menu_clear" msgid="5854038118831427492">"ਸੂਚੀ ਵਿੱਚੋਂ ਹਟਾਓ"</string>
<string name="transfer_clear_dlg_title" msgid="2953444575556460386">"ਹਟਾਓ"</string>
- <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"ਸੁਰੱਖਿਅਤ ਕਰੋ"</string>
+ <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"ਰੱਖਿਅਤ ਕਰੋ"</string>
<string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"ਰੱਦ ਕਰੋ"</string>
<string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"ਉਹਨਾਂ ਖਾਤਿਆਂ ਨੂੰ ਚੁਣੋ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਸੀਂ ਬਲੂਟੁੱਥ ਦੇ ਰਾਹੀਂ ਸਾਂਝਾ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ। ਤੁਹਾਨੂੰ ਹਾਲੇ ਵੀ ਕਨੈਕਟ ਕਰਨ ਦੌਰਾਨ ਖਾਤਿਆਂ \'ਤੇ ਕਿਸੇ ਵੀ ਪਹੁੰਚ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨਾ ਹੋਵੇਗਾ।"</string>
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"ਸਲੌਟ ਬਾਕੀ:"</string>
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ਐਪਲੀਕੇਸ਼ਨ ਆਈਕਨ"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ਬਲੂਟੁੱਥ ਸੁਨੇਹਾ ਸ਼ੇਅਰਿੰਗ ਸੈੱਟਿੰਗਜ਼"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ਖਾਤਾ ਨਹੀਂ ਚੁਣ ਸਕਦਾ। 0 ਸਲੌਟ ਬਾਕੀ"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਕਨੈਕਟ ਕੀਤੀ ਗਈ"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਡਿਸਕਨੈਕਟ ਕੀਤੀ ਗਈ"</string>
</resources>
diff --git a/res/values-pa-rIN/strings_pbap_client.xml b/res/values-pa-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-pa-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-pa-rIN/test_strings.xml b/res/values-pa-rIN/test_strings.xml
index b3f5879..d8e6c76 100644
--- a/res/values-pa-rIN/test_strings.xml
+++ b/res/values-pa-rIN/test_strings.xml
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="hello" msgid="1740533743008967039">"ਹੈਲੋ ਦੁਨੀਆ, TestActivity"</string>
<string name="app_name" msgid="1203877025577761792">"Bluetooth ਸ਼ੇਅਰ"</string>
- <string name="insert_record" msgid="1450997173838378132">"ਰਿਕਾਰਡ ਦਰਜ ਕਰੋ"</string>
+ <string name="insert_record" msgid="1450997173838378132">"ਰਿਕਾਰਡ ਦਾਖਲ ਕਰੋ"</string>
<string name="update_record" msgid="2480425402384910635">"ਰਿਕਾਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
<string name="ack_record" msgid="6716152390978472184">"Ack ਰਿਕਾਰਡ"</string>
<string name="deleteAll_record" msgid="4383349788485210582">"ਸਾਰੇ ਰਿਕਾਰਡ ਮਿਟਾਓ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 83dca9f..99ed7fe 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Akceptuj"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Upłynął czas oczekiwania przy akceptowaniu przychodzącego pliku z urządzenia „<xliff:g id="SENDER">%1$s</xliff:g>”"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Udostępnianie Bluetooth: plik przychodzący"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Chcesz odebrać ten plik?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Przesłano plik z innego urządzenia. Potwierdź, że chcesz go odebrać."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Plik przychodzący"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> może wysłać plik <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Udostępnianie Bluetooth: odbieranie <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Udostępnianie Bluetooth: odebrano plik <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Udostępnianie Bluetooth: nie odebrano pliku <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikacji"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Ustawienia udostępniania wiadomości przez Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Nie można wybrać konta. Pozostało 0 miejsc."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Dźwięk Bluetooth podłączony"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Dźwięk Bluetooth odłączony"</string>
</resources>
diff --git a/res/values-pl/strings_pbap_client.xml b/res/values-pl/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-pl/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index c61e220..c8290bd 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceitar"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Foi excedido o tempo limite durante a aceitação de um ficheiro de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Partilha Bluetooth: ficheiro recebido"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Pretende receber este ficheiro?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"A receber ficheiro de outro aparelho. Confirme se pretende recebê-lo."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Ficheiro a receber"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> está pronto para enviar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Partilha Bluetooth: a receber <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Partilha Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> recebido"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Partilha Bluetooth: Ficheiro <xliff:g id="FILE">%1$s</xliff:g> não recebido"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ícone de aplicação"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Definições de partilha de mensagens por Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Não é possível selecionar a conta. Não há ranhuras restantes"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Áudio Bluetooth ligado"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Áudio Bluetooth desligado"</string>
</resources>
diff --git a/res/values-pt-rPT/strings_pbap_client.xml b/res/values-pt-rPT/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-pt-rPT/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 8848b3e..ebc2d6a 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceitar"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Tempo limite excedido ao receber um arquivo de \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Compartilhamento Bluetooth: arquivo recebido"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Deseja receber este arquivo?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Arquivo recebido de outro dispositivo. Confirme se deseja receber este arquivo."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Arquivo recebido"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> está pronto para enviar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Compart. Bluetooth: recebendo <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Compart. Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> recebido"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Compart. Bluetooth: o arquivo <xliff:g id="FILE">%1$s</xliff:g> não foi recebido"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ícone do app"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Configurações de compartilhamento de mensagens Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Não é possível selecionar conta. 0 espaços disponíveis"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Áudio Bluetooth conectado"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Áudio Bluetooth desconectado"</string>
</resources>
diff --git a/res/values-pt/strings_pbap_client.xml b/res/values-pt/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-pt/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 1aeb787..9a4a536 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Acceptați"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"A fost atins timpul limită pentru acceptarea unui fișier primit de la „<xliff:g id="SENDER">%1$s</xliff:g>”"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth: se primește fișierul"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Doriți să primiți acest fișier?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Un fișier de la alt dispozitiv se află în curs de primire. Confirmați dacă doriți primirea acestuia."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Fișier primit"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> este gata să trimită <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Distribuire prin Bluetooth: se primește <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Distribuire prin Bluetooth: s-a primit <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Distribuire prin Bluetooth: fișierul <xliff:g id="FILE">%1$s</xliff:g> nu s-a primit"</string>
@@ -131,4 +130,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Pictograma aplicației"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Setări de permitere a accesului la mesajele prin Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Nu se poate selecta contul. 0 sloturi rămase"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audio prin Bluetooth conectat"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio prin Bluetooth deconectat"</string>
</resources>
diff --git a/res/values-ro/strings_pbap_client.xml b/res/values-ro/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ro/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 289daa8..cb673e6 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Принять"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ОК"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"В процессе приема файла от \"<xliff:g id="SENDER">%1$s</xliff:g>\" произошел тайм-аут"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Передача по Bluetooth"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Принять файл?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Входящий файл с другого устройства. Подтвердите, что хотите загрузить его."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Входящий файл"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Устройство \"<xliff:g id="SENDER">%1$s</xliff:g>\" готово к отправке файла \"<xliff:g id="FILE">%2$s</xliff:g>\""</string>
<string name="notification_receiving" msgid="4674648179652543984">"Передача по Bluetooth: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Получено по Bluetooth: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Файл не передан: <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Значок приложения"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Настройки доступа к сообщениям через Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Не удалось выбрать аккаунт: не осталось мест."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Звук через Bluetooth включен"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Звук через Bluetooth отключен"</string>
</resources>
diff --git a/res/values-ru/strings_pbap_client.xml b/res/values-ru/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ru/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 69721a8..f87cc9e 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"පිළිගන්න"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"හරි"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" වෙතින් පැමිණෙන ගොනුවක් පිළිගන්නා අතරතුර කාල නිමාවක් විය"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"බ්ලූටූත් බෙදා ගැනීම: පැමිණෙන ගොනුව"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ඔබට මෙම ගොනුව ලබා ගැනීමට අවශ්යද?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"වෙනත් උපාංගයක් වෙතින් පැමිණෙන ගොනුව. මෙම ගොනුව ලබා ගැනීමට ඔබට අවශ්ය බව තහවුරු කරන්න."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ලැබෙන ගොනුව"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> යැවීමට සූදානම්ය"</string>
<string name="notification_receiving" msgid="4674648179652543984">"බ්ලූටූත් බෙදා ගැනීම: <xliff:g id="FILE">%1$s</xliff:g> ලැබේ"</string>
<string name="notification_received" msgid="3324588019186687985">"බ්ලූටූත් බෙදා ගැනීම: <xliff:g id="FILE">%1$s</xliff:g> ලැබිණි"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"බ්ලූටූත් බෙදා ගැනීම: <xliff:g id="FILE">%1$s</xliff:g> ගොනුව නොලැබිණි"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"යෙදුම් නිරූපකය"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"බ්ලූටූත් පණිවිඩ බෙදාගැනීමේ සැකසීම්"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ගිණුමක් තේරිය නොහැක. විවර 0 ක් ඉතිරිව තිබේ"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"බ්ලූටූත් ශ්රව්යය සම්බන්ධ කරන ලදී"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"බ්ලූටූත් ශ්රව්යය විසන්ධි කරන ලදී"</string>
</resources>
diff --git a/res/values-si-rLK/strings_pbap_client.xml b/res/values-si-rLK/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-si-rLK/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index edc2fa6..889f379 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Prijať"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Pri prijímaní prichádzajúceho súboru od používateľa <xliff:g id="SENDER">%1$s</xliff:g> vypršal časový limit."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth: Prichádzajúci súbor"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Chcete prijať tento súbor?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Potvrďte príjem súboru prichádzajúceho z iného zariadenia."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Prichádzajúci súbor"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> je pripravený/-á odoslať súbor <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth: Prijíma sa <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> prijatý"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> neprijatý"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikácie"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Nastavenia zdieľania správ prostredníctvom rozhrania Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Účet sa nedá vybrať. Nie sú k dispozícii žiadne ďalšie sloty."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Rozhranie Bluetooth Audio je pripojené"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Rozhranie Bluetooth Audio je odpojené"</string>
</resources>
diff --git a/res/values-sk/strings_pbap_client.xml b/res/values-sk/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-sk/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index a132212..2fcd223 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Sprejmi"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"V redu"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Pri sprejemanju datoteke pošiljatelja »<xliff:g id="SENDER">%1$s</xliff:g>« je potekla časovna omejitev"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth: Prihajajoča datoteka"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Ali želite prejeti to datoteko?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Iz druge naprave prihaja datoteka. Potrdite, da jo želite prejeti."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Dohodna datoteka"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Uporabnik <xliff:g id="SENDER">%1$s</xliff:g> je pripravljen za pošiljanje datoteke <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth: Prejemanje <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth: Prejeto <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth: Datoteka <xliff:g id="FILE">%1$s</xliff:g> ni bila prejeta"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona aplikacije"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Nastavitve skupne rabe sporočil prek Bluetootha"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Ni mogoče izbrati računa. Na voljo je 0 mest."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Zvok prek Bluetootha je povezan"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Zvok prek Bluetootha ni povezan"</string>
</resources>
diff --git a/res/values-sl/strings_pbap_client.xml b/res/values-sl/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-sl/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index bed09a7..5e3c34a 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Prano"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Në rregull!"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Përfundoi koha e veprimit për pranimin e skedarit hyrës nga \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Shpërndarja përmes bluetooth-it: Skedar hyrës"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Dëshiron ta marrësh këtë skedar?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Skedar hyrës nga një tjetër pajisje. Konfirmo që dëshiron ta marrësh këtë skedar."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Skedari në ardhje"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> është gati për të dërguar <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Shpërndarja përmes bluetooth-it: Po merret skedari <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Shpërndarja përmes bluetooth-it: U pranua skedari <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Shpërndarja përmes bluetooth-it: Skedari <xliff:g id="FILE">%1$s</xliff:g> nuk u pranua"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ikona e aplikacionit"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Cilësimet e ndarjes së mesazheve me Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Llogaria nuk mund të zgjidhet. 0 fole të mbetura"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Audioja e \"bluetooth-it\" e lidhur"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Audioja e \"bluetooth-it\" e shkëputur"</string>
</resources>
diff --git a/res/values-sq-rAL/strings_pbap_client.xml b/res/values-sq-rAL/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-sq-rAL/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 43b979f..03fed95 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Прихвати"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Потврди"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Дошло је до временског ограничења током пријема долазне датотеке од „<xliff:g id="SENDER">%1$s</xliff:g>“"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Дељење преко Bluetooth-а: долазећа датотека"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Да ли желите да примите ову датотеку?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Долазна датотека са другог уређаја. Потврдите да желите да је примите."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Долазна датотека"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> је спреман/на да пошаље <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth дељење: пријем <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth дељење: примљено <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth дељење: датотека <xliff:g id="FILE">%1$s</xliff:g> није примљена"</string>
@@ -131,4 +130,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Икона апликације"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Подешавања Bluetooth дељења порука"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Није могуће изабрати налог. Нема преосталих места"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth аудио је повезан"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Веза са Bluetooth аудијом је прекинута"</string>
</resources>
diff --git a/res/values-sr/strings_pbap_client.xml b/res/values-sr/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-sr/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index bb16a57..35a9097 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Godkänn"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Tidsgränsen överskreds när en inkommande fil från <xliff:g id="SENDER">%1$s</xliff:g> skulle tas emot"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-delning: inkommande fil"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Vill du ta emot den här filen?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Inkommande fil från en annan enhet. Bekräfta att du vill ta emot denna fil."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Inkommande fil"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> är klar att skicka <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth-delning: tar emot <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth-delning: <xliff:g id="FILE">%1$s</xliff:g> har tagits emot"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth-delning: filen <xliff:g id="FILE">%1$s</xliff:g> har inte tagits emot"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Appikon"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Inställningar för meddelandedelning via Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Det gick inte att välja konton. 0 platser kvar"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth-ljud är anslutet"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-ljud är frånkopplat"</string>
</resources>
diff --git a/res/values-sv/strings_pbap_client.xml b/res/values-sv/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-sv/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 96ad143..0deefec 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Kubali"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Sawa"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Muda ulikatika wakati wa kukubali faili inayoingia kutoka \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Shiriki Bluetooth: Faili inayoingia"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Je, unataka kupokea faili hii?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Faili zinazoingia kutoka kwa kifaa kingine. Thibitisha kuwa unataka kupokea faili hii."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Faili Zinazoingia"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ako tayari kutuma <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Kushiriki kwa bluetooth: Inapokea <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Kushiriki kwa bluetooth: Imepokea <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Kushiriki kwa Bluetooth: Faili <xliff:g id="FILE">%1$s</xliff:g> haijapokewa"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Aikoni ya programu"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Mipangilio ya Kushiriki Ujumbe wa Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Haiwezi kuchagua akaunti. Nafasi 0 zimesalia"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Imeunganisha sauti ya Bluetooth"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Imeondoa sauti ya Bluetooth"</string>
</resources>
diff --git a/res/values-sw/strings_pbap_client.xml b/res/values-sw/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-sw/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index a75babe..04f705e 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ஏற்கிறேன்"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"சரி"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" இடமிருந்து வரும் கோப்பை ஏற்கும்போது நேரம் முடிந்தது"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"புளூடூத் பகிர்வு: உள்வரும் கோப்பு"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"கோப்பைப் பெறவா?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"வேறொரு சாதனத்திலிருந்து கோப்பு வருகிறது. அதைப் பெற விருப்பமா என்பதை உறுதிப்படுத்தவும்."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"உள்வரும் கோப்பு"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>, <xliff:g id="FILE">%2$s</xliff:g> கோப்பை அனுப்புவதற்குத் தயாராக உள்ளார்"</string>
<string name="notification_receiving" msgid="4674648179652543984">"புளூடூத் பகிர்வு: <xliff:g id="FILE">%1$s</xliff:g> ஐப் பெறுகிறது"</string>
<string name="notification_received" msgid="3324588019186687985">"புளூடூத் பகிர்வு: <xliff:g id="FILE">%1$s</xliff:g> பெறப்பட்டது"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"புளூடூத் பகிர்வு: <xliff:g id="FILE">%1$s</xliff:g> ஐப் பெறவில்லை"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"பயன்பாட்டு ஐகான்"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"புளூடூத் செய்தி பகிர்தல் அமைப்புகள்"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"கணக்கைத் தேர்வுசெய்ய முடியாது. ஸ்லாட்கள் எதுவுமில்லை"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"புளூடூத் ஆடியோ இணைக்கப்பட்டது"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"புளூடூத் ஆடியோ துண்டிக்கப்பட்டது"</string>
</resources>
diff --git a/res/values-ta-rIN/strings_pbap_client.xml b/res/values-ta-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ta-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 89d40be..e0725a4 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"అంగీకరిస్తున్నాను"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"సరే"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" పంపిన ఇన్కమింగ్ ఫైల్ను అంగీకరిస్తున్నప్పుడు గడువు సమయం ముగిసింది"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"బ్లూటూత్ భాగస్వామ్యం: ఇన్కమింగ్ ఫైల్"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"మీరు ఈ ఫైల్ను స్వీకరించాలనుకుంటున్నారా?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"మరో పరికరం నుండి ఇన్కమింగ్ ఫైల్. మీరు ఈ ఫైల్ను స్వీకరించాలనుకుంటున్నట్లు నిర్ధారించండి."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ఫైల్ స్వీకరణ"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> ఫైల్ పంపడానికి సిద్ధంగా ఉన్నారు"</string>
<string name="notification_receiving" msgid="4674648179652543984">"బ్లూటూత్ భాగస్వామ్యం: <xliff:g id="FILE">%1$s</xliff:g>ను స్వీకరిస్తోంది"</string>
<string name="notification_received" msgid="3324588019186687985">"బ్లూటూత్ భాగస్వామ్యం: <xliff:g id="FILE">%1$s</xliff:g> స్వీకరించబడింది"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"బ్లూటూత్ భాగస్వామ్యం: <xliff:g id="FILE">%1$s</xliff:g> ఫైల్ స్వీకరించబడలేదు"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"అనువర్తన చిహ్నం"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"బ్లూటూత్ సందేశ భాగస్వామ్య సెట్టింగ్లు"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ఖాతాను ఎంచుకోవడం సాధ్యపడదు. 0 స్లాట్లు మిగిలి ఉన్నాయి"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"బ్లూటూత్ ఆడియో కనెక్ట్ చేయబడింది"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"బ్లూటూత్ ఆడియో డిస్కనెక్ట్ చేయబడింది"</string>
</resources>
diff --git a/res/values-te-rIN/strings_pbap_client.xml b/res/values-te-rIN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-te-rIN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 2fc58cb..7740718 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"ยอมรับ"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ตกลง"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"มีการหมดเวลาเกิดขึ้นขณะยอมรับไฟล์ขาเข้าจาก \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"การแชร์ทางบลูทูธ: ไฟล์ขาเข้า"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"คุณต้องการรับไฟล์นี้หรือไม่"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ไฟล์ขาเข้าจากอุปกรณ์อื่น ยืนยันว่าคุณต้องการรับไฟล์นี้"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ไฟล์ที่เข้ามา"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> พร้อมที่จะส่ง <xliff:g id="FILE">%2$s</xliff:g> แล้ว"</string>
<string name="notification_receiving" msgid="4674648179652543984">"การแชร์ทางบลูทูธ: กำลังรับ <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"การแชร์ทางบลูทูธ: รับ <xliff:g id="FILE">%1$s</xliff:g> แล้ว"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"การแชร์ทางบลูทูธ: ไม่ได้รับไฟล์ <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ไอคอนแอปพลิเคชัน"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"การตั้งค่าการแชร์ข้อความผ่านบลูทูธ"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ไม่สามารถเลือกบัญชีได้ ช่องเหลือ 0 ช่อง"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"เชื่อมต่อ Bluetooth Audio แล้ว"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"ยกเลิกการเชื่อมต่อ Bluetooth Audio แล้ว"</string>
</resources>
diff --git a/res/values-th/strings_pbap_client.xml b/res/values-th/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-th/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 22e08ed..0959e88 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Tanggapin"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Nagkaroon ng timeout habang tinatanggap ang papasok na file mula kay \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth share: Paparating na file"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Nais mo bang tanggapin ang file na ito?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Papasok na file mula sa isa pang device. Kumpirmahing nais mong tanggapin ang file na ito."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Papasok na file"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Handa nang ipadala ni <xliff:g id="SENDER">%1$s</xliff:g> ang <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Pagbahagi sa Bluetooth: Tinatanggap ang <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Pagbahagi sa Bluetooth: Natanggap ang <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Pagbahagi sa Bluetooth: Hindi natanggap ang file na <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Icon ng Application"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Mga Setting ng Pagbabahagi ng Mensahe sa Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Hindi mapili ang account. 0 slot ang natitira"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Nakakonekta ang Bluetooth audio"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Nadiskonekta ang Bluetooth audio"</string>
</resources>
diff --git a/res/values-tl/strings_pbap_client.xml b/res/values-tl/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-tl/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-tl/test_strings.xml b/res/values-tl/test_strings.xml
index 5fd2665..1feb032 100644
--- a/res/values-tl/test_strings.xml
+++ b/res/values-tl/test_strings.xml
@@ -6,9 +6,9 @@
<string name="insert_record" msgid="1450997173838378132">"Ipasok ang tala"</string>
<string name="update_record" msgid="2480425402384910635">"Kumpirmahin ang tala"</string>
<string name="ack_record" msgid="6716152390978472184">"Ack na tala"</string>
- <string name="deleteAll_record" msgid="4383349788485210582">"Tanggalin ang lahat ng tala"</string>
+ <string name="deleteAll_record" msgid="4383349788485210582">"I-delete ang lahat ng tala"</string>
<string name="ok_button" msgid="6519033415223065454">"OK"</string>
- <string name="delete_record" msgid="4645040331967533724">"Tanggalin ang tala"</string>
+ <string name="delete_record" msgid="4645040331967533724">"I-delete ang tala"</string>
<string name="start_server" msgid="9034821924409165795">"Simulan ang TCP server"</string>
<string name="notify_server" msgid="4369106744022969655">"I-notify ang TCP server"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 1a7ef70..2dc2726 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Kabul Et"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Tamam"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" kaynağından gelen dosyayı kabul etme süresi doldu"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth paylaşımı: Gelen dosya"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Bu dosyayı almak istiyor musunuz?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Başka cihazdan gelen bir dosya. Bu dosyayı almak istediğinizi onaylayın."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Gelen dosya"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>, <xliff:g id="FILE">%2$s</xliff:g> adlı dosyayı göndermeye hazır"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth paylaşımı: <xliff:g id="FILE">%1$s</xliff:g> alınıyor"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth paylaşımı: <xliff:g id="FILE">%1$s</xliff:g> alındı"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Bluetooth paylaşımı: <xliff:g id="FILE">%1$s</xliff:g> dosyası alınamadı"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Uygulama Simgesi"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth İleti Paylaşımı Ayarları"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Hesap seçilemiyor. 0 yuva kaldı."</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth ses bağlandı"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth ses bağlantısı kesildi"</string>
</resources>
diff --git a/res/values-tr/strings_pbap_client.xml b/res/values-tr/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-tr/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index f9551f0..8cac913 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Прийняти"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Під час приймання вхідного файлу від \"<xliff:g id="SENDER">%1$s</xliff:g>\" виникла затримка"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Через Bluetooth: вхідний файл"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Отримати цей файл?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Вхідний файл з іншого пристрою. Підтвердьте, що хочете отримати цей файл."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Вхідний файл"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"Користувач <xliff:g id="SENDER">%1$s</xliff:g> готовий надіслати файл <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Через Bluetooth: отримання <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Через Bluetooth: отримано <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Через Bluetooth: файл <xliff:g id="FILE">%1$s</xliff:g> не отримано"</string>
@@ -133,4 +132,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Значок додатка"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Параметри надсилання повідомлень через Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Обліковий запис не вибрано. Залишилося 0 місць"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Аудіо Bluetooth під’єднано"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Аудіо Bluetooth від’єднано"</string>
</resources>
diff --git a/res/values-uk/strings_pbap_client.xml b/res/values-uk/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-uk/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index 18935b2..2c83ec2 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"قبول کریں"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"ٹھیک ہے"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" کی جانب سے ایک موصول ہونے والی فائل کو قبول کرتے وقت ایک ٹائم آؤٹ پیش آگیا"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"بلوٹوتھ اشتراک: موصول ہونے والی فائل"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"کیا آپ اس فائل کو موصول کرنا چاہتے ہیں؟"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"کسی دوسرے آلہ سے موصول ہونے والی فائل۔ توثیق کریں کہ آپ اس فائل کو موصول کرنا چاہتے ہیں۔"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"آنے والی فائل"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> بھیجنے کے لئے تیار ہے"</string>
<string name="notification_receiving" msgid="4674648179652543984">"بلوٹوتھ اشتراک: <xliff:g id="FILE">%1$s</xliff:g> موصول ہو رہی ہے"</string>
<string name="notification_received" msgid="3324588019186687985">"بلوٹوتھ اشتراک: <xliff:g id="FILE">%1$s</xliff:g> موصول ہوئی"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"بلوٹوتھ اشتراک: فائل <xliff:g id="FILE">%1$s</xliff:g> موصول نہیں ہوئی"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ایپلیکیشن کا آئیکن"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"بلوٹوتھ پیغام کے اشتراک کی ترتیبات"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"اکاؤنٹ کو منتخب نہیں کیا جا سکتا ہے۔ 0 سلاٹس باقی ہیں"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"بلوٹوتھ آڈیو منسلک ہے"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"بلوٹوتھ آڈیو غیر منسلک ہے"</string>
</resources>
diff --git a/res/values-ur-rPK/strings_pbap_client.xml b/res/values-ur-rPK/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-ur-rPK/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 203f406..d023fe7 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Qabul qilish"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\"dan kiruvchi xabarni olishga rozilik bildirilayotganda, kutish vaqti o‘tib ketdi."</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth o‘tkazmalari"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Fayl qabul qilinsinmi?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Boshqa qurilmadan kiruvchi fayl. Ushbu faylni qabul qilmoqchi bo‘lsangiz, tasdiqlang."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Kiruvchi fayl"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"“<xliff:g id="SENDER">%1$s</xliff:g>” qurilmasi “<xliff:g id="FILE">%2$s</xliff:g>” faylini yuborishga tayyor"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Bluetooth orqali yuborildi: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Bluetooth orqali olindi: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Fayl qabul qilinmadi: <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Ilova ikonkasi"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Bluetooth orqali xabar ulashish sozlamalari"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Hisobni tanlab bo‘lmadi: bo‘sh joy qolmadi"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth orqali ovoz yoqildi"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth orqali ovoz o‘chirildi"</string>
</resources>
diff --git a/res/values-uz-rUZ/strings_pbap_client.xml b/res/values-uz-rUZ/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-uz-rUZ/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 2b22e74..1e12eb0 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Chấp nhận"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Có thời gian chờ trong khi chấp nhận tệp tới từ \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Chia sẻ qua Bluetooth: Tệp đến"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Bạn có muốn nhận tệp này không?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Có tệp đến từ một thiết bị khác. Hãy xác nhận bạn muốn nhận tệp này."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Tệp đến"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> đã sẵn sàng gửi <xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Chia sẻ qua Bluetooth: Đang nhận <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Chia sẻ qua Bluetooth: Đã nhận <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Chia sẻ qua Bluetooth: Chưa nhận được tệp <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Biểu tượng ứng dụng"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Cài đặt chia sẻ thư qua Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Không chọn được tài khoản. Còn lại 0 khe cắm"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Đã kết nối âm thanh Bluetooth"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Đã ngắt kết nối âm thanh Bluetooth"</string>
</resources>
diff --git a/res/values-vi/strings_pbap_client.xml b/res/values-vi/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-vi/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index f84dad3..5e81da6 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"接受"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"确定"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"接受来自“<xliff:g id="SENDER">%1$s</xliff:g>”的文件时发生超时"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"蓝牙共享:传入文件"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"要接收该文件吗?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"从其他设备传入了文件,请确认是否要接收。"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"有人发送文件给您"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>已准备好发送<xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"蓝牙共享:正在接收“<xliff:g id="FILE">%1$s</xliff:g>”"</string>
<string name="notification_received" msgid="3324588019186687985">"蓝牙共享:已接收“<xliff:g id="FILE">%1$s</xliff:g>”"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"蓝牙共享:未收到文件“<xliff:g id="FILE">%1$s</xliff:g>”"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"应用图标"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"蓝牙消息共享设置"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"无法选择帐号,目前没有任何空档"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"蓝牙音频已连接"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"蓝牙音频已断开连接"</string>
</resources>
diff --git a/res/values-zh-rCN/strings_pbap_client.xml b/res/values-zh-rCN/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-zh-rCN/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 989aab7..b3274c8 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"接受"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"確定"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"接收來自「<xliff:g id="SENDER">%1$s</xliff:g>」的檔案時發生作業逾時"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"藍牙分享:外來檔案"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"您要接收這個檔案嗎?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"來自另一部裝置的檔案,請確認您要接收這個外來檔案。"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"收到的檔案"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>準備傳送<xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"藍牙分享:正在接收 <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"藍牙分享:已接收 <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"藍牙分享:未收到檔案 <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"應用程式圖示"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"藍牙訊息分享設定"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"無法選取帳戶 (剩餘插槽數為 0)"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"已與藍牙音訊連接"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"已與藍牙音訊解除連接"</string>
</resources>
diff --git a/res/values-zh-rHK/strings_pbap_client.xml b/res/values-zh-rHK/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-zh-rHK/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 55b5fc6..2d12036 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"接受"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"確定"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"接收來自「<xliff:g id="SENDER">%1$s</xliff:g>」的檔案時發生作業逾時"</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"藍牙分享:外來檔案"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"您要接收這個檔案嗎?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"其他裝置傳來檔案,請確認是否要接收這個檔案。"</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"有人傳送檔案給您"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g>已準備好傳送<xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"藍牙分享:正在接收 <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"藍牙分享:已接收 <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"藍牙分享:未收到檔案 <xliff:g id="FILE">%1$s</xliff:g>"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"應用程式圖示"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"藍牙郵件分享設定"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"無法選取帳戶 (剩餘插槽數為 0)"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"已與藍牙音訊連線"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"已中斷與藍牙音訊的連線"</string>
</resources>
diff --git a/res/values-zh-rTW/strings_pbap_client.xml b/res/values-zh-rTW/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-zh-rTW/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 3431df4..20b4936 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -36,9 +36,8 @@
<string name="incoming_file_confirm_ok" msgid="281462442932231475">"Yamukela"</string>
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"Kulungile"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Isikhathi siphelile ngenkathi yamukela ifayela engenayo esuka ku- \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
- <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Abelana ne-Bluetooth: Ifayela engenayo"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Ngabe uyafuna ukuthola leli fayela?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Kunefayela engenayo esuka kwenye idivayisi. Qiniseka ukuthi ufuna ukuthola lefayela."</string>
+ <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"Ifayela elingenayo"</string>
+ <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> ulungele ukuthumela i-<xliff:g id="FILE">%2$s</xliff:g>"</string>
<string name="notification_receiving" msgid="4674648179652543984">"Abelana ne-Bluetooth: Ithola<xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received" msgid="3324588019186687985">"Abelana ne-Bluetooth: Itholakele <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"Abelana ne-Bluetooth: Ifayela <xliff:g id="FILE">%1$s</xliff:g> ayitholakalanga"</string>
@@ -129,4 +128,6 @@
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Isithonjana sohlelo lokusebenza"</string>
<string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Izilungiselelo zokwabelana ngomlayezo we-Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Ayikwazi ukukhetha i-akhawunti. 0 izikhala ezisele"</string>
+ <string name="bluetooth_connected" msgid="6718623220072656906">"Umsindo we-Bluetooth uxhunyiwe"</string>
+ <string name="bluetooth_disconnected" msgid="3318303728981478873">"Umsindo we-Bluetooth unqanyuliwe"</string>
</resources>
diff --git a/res/values-zu/strings_pbap_client.xml b/res/values-zu/strings_pbap_client.xml
new file mode 100644
index 0000000..186b23a
--- /dev/null
+++ b/res/values-zu/strings_pbap_client.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type" msgid="6257077123906049322">"com.android.bluetooth.pbapsink"</string>
+</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 53e1095..91f0c0f 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -23,11 +23,12 @@
<bool name="profile_supported_pan">true</bool>
<bool name="profile_supported_pbap">true</bool>
<bool name="profile_supported_gatt">true</bool>
- <bool name="pbap_include_photos_in_vcard">false</bool>
+ <bool name="pbap_include_photos_in_vcard">true</bool>
<bool name="pbap_use_profile_for_owner_vcard">true</bool>
<bool name="profile_supported_map">true</bool>
<bool name="profile_supported_avrcp_controller">false</bool>
<bool name="profile_supported_sap">false</bool>
+ <bool name="profile_supported_pbapclient">false</bool>
<!-- If true, we will require location to be enabled on the device to
fire Bluetooth LE scan result callbacks in addition to having one
@@ -60,4 +61,9 @@
Valid value is 1-14, and recommended value is 8 -->
<integer name="a2dp_absolute_volume_initial_threshold">8</integer>
+ <!-- For A2DP sink ducking volume feature. -->
+ <integer name="a2dp_sink_duck_percent">25</integer>
+
+ <!-- For enabling the hfp client connection service -->
+ <bool name="hfp_client_connection_service_enabled">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0871e74..c03034b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -79,10 +79,8 @@
<string name="incoming_file_confirm_timeout_content">There was a timeout while accepting an incoming file from \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
<!-- Bluetooth File Transfer Acceptance Notification item -->
- <string name="incoming_file_confirm_Notification_title">Bluetooth share: Incoming file</string>
- <string name="incoming_file_confirm_Notification_caption">Do you want to receive this file?</string>
- <string name="incoming_file_toast_msg">Incoming file from another device.
- Confirm you want to receive this file.</string>
+ <string name="incoming_file_confirm_Notification_title">Incoming file</string>
+ <string name="incoming_file_confirm_Notification_content"><xliff:g id="sender">%1$s</xliff:g> is ready to send <xliff:g id="file">%2$s</xliff:g></string>
<!-- Inbound File Transfer Progress Notification item -->
<!-- label for the notification item of receiving file -->
@@ -245,4 +243,6 @@
<string name="bluetooth_map_settings_app_icon">Application Icon</string>
<string name="bluetooth_map_settings_title">Bluetooth Message Sharing Settings</string>
<string name="bluetooth_map_settings_no_account_slots_left">Cannot select account. 0 slots left</string>
+ <string name="bluetooth_connected">Bluetooth audio connected</string>
+ <string name="bluetooth_disconnected">Bluetooth audio disconnected"</string>
</resources>
diff --git a/res/values/strings_pbap_client.xml b/res/values/strings_pbap_client.xml
new file mode 100644
index 0000000..465d4ee
--- /dev/null
+++ b/res/values/strings_pbap_client.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_account_type">com.android.bluetooth.pbapsink</string>
+</resources>
diff --git a/res/xml/authenticator.xml b/res/xml/authenticator.xml
new file mode 100644
index 0000000..f93edea
--- /dev/null
+++ b/res/xml/authenticator.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<account-authenticator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accountType="@string/pbap_account_type" />
diff --git a/src/com/android/bluetooth/Utils.java b/src/com/android/bluetooth/Utils.java
index 100dabd..66d2172 100644
--- a/src/com/android/bluetooth/Utils.java
+++ b/src/com/android/bluetooth/Utils.java
@@ -211,8 +211,9 @@
ok = (foregroundUser == callingUser);
if (!ok) {
// Always allow SystemUI/System access.
- int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
- "com.android.systemui", UserHandle.USER_OWNER);
+ final int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
+ "com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
}
} catch (Exception ex) {
@@ -245,8 +246,9 @@
(foregroundUser == parentUser);
if (!ok) {
// Always allow SystemUI/System access.
- int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
- "com.android.systemui", UserHandle.USER_OWNER);
+ final int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
+ "com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
}
} catch (Exception ex) {
diff --git a/src/com/android/bluetooth/a2dp/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
similarity index 79%
rename from src/com/android/bluetooth/a2dp/A2dpSinkService.java
rename to src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 12a680a..1d84d0f 100644
--- a/src/com/android/bluetooth/a2dp/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-package com.android.bluetooth.a2dp;
+package com.android.bluetooth.a2dpsink;
import android.bluetooth.BluetoothAudioConfig;
+import android.bluetooth.BluetoothAvrcpController;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothA2dpSink;
+import android.content.Intent;
import android.provider.Settings;
import android.util.Log;
+import com.android.bluetooth.a2dpsink.mbs.A2dpMediaBrowserService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.Utils;
@@ -34,7 +37,7 @@
* @hide
*/
public class A2dpSinkService extends ProfileService {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String TAG = "A2dpSinkService";
private A2dpSinkStateMachine mStateMachine;
@@ -49,14 +52,26 @@
}
protected boolean start() {
+ if (DBG) {
+ Log.d(TAG, "start()");
+ }
+ // Start the media browser service.
+ Intent startIntent = new Intent(this, A2dpMediaBrowserService.class);
+ startService(startIntent);
mStateMachine = A2dpSinkStateMachine.make(this, this);
setA2dpSinkService(this);
return true;
}
protected boolean stop() {
- if(mStateMachine != null)
+ if (DBG) {
+ Log.d(TAG, "stop()");
+ }
+ if(mStateMachine != null) {
mStateMachine.doQuit();
+ }
+ Intent stopIntent = new Intent(this, A2dpMediaBrowserService.class);
+ stopService(stopIntent);
return true;
}
@@ -167,15 +182,42 @@
return priority;
}
- public void informAvrcpStatePlaying(BluetoothDevice device) {
- if(mStateMachine != null)
- mStateMachine.informAvrcpStatePlaying(device);
+ /**
+ * Called by AVRCP controller to provide information about the last user intent on CT.
+ *
+ * If the user has pressed play in the last attempt then A2DP Sink component will grant focus to
+ * any incoming sound from the phone (and also retain focus for a few seconds before
+ * relinquishing. On the other hand if the user has pressed pause/stop then the A2DP sink
+ * component will take the focus away but also notify the stack to throw away incoming data.
+ */
+ public void informAvrcpPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
+ if (mStateMachine != null) {
+ if (keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY &&
+ keyState == BluetoothAvrcpController.KEY_STATE_RELEASED) {
+ mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_CT_PLAY);
+ } else if ((keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE ||
+ keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_STOP) &&
+ keyState == BluetoothAvrcpController.KEY_STATE_RELEASED) {
+ mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_CT_PAUSE);
+ }
+ }
}
- public void informAvrcpPassThroughCmd(BluetoothDevice device, int keyCode,
- int keyState) {
- if(mStateMachine != null)
- mStateMachine.informAvrcpPassThroughCmd(device, keyCode, keyState);
+ /**
+ * Called by AVRCP controller to provide information about the last user intent on TG.
+ *
+ * Tf the user has pressed pause on the TG then we can preempt streaming music. This is opposed
+ * to when the streaming stops abruptly (jitter) in which case we will wait for sometime before
+ * stopping playback.
+ */
+ public void informTGStatePlaying(BluetoothDevice device, boolean isPlaying) {
+ if (mStateMachine != null) {
+ if (!isPlaying) {
+ mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_TG_PAUSE);
+ } else {
+ mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_TG_PLAY);
+ }
+ }
}
synchronized boolean isA2dpPlaying(BluetoothDevice device) {
diff --git a/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
similarity index 63%
rename from src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
rename to src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
index 0745642..6e6e2af 100644
--- a/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
@@ -15,7 +15,7 @@
*/
/**
- * Bluetooth A2dp StateMachine
+ * Bluetooth A2dp Sink StateMachine
* (Disconnected)
* | ^
* CONNECT | | DISCONNECTED
@@ -24,9 +24,9 @@
* | ^
* CONNECTED | | CONNECT
* V |
- * (Connected)
+ * (Connected -- See A2dpSinkStreamingStateMachine)
*/
-package com.android.bluetooth.a2dp;
+package com.android.bluetooth.a2dpsink;
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAvrcpController;
@@ -39,13 +39,6 @@
import android.content.Context;
import android.content.Intent;
import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.AudioManager.OnAudioFocusChangeListener;
-import android.media.AudioDevicePort;
-import android.media.AudioPatch;
-import android.media.AudioSystem;
-import android.media.AudioPortConfig;
-import android.media.AudioPort;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -71,9 +64,12 @@
static final int CONNECT = 1;
static final int DISCONNECT = 2;
- final private static int EVENT_TYPE_REQUEST_AUDIO_FOCUS = 4;
private static final int STACK_EVENT = 101;
private static final int CONNECT_TIMEOUT = 201;
+ public static final int EVENT_AVRCP_CT_PLAY = 301;
+ public static final int EVENT_AVRCP_CT_PAUSE = 302;
+ public static final int EVENT_AVRCP_TG_PLAY = 303;
+ public static final int EVENT_AVRCP_TG_PAUSE = 304;
private static final int IS_INVALID_DEVICE = 0;
private static final int IS_VALID_DEVICE = 1;
@@ -82,6 +78,10 @@
public static final int KEY_STATE_PRESSED = 0;
public static final int KEY_STATE_RELEASED = 1;
+ // Connection states.
+ // 1. Disconnected: The connection does not exist.
+ // 2. Pending: The connection is being established.
+ // 3. Connected: The connection is established. The audio connection is in Idle state.
private Disconnected mDisconnected;
private Pending mPending;
private Connected mConnected;
@@ -89,34 +89,11 @@
private A2dpSinkService mService;
private Context mContext;
private BluetoothAdapter mAdapter;
- private final AudioManager mAudioManager;
private IntentBroadcastHandler mIntentBroadcastHandler;
- private final WakeLock mWakeLock;
private static final int MSG_CONNECTION_STATE_CHANGED = 0;
- private static final int AUDIO_FOCUS_LOSS = 0;
- private static final int AUDIO_FOCUS_GAIN = 1;
- private static final int AUDIO_FOCUS_LOSS_TRANSIENT = 2;
- private static final int AUDIO_FOCUS_LOSS_CAN_DUCK = 3;
- private static final int AUDIO_FOCUS_REQUEST_MESSAGE_DELAYED = 500;
- private static final boolean USE_AUDIOTRACK = true;
- private static final boolean BLOCK_REMOTE_INITIATED_STREAMING = true;
- private static boolean mAvrcpPlaySent = false;
-
- private int mAudioFocusAcquired = AUDIO_FOCUS_LOSS;
-
- /* Used to indicate focus lost */
- private static final int STATE_FOCUS_LOST = 0;
- /* Used to inform bluedroid about AVRCP State changes */
- private static final int STATE_FOCUS_READY = 1;
- /* Used to inform bluedroid that focus is granted */
- private static final int STATE_FOCUS_GRANTED = 3;
-
private final Object mLockForPatch = new Object();
- private AudioDevicePort mInPortA2dpSink;
- private AudioDevicePort mOutPortSpeaker;
- private AudioPatch mA2dpSinkAudioPatch;
// mCurrentDevice is the device connected before the state changes
// mTargetDevice is the device to be connected
@@ -171,116 +148,9 @@
setInitialState(mDisconnected);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpSinkService");
mIntentBroadcastHandler = new IntentBroadcastHandler();
-
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- if(!USE_AUDIOTRACK)
- mAudioManager.registerAudioPortUpdateListener(mAudioPortListener);
}
- private void releasePatch() {
- if(USE_AUDIOTRACK) {
- log(" releasePatch: AudioTrack, inform focus loss");
- informAudioFocusStateNative(STATE_FOCUS_LOST);
- return;
- }
- synchronized (mLockForPatch){
- log("releaseAudioPatch AudioPatch = " + mA2dpSinkAudioPatch);
- if(mA2dpSinkAudioPatch != null) {
- mAudioManager.releaseAudioPatch(mA2dpSinkAudioPatch);
- mA2dpSinkAudioPatch = null;
- }
- }
- }
- private void patchPorts() {
- /* If we are using AudioTrack, we need to broadcast and inform bluedroid from here */
- if(USE_AUDIOTRACK){
- log(" patchPorts: AudioTrack, inform focus gain");
- broadcastAudioState(mPlayingDevice, BluetoothA2dpSink.STATE_PLAYING,
- BluetoothA2dpSink.STATE_NOT_PLAYING);
- informAudioFocusStateNative(STATE_FOCUS_GRANTED);
- return;
- }
- synchronized (mLockForPatch) {
- log("patchPorts : mA2dpSinkAudioPatch: " + mA2dpSinkAudioPatch +
- " mPlayingDevice " + mPlayingDevice + "mOutPortSpeaker" + mOutPortSpeaker);
- if((mA2dpSinkAudioPatch == null) && (mPlayingDevice != null) &&
- (mOutPortSpeaker != null) && (mInPortA2dpSink != null)) {
- if((mAudioConfigs == null)||(!mAudioConfigs.containsKey(mPlayingDevice))) {
- log(" AudioConfigs not yet received, returning");
- return;
- }
- int sampleRate = getAudioConfig(mPlayingDevice).getSampleRate();
- int channelMask = getAudioConfig(mPlayingDevice).getChannelConfig();
- int format = getAudioConfig(mPlayingDevice).getAudioFormat();
-
- AudioPortConfig sourcePortArray[] =
- {mInPortA2dpSink.buildConfig(sampleRate, channelMask, format, null)};
- AudioPortConfig sinkPortArray[] =
- {mOutPortSpeaker.buildConfig(sampleRate, channelMask, format, null)};
- AudioPatch patchPortArray[] = {null};
- /* broadCast Audio State */
- broadcastAudioState(mPlayingDevice, BluetoothA2dpSink.STATE_PLAYING,
- BluetoothA2dpSink.STATE_NOT_PLAYING);
-
- int ret = mAudioManager.createAudioPatch(patchPortArray, sourcePortArray,
- sinkPortArray);
- if (ret == 0) {
- mA2dpSinkAudioPatch = patchPortArray[0];
- log("PatchCreated success: " + ret + " mA2dpSinkAudioPatch: "
- + mA2dpSinkAudioPatch);
- } else {
- log("PatchCreated failed returned: " + ret);
- }
- }
- }
- }
-
- private final AudioManager.OnAudioPortUpdateListener mAudioPortListener =
- new AudioManager.OnAudioPortUpdateListener(){
- public void onAudioPortListUpdate(AudioPort[] portList) {
- synchronized (mLockForPatch){
- log("onAudioPortListUpdate");
- mOutPortSpeaker = null;
- mInPortA2dpSink = null;
-
- for (int i = 0; i < portList.length; i++) {
- AudioPort port = portList[i];
- if(port instanceof AudioDevicePort) {
- AudioDevicePort devicePort = (AudioDevicePort)port;
- if(devicePort.type() == AudioSystem.DEVICE_OUT_SPEAKER) {
- log("Updating Speaker Port");
- mOutPortSpeaker = devicePort;
- } else if(devicePort.type() == AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) {
- log("Updating In Port A2DP Sink");
- mInPortA2dpSink = devicePort;
- /* Check if we still have focus */
- if ((mAudioFocusAcquired == AUDIO_FOCUS_GAIN) &&
- (mInPortA2dpSink != null) && (mA2dpSinkAudioPatch == null)) {
- /* This is the case of Port available
- * later than focus acquired. Try patching ports now
- */
- log(" Sink Port updated, but patch not made");
- patchPorts();
- }
- }
- }
- }
- }
- }
-
- public void onAudioPatchListUpdate(AudioPatch[] patchList){
- log("onAudioPatchListUpdate");
- for(int i = 0; i < patchList.length; i++) {
- log("Patch List " + i +" : "+ patchList[i]);
- }
- }
-
- public void onServiceDied() {
- log(" Service Died");
- }
- };
static A2dpSinkStateMachine make(A2dpSinkService svc, Context context) {
Log.d("A2dpSinkStateMachine", "make");
@@ -294,18 +164,8 @@
}
public void cleanup() {
- /*
- * remove cleanup of resources
- * not acquired in AudioTrack approach
- */
- if(!USE_AUDIOTRACK)
- releasePatch();
cleanupNative();
mAudioConfigs.clear();
- if(USE_AUDIOTRACK) return;
- if (mContext != null)
- mContext.unregisterReceiver(mA2dpReceiver);
- mAudioManager.unregisterAudioPortUpdateListener(mAudioPortListener);
}
public void dump(StringBuilder sb) {
@@ -319,11 +179,6 @@
@Override
public void enter() {
log("Enter Disconnected: " + getCurrentMessage().what);
- /*
- * Remove audio focus request when not in connected state
- */
- removeMessages(EVENT_TYPE_REQUEST_AUDIO_FOCUS);
- mAvrcpPlaySent = false;
}
@Override
@@ -446,11 +301,6 @@
@Override
public void enter() {
log("Enter Pending: " + getCurrentMessage().what);
- /*
- * Remove audio focus request when not in connected state
- */
- removeMessages(EVENT_TYPE_REQUEST_AUDIO_FOCUS);
- mAvrcpPlaySent = false;
}
@Override
@@ -482,6 +332,7 @@
break;
case STACK_EVENT:
StackEvent event = (StackEvent) message.obj;
+ log("STACK_EVENT " + event.type);
switch (event.type) {
case EVENT_TYPE_CONNECTION_STATE_CHANGED:
removeMessages(CONNECT_TIMEOUT);
@@ -503,6 +354,7 @@
// in Pending state
private void processConnectionEvent(int state, BluetoothDevice device) {
+ log("processConnectionEvent state " + state);
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
mAudioConfigs.remove(device);
@@ -532,7 +384,7 @@
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
// outgoing connection failed
- broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
+ broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpSinkStateMachine.this) {
mTargetDevice = null;
@@ -552,6 +404,7 @@
break;
case CONNECTION_STATE_CONNECTED:
if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
+ loge("current device is not null");
// disconnection failed
broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTING);
@@ -564,6 +417,7 @@
transitionTo(mConnected);
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
+ loge("target device is not null");
broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpSinkStateMachine.this) {
@@ -572,6 +426,7 @@
transitionTo(mConnected);
}
} else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
+ loge("incoming device is not null");
broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpSinkStateMachine.this) {
@@ -633,13 +488,14 @@
}
private class Connected extends State {
+ private A2dpSinkStreamingStateMachine mStreaming;
@Override
public void enter() {
log("Enter Connected: " + getCurrentMessage().what);
// Upon connected, the audio starts out as stopped
broadcastAudioState(mCurrentDevice, BluetoothA2dpSink.STATE_NOT_PLAYING,
BluetoothA2dpSink.STATE_PLAYING);
- mAvrcpPlaySent = false;
+ mStreaming = A2dpSinkStreamingStateMachine.make(A2dpSinkStateMachine.this, mContext);
}
@Override
@@ -650,7 +506,6 @@
return NOT_HANDLED;
}
- boolean retValue = HANDLED;
switch(message.what) {
case CONNECT:
{
@@ -669,10 +524,12 @@
synchronized (A2dpSinkStateMachine.this) {
mTargetDevice = device;
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.DISCONNECT);
transitionTo(mPending);
}
}
- break;
+ break;
+
case DISCONNECT:
{
BluetoothDevice device = (BluetoothDevice) message.obj;
@@ -686,22 +543,12 @@
BluetoothProfile.STATE_DISCONNECTED);
break;
}
-
- if (mAudioFocusAcquired != AUDIO_FOCUS_LOSS) {
- releasePatch();
- int status = mAudioManager.abandonAudioFocus(mAudioFocusListener);
- if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS;
- }
- }
mPlayingDevice = null;
-
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.DISCONNECT);
transitionTo(mPending);
}
- break;
- case EVENT_TYPE_REQUEST_AUDIO_FOCUS:
- processAudioFocusRequestEvent(1, (BluetoothDevice) message.obj);
- break;
+ break;
+
case STACK_EVENT:
StackEvent event = (StackEvent) message.obj;
switch (event.type) {
@@ -719,10 +566,22 @@
break;
}
break;
+
+ case EVENT_AVRCP_CT_PLAY:
+ case EVENT_AVRCP_TG_PLAY:
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.ACT_PLAY);
+ break;
+
+ case EVENT_AVRCP_CT_PAUSE:
+ case EVENT_AVRCP_TG_PAUSE:
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.ACT_PAUSE);
+ break;
+
+
default:
return NOT_HANDLED;
}
- return retValue;
+ return HANDLED;
}
// in Connected state
@@ -737,17 +596,11 @@
broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED);
synchronized (A2dpSinkStateMachine.this) {
+ // Take care of existing audio focus in the streaming state machine.
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.DISCONNECT);
mCurrentDevice = null;
transitionTo(mDisconnected);
}
- releasePatch();
- int status = mAudioManager.abandonAudioFocus(mAudioFocusListener);
- if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS;
- /* Check if we need to release patch here
- * Would we ever get disconnect directly without
- * Suspend or Stoppped being called before */
- }
} else {
loge("Disconnected from unknown device: " + device);
}
@@ -757,6 +610,7 @@
break;
}
}
+
private void processAudioStateEvent(int state, BluetoothDevice device) {
if (!mCurrentDevice.equals(device)) {
loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
@@ -766,82 +620,17 @@
log(" processAudioStateEvent in state " + state);
switch (state) {
case AUDIO_STATE_STARTED:
- if (mPlayingDevice == null) {
- mPlayingDevice = device;
- }
- if(!USE_AUDIOTRACK)
- requestAudioFocus(true, device, 0);
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.SRC_STR_START);
break;
case AUDIO_STATE_REMOTE_SUSPEND:
case AUDIO_STATE_STOPPED:
- mPlayingDevice = null;
- releasePatch();
- broadcastAudioState(device, BluetoothA2dpSink.STATE_NOT_PLAYING,
- BluetoothA2dpSink.STATE_PLAYING);
- if (mAudioFocusAcquired == AUDIO_FOCUS_LOSS) {
- /* Audio Focus was already lost, no need to do it again */
- return;
- }
- if ((mAudioFocusAcquired == AUDIO_FOCUS_LOSS_TRANSIENT) &&
- (state == AUDIO_STATE_REMOTE_SUSPEND)) {
- log(" Dont't Loose audiofocus in case of suspend ");
- break;
- }
- int status = mAudioManager.abandonAudioFocus(mAudioFocusListener);
- if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS;
- }
+ mStreaming.sendMessage(A2dpSinkStreamingStateMachine.SRC_STR_STOP);
break;
default:
loge("Audio State Device: " + device + " bad state: " + state);
break;
}
}
-
- private void processAudioFocusRequestEvent(int enable, BluetoothDevice device) {
- if ((mCurrentDevice != null) && (mCurrentDevice.equals(device))
- && (1 == enable)) {
-
- if (BLOCK_REMOTE_INITIATED_STREAMING) {
- /*
- * If play command is not send from UI
- * don't rquest focus.
- */
- if (!mAvrcpPlaySent) {
- log(" AVRCP Play not sent from UI, send Pause");
- SendPassThruPause(device);
- if(USE_AUDIOTRACK)
- informAudioFocusStateNative(STATE_FOCUS_LOST);
- /*
- * TODO, any additional intent that we want to send
- */
- return;
- }
- }
- if (mAudioFocusAcquired == AUDIO_FOCUS_LOSS_TRANSIENT) {
- log(" Transient Loss Still, Don't acquire focus, Send PAUSE");
- SendPassThruPause(device);
- if(USE_AUDIOTRACK)
- informAudioFocusStateNative(STATE_FOCUS_LOST);
- return;
- }
- else if (mAudioFocusAcquired == AUDIO_FOCUS_GAIN) {
- patchPorts();
- return; /* if we already have focus, don't request again */
- }
- int status = mAudioManager.requestAudioFocus(mAudioFocusListener,
- AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN);
- log(" Audio Focus Request returned " + status);
- if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocusAcquired = AUDIO_FOCUS_GAIN;
- patchPorts();
- }
- else {
- log("Can't acquire Focus, request with delay");
- requestAudioFocus(true, device, AUDIO_FOCUS_REQUEST_MESSAGE_DELAYED);
- }
- }
- }
}
private void processAudioConfigEvent(BluetoothAudioConfig audioConfig, BluetoothDevice device) {
@@ -905,50 +694,6 @@
return false;
}
- void informAvrcpStatePlaying(BluetoothDevice device) {
- if ((mPlayingDevice != null) && (device.equals(mPlayingDevice))) {
- log(" AVRCP State is Playing");
- /* If call is ongoing, Focus already acquired, no need to
- * send this info to bluedroid
- */
- /*
- * If Flag is set, this info should not be used
- * to request focus.
- */
- if (BLOCK_REMOTE_INITIATED_STREAMING)
- return;
- if(mAudioFocusAcquired == AUDIO_FOCUS_LOSS_TRANSIENT)
- SendPassThruPause(device);
- if((USE_AUDIOTRACK) && (mAudioFocusAcquired == AUDIO_FOCUS_LOSS))
- informAudioFocusStateNative(STATE_FOCUS_READY);
- }
- }
- void informAvrcpPassThroughCmd(BluetoothDevice device, int keyCode,
- int keyState) {
- if (!BLOCK_REMOTE_INITIATED_STREAMING)
- return;
- log(" informAvrcpPassThroughCmd device: " + device + " key: " + keyCode
- + " keyState: " + keyState);
- if ((keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY) &&
- (keyState == BluetoothAvrcpController.KEY_STATE_RELEASED)) {
- mAvrcpPlaySent = true;
- /*
- * SO that if packets are received, we can request for focus
- * from bluedroid
- */
- informAudioFocusStateNative(STATE_FOCUS_READY);
- }
- if (((keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE)||
- (keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_STOP)) &&
- (keyState == BluetoothAvrcpController.KEY_STATE_RELEASED)) {
- mAvrcpPlaySent = false;
- /*
- * emulate a remote initiaited suspend.
- */
- onAudioStateChanged(AUDIO_STATE_REMOTE_SUSPEND,
- getByteAddress(device));
- }
- }
boolean okToConnect(BluetoothDevice device) {
AdapterService adapterService = AdapterService.getAdapterService();
boolean ret = true;
@@ -986,11 +731,6 @@
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
int delay = 0;
- if(!USE_AUDIOTRACK)
- delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState,
- BluetoothProfile.A2DP_SINK);
-
- mWakeLock.acquire();
mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
MSG_CONNECTION_STATE_CHANGED,
prevState,
@@ -1048,27 +788,6 @@
sendMessage(STACK_EVENT, event);
}
- private void onAudioFocusRequested(byte[] address) {
- BluetoothDevice device = getDevice(address);
- if (BLOCK_REMOTE_INITIATED_STREAMING) {
- /*
- * IN this case we mPlayingDevice can be null
- */
- requestAudioFocus(true, device, 0);
- }
- else if(device.equals(mPlayingDevice))
- requestAudioFocus(true, device, 0);
- }
-
- private void requestAudioFocus(boolean enable, BluetoothDevice device, int delay) {
- log(" requestAudioFocus for " + device + " enable " + enable);
- if (enable) {
- // send a request for audio_focus
- Message posMsg = obtainMessage(EVENT_TYPE_REQUEST_AUDIO_FOCUS, device);
- sendMessageDelayed(posMsg, delay);
- }
- }
-
private BluetoothDevice getDevice(byte[] address) {
return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
}
@@ -1103,7 +822,6 @@
switch (msg.what) {
case MSG_CONNECTION_STATE_CHANGED:
onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
- mWakeLock.release();
break;
}
}
@@ -1128,131 +846,13 @@
}
}
- public boolean SendPassThruPause(BluetoothDevice mDevice) {
- log("SendPassThruPause + ");
- AvrcpControllerService avrcpCtrlService = AvrcpControllerService.getAvrcpControllerService();
- if ((avrcpCtrlService != null) && (mDevice != null) &&
- (avrcpCtrlService.getConnectedDevices().contains(mDevice))){
-
- avrcpCtrlService.sendPassThroughCmd(mDevice,
- BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE,
- BluetoothAvrcpController.KEY_STATE_PRESSED);
- avrcpCtrlService.sendPassThroughCmd(mDevice,
- BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE,
- BluetoothAvrcpController.KEY_STATE_RELEASED);
- log(" SendPassThruPause command sent - ");
- return true;
- } else {
- log("passthru command not sent, connection unavailable");
- return false;
- }
- }
-
- private final BroadcastReceiver mA2dpReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- log("onReceive " + action);
- if (action.equals("com.android.music.musicservicecommand")) {
- String cmd = intent.getStringExtra("command");
- log("Command Received " + cmd);
- if (cmd.equals("pause")) {
- if (mCurrentDevice != null) {
- if (SendPassThruPause(mCurrentDevice)) {
- log(" Sending AVRCP Pause");
- } else {
- log(" Sending Disconnect AVRCP Not Up");
- disconnectA2dpNative(getByteAddress(mCurrentDevice));
- }
- releasePatch();
- if (mAudioFocusAcquired != AUDIO_FOCUS_LOSS) {
- int status = mAudioManager.abandonAudioFocus(mAudioFocusListener);
- log("abandonAudioFocus returned" + status);
- if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS;
- }
- }
- }
- }
- }
- }
- };
-
- private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
- public void onAudioFocusChange(int focusChange){
- log("onAudioFocusChangeListener focuschange " + focusChange);
- switch(focusChange){
- case AudioManager.AUDIOFOCUS_LOSS:
- if (mCurrentDevice != null) {
- /* First Release the Patch Anyways */
- if (SendPassThruPause(mCurrentDevice)) {
- log(" Sending AVRCP Pause");
- } else {
- log(" Sending Disconnect AVRCP Not Up");
- disconnectA2dpNative(getByteAddress(mCurrentDevice));
- }
- if(USE_AUDIOTRACK)
- informAudioFocusStateNative(STATE_FOCUS_LOST);
- int status = mAudioManager.abandonAudioFocus(mAudioFocusListener);
- log("abandonAudioFocus returned" + status);
- if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS;
- }
- }
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
- if ((mCurrentDevice != null) && (getCurrentState() == mConnected)) {
- /* don't abandon focus, but fake focus loss */
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS_TRANSIENT;
- if (SendPassThruPause(mCurrentDevice)) {
- log(" Sending AVRCP Pause");
- } else {
- log(" AVRCP Connection not UP");
- disconnectA2dpNative(getByteAddress(mCurrentDevice));
- /* TODO what shld we do in case AVRCP connection is not there */
- }
- if(USE_AUDIOTRACK)
- informAudioFocusStateNative(STATE_FOCUS_LOST);
- }
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
- log(" Received AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK ");
- mAudioFocusAcquired = AUDIO_FOCUS_LOSS_CAN_DUCK;
- // TODO(sanketa): Currently we do not honor this and keep playing music at the
- // previous volume. This should be changed to implement ducking.
- break;
- case AudioManager.AUDIOFOCUS_GAIN:
- // we got focus gain
- if ((mCurrentDevice != null) && (getCurrentState() == mConnected)) {
- if (mAudioFocusAcquired == AUDIO_FOCUS_LOSS_CAN_DUCK) {
- log(" Received Can_Duck earlier, Ignore Now ");
- mAudioFocusAcquired = AUDIO_FOCUS_GAIN;
- break;
- }
- mAudioFocusAcquired = AUDIO_FOCUS_GAIN;
- if (SendPassThruPlay(mCurrentDevice)) {
- log(" Sending AVRCP Play");
- } else {
- log(" AVRCP Connection not up");
- /* TODO what shld we do in case AVRCP connection is not there */
- }
- if(USE_AUDIOTRACK)
- informAudioFocusStateNative(STATE_FOCUS_GRANTED);
- }
- break;
- default:
- break;
- }
- }
- };
-
// Event types for STACK_EVENT message
final private static int EVENT_TYPE_NONE = 0;
final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
final private static int EVENT_TYPE_AUDIO_CONFIG_CHANGED = 3;
- // Do not modify without updating the HAL bt_av.h files.
+ // Do not modify without updating the HAL bt_av.h files.
// match up with btav_connection_state_t enum of bt_av.h
final static int CONNECTION_STATE_DISCONNECTED = 0;
@@ -1270,5 +870,6 @@
private native void cleanupNative();
private native boolean connectA2dpNative(byte[] address);
private native boolean disconnectA2dpNative(byte[] address);
- private native void informAudioFocusStateNative(int focusGranted);
+ public native void informAudioFocusStateNative(int focusGranted);
+ public native void informAudioTrackGainNative(float focusGranted);
}
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java
new file mode 100644
index 0000000..fb62c95
--- /dev/null
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.a2dpsink;
+
+import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothAvrcpController;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.bluetooth.avrcp.AvrcpControllerService;
+import com.android.bluetooth.R;
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+/**
+ * Bluetooth A2DP SINK Streaming StateMachine.
+ *
+ * This state machine defines how the stack behaves once the A2DP connection is established and both
+ * devices are ready for streaming. For simplification we assume that the connection can either
+ * stream music immediately (i.e. data packets coming in or have potential to come in) or it cannot
+ * stream (i.e. Idle and Open states are treated alike). See Fig 4-1 of GAVDP Spec 1.0.
+ *
+ * Legend:
+ * SRC: Source (Remote)
+ * (SRC, F) - Remote is not streaming. F stands for false.
+ * (SRC, T) - Remote is intent to streaming. T stands for true.
+ * ACT: Action
+ * (ACT, F) - Local/Remote user intent is to pause/stop (AVRCP pause/stop).
+ * (ACT, T) - Local/Remote user intent is to play (AVRCP play).
+ * The way to detect action is two fold:
+ * -- We can detect action on the SNK side by directly monitoring the AVRCP controller service.
+ * -- On the SRC side, any AVRCP action will be accompanied by an update via AVRCP and hence we can
+ * update our action state.
+ *
+ * A state will be a combination of SRC and ACT state. Hence a state such as:
+ * (F, T) will mean that user has shown intent to play on local or remote device (second T) but the
+ * connection is not in streaming state yet.
+ *
+ * -----------------------------------------------------------------------------------------------
+ * Start State | End State | Transition(s)
+ * -----------------------------------------------------------------------------------------------
+ * (F, F) (F, T) ACT Play (No streaming in either states)
+ * (F, F) (T, F) Remote streams (play depends on policy)
+ * (T, F) (F, F) Remote stops streaming.
+ * (T, F) (T, T) ACT Play (streaming already existed).
+ * (F, T) (F, F) ACT Pause.
+ * (F, T) (T, T) Remote starts streaming (ACT Play already existed)
+ * (T, T) (F, T) Remote stops streaming.
+ * (T, T) (F, F) ACT stop.
+ * (T, T) (T, F) ACT pause.
+ *
+ * -----------------------------------------------------------------------------------------------
+ * State | Action(s)
+ * -----------------------------------------------------------------------------------------------
+ * (F, F) 1. Lose audio focus (if it exists) and notify fluoride of audio focus loss.
+ * 2. Stop AVRCP from pushing updates to UI.
+ * (T, F) 1. If policy is opt-in then get focus and stream (get audio focus etc).
+ * 2. Else throw away the data (lose audio focus etc).
+ * (F, T) In this state the source does not stream although we have play intent.
+ * 1. Show a spinny that data will come through.
+ * (T, T) 1. Request Audio Focus and on success update AVRCP to show UI updates.
+ * 2. On Audio focus enable streaming in Fluoride.
+ */
+final class A2dpSinkStreamingStateMachine extends StateMachine {
+ private static final boolean DBG = true;
+ private static final String TAG = "A2dpSinkStreamingStateMachine";
+ private static final int ACT_PLAY_NUM_RETRIES = 5;
+ private static final int ACT_PLAY_RETRY_DELAY = 2000; // millis.
+ private static final int DEFAULT_DUCK_PERCENT = 25;
+
+ // Streaming states (see the description above).
+ private SRC_F_ACT_F mSrcFActF;
+ private SRC_F_ACT_T mSrcFActT;
+ private SRC_T_ACT_F mSrcTActF;
+ private SRC_T_ACT_T mSrcTActT;
+
+ // Transitions.
+ public static final int SRC_STR_START = 0;
+ public static final int SRC_STR_STOP = 1;
+ public static final int SRC_STR_STOP_JITTER_WAIT_OVER = 2;
+ public static final int ACT_PLAY = 3;
+ public static final int ACT_PLAY_RETRY = 4;
+ public static final int ACT_PAUSE = 5;
+ public static final int AUDIO_FOCUS_CHANGE = 6;
+ public static final int DISCONNECT = 7;
+
+ // Private variables.
+ private A2dpSinkStateMachine mA2dpSinkSm;
+ private Context mContext;
+ private AudioManager mAudioManager;
+ // Set default focus to loss since we have never requested it before.
+ private int mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+
+ /* Used to indicate focus lost */
+ private static final int STATE_FOCUS_LOST = 0;
+ /* Used to inform bluedroid that focus is granted */
+ private static final int STATE_FOCUS_GRANTED = 1;
+
+ /* Wait in millis before the ACT loses focus on SRC jitter when streaming */
+ private static final int SRC_STR_JITTER_WAIT = 5 * 1000; // 5sec
+
+ /* Focus changes when we are currently holding focus (i.e. we're in SRC_T_ACT_T state). */
+ private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
+ public void onAudioFocusChange(int focusChange){
+ if (DBG) {
+ Log.d(TAG, "onAudioFocusChangeListener focuschange " + focusChange);
+ }
+ A2dpSinkStreamingStateMachine.this.sendMessage(AUDIO_FOCUS_CHANGE, focusChange);
+ }
+ };
+
+ private A2dpSinkStreamingStateMachine(A2dpSinkStateMachine a2dpSinkSm, Context context) {
+ super("A2dpSinkStreamingStateMachine");
+ mA2dpSinkSm = a2dpSinkSm;
+ mContext = context;
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+ mSrcFActF = new SRC_F_ACT_F();
+ mSrcFActT = new SRC_F_ACT_T();
+ mSrcTActF = new SRC_T_ACT_F();
+ mSrcTActT = new SRC_T_ACT_T();
+
+ // States are independent of each other. We simply use transitionTo.
+ addState(mSrcFActF);
+ addState(mSrcFActT);
+ addState(mSrcTActF);
+ addState(mSrcTActT);
+ setInitialState(mSrcFActF);
+
+ }
+
+ public static A2dpSinkStreamingStateMachine make(
+ A2dpSinkStateMachine a2dpSinkSm, Context context) {
+ if (DBG) {
+ Log.d(TAG, "make");
+ }
+ A2dpSinkStreamingStateMachine a2dpStrStateMachine =
+ new A2dpSinkStreamingStateMachine(a2dpSinkSm, context);
+ a2dpStrStateMachine.start();
+ return a2dpStrStateMachine;
+ }
+
+ /**
+ * Utility functions that can be used by all states.
+ */
+ private boolean requestAudioFocus() {
+ return (mAudioManager.requestAudioFocus(
+ mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) ==
+ AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+ }
+
+ private void startAvrcpUpdates() {
+ // Since AVRCP gets started after A2DP we may need to request it later in cycle.
+ AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();
+
+ if (DBG) {
+ Log.d(TAG, "startAvrcpUpdates");
+ }
+ if (avrcpService != null && avrcpService.getConnectedDevices().size() == 1) {
+ avrcpService.startAvrcpUpdates();
+ } else {
+ Log.e(TAG, "startAvrcpUpdates failed because of connection.");
+ }
+ }
+
+ private void stopAvrcpUpdates() {
+ // Since AVRCP gets started after A2DP we may need to request it later in cycle.
+ AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();
+
+ if (DBG) {
+ Log.d(TAG, "stopAvrcpUpdates");
+ }
+ if (avrcpService != null && avrcpService.getConnectedDevices().size() == 1) {
+ avrcpService.stopAvrcpUpdates();
+ } else {
+ Log.e(TAG, "stopAvrcpUpdates failed because of connection.");
+ }
+ }
+
+ private void sendAvrcpPause() {
+ // Since AVRCP gets started after A2DP we may need to request it later in cycle.
+ AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();
+
+ if (DBG) {
+ Log.d(TAG, "sendAvrcpPause");
+ }
+ if (avrcpService != null && avrcpService.getConnectedDevices().size() == 1) {
+ if (DBG) {
+ Log.d(TAG, "Pausing AVRCP.");
+ }
+ avrcpService.sendPassThroughCmd(
+ avrcpService.getConnectedDevices().get(0),
+ BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE,
+ BluetoothAvrcpController.KEY_STATE_PRESSED);
+ avrcpService.sendPassThroughCmd(
+ avrcpService.getConnectedDevices().get(0),
+ BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE,
+ BluetoothAvrcpController.KEY_STATE_RELEASED);
+ } else {
+ Log.e(TAG, "Passthrough not sent, connection un-available.");
+ }
+ }
+
+ private void sendAvrcpPlay() {
+ // Since AVRCP gets started after A2DP we may need to request it later in cycle.
+ AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();
+
+ if (DBG) {
+ Log.d(TAG, "sendAvrcpPlay");
+ }
+ if (avrcpService != null && avrcpService.getConnectedDevices().size() == 1) {
+ if (DBG) {
+ Log.d(TAG, "Playing AVRCP.");
+ }
+ avrcpService.sendPassThroughCmd(
+ avrcpService.getConnectedDevices().get(0),
+ BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY,
+ BluetoothAvrcpController.KEY_STATE_PRESSED);
+ avrcpService.sendPassThroughCmd(
+ avrcpService.getConnectedDevices().get(0),
+ BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY,
+ BluetoothAvrcpController.KEY_STATE_RELEASED);
+ } else {
+ Log.e(TAG, "Passthrough not sent, connection un-available.");
+ }
+ }
+
+ private void startFluorideStreaming() {
+ mA2dpSinkSm.informAudioFocusStateNative(STATE_FOCUS_GRANTED);
+ mA2dpSinkSm.informAudioTrackGainNative(1.0f);
+ }
+
+ private void stopFluorideStreaming() {
+ mA2dpSinkSm.informAudioFocusStateNative(STATE_FOCUS_LOST);
+ }
+
+ private void setFluorideAudioTrackGain(float gain) {
+ mA2dpSinkSm.informAudioTrackGainNative(gain);
+ }
+
+ private class SRC_F_ACT_F extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_F_ACT_F";
+ @Override
+ public void enter() {
+ if (DBG) {
+ Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) {
+ Log.d(STATE_TAG, " process message: " + message.what);
+ }
+ switch (message.what) {
+ case SRC_STR_START:
+ // Opt out of all sounds without AVRCP play. We simply throw away.
+ transitionTo(mSrcTActF);
+ break;
+
+ case ACT_PLAY:
+ // Wait in next state for actual playback. We defer the message so that the next
+ // state (SRC_F_ACT_T) can execute the retry logic.
+ deferMessage(message);
+ transitionTo(mSrcFActT);
+ break;
+
+ case DISCONNECT:
+ mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+ break;
+
+ case AUDIO_FOCUS_CHANGE:
+ // If we are regaining focus after transient loss this indicates that we should
+ // press play again.
+ int newAudioFocus = message.arg1;
+ if (DBG) {
+ Log.d(STATE_TAG,
+ "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
+ }
+ if (mCurrentAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT &&
+ newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
+ sendAvrcpPlay();
+ // We should transition to SRC_F_ACT_T after this message. We also send some
+ // retries here because after phone calls we may have race conditions.
+ sendMessageDelayed(
+ ACT_PLAY_RETRY, ACT_PLAY_NUM_RETRIES, ACT_PLAY_RETRY_DELAY);
+ }
+ mCurrentAudioFocus = newAudioFocus;
+ break;
+
+ default:
+ Log.e(TAG, "Don't know how to handle " + message.what);
+ }
+ return HANDLED;
+ }
+ }
+
+ private class SRC_F_ACT_T extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_F_ACT_T";
+ private boolean mPlay = false;
+
+ @Override
+ public void enter() {
+ if (DBG) {
+ Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) {
+ Log.d(STATE_TAG, " process message: " + message.what);
+ }
+ switch (message.what) {
+ case SRC_STR_START:
+ deferMessage(message);
+ transitionTo(mSrcTActT);
+ break;
+
+ case ACT_PAUSE:
+ transitionTo(mSrcFActF);
+ break;
+
+ case ACT_PLAY:
+ // Retry if the remote has not yet started playing music. This is seen in some
+ // devices where after the phone call it requires multiple play commands to
+ // start music.
+ break;
+
+ case ACT_PLAY_RETRY:
+ if (message.arg1 > 0) {
+ Log.d(STATE_TAG, "Retry " + message.arg1);
+ sendAvrcpPlay();
+ sendMessageDelayed(ACT_PLAY_RETRY, message.arg1 - 1, ACT_PLAY_RETRY_DELAY);
+ }
+ break;
+
+
+ case DISCONNECT:
+ deferMessage(message);
+ transitionTo(mSrcFActF);
+ mPlay = false;
+ break;
+
+ case AUDIO_FOCUS_CHANGE:
+ int newAudioFocus = message.arg1;
+ if (DBG) {
+ Log.d(STATE_TAG,
+ "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
+ }
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
+ sendAvrcpPlay();
+ mCurrentAudioFocus = newAudioFocus;
+ } else if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ sendAvrcpPause();
+ mCurrentAudioFocus = newAudioFocus;
+ } else if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
+ mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+ }
+ break;
+
+ default:
+ Log.e(TAG, "Don't know how to handle " + message.what);
+ }
+ return HANDLED;
+ }
+ }
+
+ private class SRC_T_ACT_F extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_T_ACT_F";
+ @Override
+ public void enter() {
+ if (DBG) {
+ Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) {
+ Log.d(STATE_TAG, " process message: " + message.what);
+ }
+ switch (message.what) {
+ case SRC_STR_STOP:
+ transitionTo(mSrcFActF);
+ break;
+
+ case ACT_PLAY:
+ deferMessage(message);
+ transitionTo(mSrcTActT);
+ break;
+
+ case DISCONNECT:
+ deferMessage(message);
+ transitionTo(mSrcFActF);
+ break;
+
+ case AUDIO_FOCUS_CHANGE:
+ // If we regain focus from TRANSIENT that means that the remote was playing all
+ // this while although we must have sent a PAUSE (see focus loss in SRC_T_ACT_T
+ // state). In any case, we should resume music here if that is the case.
+ int newAudioFocus = message.arg1;
+ if (DBG) {
+ Log.d(STATE_TAG,
+ "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
+ }
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS ||
+ newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ sendAvrcpPause();
+ } else if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
+ sendAvrcpPlay();
+ }
+ mCurrentAudioFocus = newAudioFocus;
+ break;
+
+ default:
+ Log.e(TAG, "Don't know how to handle " + message.what);
+ }
+ return HANDLED;
+ }
+ }
+
+ private class SRC_T_ACT_T extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_T_ACT_T";
+ private boolean mWaitForJitter = false;
+ @Override
+ public void enter() {
+ if (DBG) {
+ Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) {
+ Log.d(STATE_TAG, " process message: " + message.what);
+ }
+ switch (message.what) {
+ case ACT_PAUSE:
+ // Stop avrcp updates.
+ stopAvrcpUpdates();
+ stopFluorideStreaming();
+ transitionTo(mSrcTActF);
+ if (mCurrentAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
+ // If we have focus gain and we still get pause that means that we must have
+ // gotten a PAUSE by user explicitly pressing PAUSE on Car or Phone. Hence
+ // we release focus.
+ mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+ }
+ break;
+
+ case SRC_STR_STOP:
+ stopAvrcpUpdates();
+ stopFluorideStreaming();
+ transitionTo(mSrcFActT);
+ // This could be variety of reasons including that the remote is going to
+ // (eventually send us pause) or the device is going to go into a call state
+ // etc. Also it may simply be stutter of music. Instead of sending pause
+ // prematurely we wait for either a Pause from remote or AudioFocus change owing
+ // an ongoing call.
+ break;
+
+ case SRC_STR_START:
+ case ACT_PLAY:
+ Log.d(STATE_TAG, "Current Audio Focus " + mCurrentAudioFocus);
+ boolean startStream = true;
+ if (mCurrentAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
+ if (!requestAudioFocus()) {
+ Log.e(STATE_TAG, "Cannot get focus, hence not starting streaming.");
+ startStream = false;
+ } else {
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_GAIN;
+ }
+ }
+ if (startStream) {
+ startAvrcpUpdates();
+ startFluorideStreaming();
+ }
+ // If we did not get focus, it may mean that the device in a call state and
+ // hence we should wait for an audio focus event.
+ break;
+
+ // On Audio Focus events we stay in the same state but this can potentially change
+ // if we playback.
+ case AUDIO_FOCUS_CHANGE:
+ int newAudioFocus = (int) message.arg1;
+ if (DBG) {
+ Log.d(STATE_TAG,
+ "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
+ }
+
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
+ // We have gained focus so play with 1.0 gain.
+ sendAvrcpPlay();
+ startAvrcpUpdates();
+ startFluorideStreaming();
+ setFluorideAudioTrackGain(1.0f);
+ } else if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ // Make the volume duck.
+ int duckPercent =
+ mContext.getResources().getInteger(R.integer.a2dp_sink_duck_percent);
+ if (duckPercent < 0 || duckPercent > 100) {
+ Log.e(STATE_TAG, "Invalid duck percent using default.");
+ duckPercent = DEFAULT_DUCK_PERCENT;
+ }
+ float duckRatio = (float) ((duckPercent * 1.0f) / 100);
+ Log.d(STATE_TAG,
+ "Setting reduce gain on transient loss gain=" + duckRatio);
+ setFluorideAudioTrackGain(duckRatio);
+ } else {
+ // We either are in transient loss or we are in permanent loss,
+ // either ways we should stop streaming.
+ sendAvrcpPause();
+ stopAvrcpUpdates();
+ stopFluorideStreaming();
+
+ // If it is permanent focus loss then we should abandon focus here and wait
+ // for user to explicitly play again.
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
+ mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+ }
+ }
+ mCurrentAudioFocus = newAudioFocus;
+ break;
+
+ case DISCONNECT:
+ deferMessage(message);
+ transitionTo(mSrcFActF);
+ break;
+
+ default:
+ Log.e(TAG, "Don't know how to handle " + message.what);
+ }
+ return HANDLED;
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java b/src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java
new file mode 100644
index 0000000..7ee0276
--- /dev/null
+++ b/src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.a2dpsink.mbs;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothAvrcpController;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.MediaMetadata;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ResultReceiver;
+import android.service.media.MediaBrowserService;
+import android.util.Pair;
+import android.util.Log;
+
+import com.android.bluetooth.R;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class A2dpMediaBrowserService extends MediaBrowserService {
+ private static final String TAG = "A2dpMediaBrowserService";
+ private static final String MEDIA_ID_ROOT = "__ROOT__";
+ private static final String UNKNOWN_BT_AUDIO = "__UNKNOWN_BT_AUDIO__";
+ private static final float PLAYBACK_SPEED = 1.0f;
+
+ // Message sent when A2DP device is disconnected.
+ private static final int MSG_DEVICE_DISCONNECT = 0;
+ // Message snet when the AVRCP profile is disconnected = 1;
+ private static final int MSG_PROFILE_DISCONNECT = 1;
+ // Message sent when A2DP device is connected.
+ private static final int MSG_DEVICE_CONNECT = 2;
+ // Message sent when AVRCP profile is connected (note AVRCP profile may be connected before or
+ // after A2DP device is connected).
+ private static final int MSG_PROFILE_CONNECT = 3;
+ // Message sent when we recieve a TRACK update from AVRCP profile over a connected A2DP device.
+ private static final int MSG_TRACK = 4;
+ // Internal message sent to trigger a AVRCP action.
+ private static final int MSG_AVRCP_PASSTHRU = 5;
+
+ private MediaSession mSession;
+ private MediaMetadata mA2dpMetadata;
+
+ private BluetoothAdapter mAdapter;
+ private BluetoothAvrcpController mAvrcpProfile;
+ private BluetoothDevice mA2dpDevice = null;
+ private Handler mAvrcpCommandQueue;
+
+ private long mTransportControlFlags = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY
+ | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+
+ private static final class AvrcpCommandQueueHandler extends Handler {
+ WeakReference<A2dpMediaBrowserService> mInst;
+
+ AvrcpCommandQueueHandler(Looper looper, A2dpMediaBrowserService sink) {
+ super(looper);
+ mInst = new WeakReference<A2dpMediaBrowserService>(sink);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ A2dpMediaBrowserService inst = mInst.get();
+ if (inst == null) {
+ Log.e(TAG, "Parent class has died; aborting.");
+ return;
+ }
+
+ switch (msg.what) {
+ case MSG_DEVICE_CONNECT:
+ inst.msgDeviceConnect((BluetoothDevice) msg.obj);
+ break;
+ case MSG_PROFILE_CONNECT:
+ inst.msgProfileConnect((BluetoothProfile) msg.obj);
+ break;
+ case MSG_DEVICE_DISCONNECT:
+ inst.msgDeviceDisconnect((BluetoothDevice) msg.obj);
+ break;
+ case MSG_PROFILE_DISCONNECT:
+ inst.msgProfileDisconnect();
+ break;
+ case MSG_TRACK:
+ Pair<PlaybackState, MediaMetadata> pair =
+ (Pair<PlaybackState, MediaMetadata>) (msg.obj);
+ inst.msgTrack(pair.first, pair.second);
+ break;
+ case MSG_AVRCP_PASSTHRU:
+ inst.msgPassThru((int) msg.obj);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "onCreate");
+ super.onCreate();
+ mSession = new MediaSession(this, TAG);
+ setSessionToken(mSession.getSessionToken());
+ mSession.setCallback(mSessionCallbacks);
+ mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
+ MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mAvrcpCommandQueue = new AvrcpCommandQueueHandler(Looper.getMainLooper(), this);
+
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter.getProfileProxy(this, mServiceListener, BluetoothProfile.AVRCP_CONTROLLER);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothAvrcpController.ACTION_TRACK_EVENT);
+ registerReceiver(mBtReceiver, filter);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy");
+ mSession.release();
+ unregisterReceiver(mBtReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ return new BrowserRoot(MEDIA_ID_ROOT, null);
+ }
+
+ @Override
+ public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+ Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);
+ List<MediaItem> items = new ArrayList<MediaItem>();
+ result.sendResult(items);
+ }
+
+ BluetoothProfile.ServiceListener mServiceListener = new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ Log.d(TAG, "onServiceConnected");
+ if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
+ mAvrcpCommandQueue.obtainMessage(MSG_PROFILE_CONNECT, proxy).sendToTarget();
+ List<BluetoothDevice> devices = proxy.getConnectedDevices();
+ if (devices != null && devices.size() > 0) {
+ BluetoothDevice device = devices.get(0);
+ Log.d(TAG, "got AVRCP device " + device);
+ }
+ }
+ }
+
+ public void onServiceDisconnected(int profile) {
+ Log.d(TAG, "onServiceDisconnected " + profile);
+ if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
+ mAvrcpProfile = null;
+ mAvrcpCommandQueue.obtainMessage(MSG_PROFILE_DISCONNECT).sendToTarget();
+ }
+ }
+ };
+
+ // Media Session Stuff.
+ private MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() {
+ @Override
+ public void onPlay() {
+ Log.d(TAG, "onPlay");
+ mAvrcpCommandQueue.obtainMessage(
+ MSG_AVRCP_PASSTHRU, BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY).sendToTarget();
+ // TRACK_EVENT should be fired eventually and the UI should be hence updated.
+ }
+
+ @Override
+ public void onPause() {
+ Log.d(TAG, "onPause");
+ mAvrcpCommandQueue.obtainMessage(
+ MSG_AVRCP_PASSTHRU, BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE).sendToTarget();
+ // TRACK_EVENT should be fired eventually and the UI should be hence updated.
+ }
+
+ @Override
+ public void onSkipToNext() {
+ Log.d(TAG, "onSkipToNext");
+ mAvrcpCommandQueue.obtainMessage(
+ MSG_AVRCP_PASSTHRU, BluetoothAvrcpController.PASS_THRU_CMD_ID_FORWARD)
+ .sendToTarget();
+ // TRACK_EVENT should be fired eventually and the UI should be hence updated.
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ Log.d(TAG, "onSkipToPrevious");
+
+ mAvrcpCommandQueue.obtainMessage(
+ MSG_AVRCP_PASSTHRU, BluetoothAvrcpController.PASS_THRU_CMD_ID_BACKWARD)
+ .sendToTarget();
+ // TRACK_EVENT should be fired eventually and the UI should be hence updated.
+ }
+
+ // These are not yet supported.
+ @Override
+ public void onStop() {
+ Log.d(TAG, "onStop");
+ }
+
+ @Override
+ public void onCustomAction(String action, Bundle extras) {
+ Log.d(TAG, "onCustomAction action=" + action + " extras=" + extras);
+ }
+
+ @Override
+ public void onPlayFromSearch(String query, Bundle extras) {
+ Log.d(TAG, "playFromSearch not supported in AVRCP");
+ }
+
+ @Override
+ public void onCommand(String command, Bundle args, ResultReceiver cb) {
+ Log.d(TAG, "onCommand command=" + command + " args=" + args);
+ }
+
+ @Override
+ public void onSkipToQueueItem(long queueId) {
+ Log.d(TAG, "onSkipToQueueItem");
+ }
+
+ @Override
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ Log.d(TAG, "onPlayFromMediaId mediaId=" + mediaId + " extras=" + extras);
+ }
+
+ };
+
+ private BroadcastReceiver mBtReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive intent=" + intent);
+ String action = intent.getAction();
+ BluetoothDevice btDev =
+ (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+
+ if (BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ Log.d(TAG, "handleConnectionStateChange: newState="
+ + state + " btDev=" + btDev);
+
+ // Connected state will be handled when AVRCP BluetoothProfile gets connected.
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ mAvrcpCommandQueue.obtainMessage(MSG_DEVICE_CONNECT, btDev).sendToTarget();
+ } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ // Set the playback state to unconnected.
+ mAvrcpCommandQueue.obtainMessage(MSG_DEVICE_DISCONNECT, btDev).sendToTarget();
+ }
+ } else if (BluetoothAvrcpController.ACTION_TRACK_EVENT.equals(action)) {
+ PlaybackState pbb =
+ intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_PLAYBACK);
+ MediaMetadata mmd =
+ intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_METADATA);
+ mAvrcpCommandQueue.obtainMessage(
+ MSG_TRACK, new Pair<PlaybackState, MediaMetadata>(pbb, mmd)).sendToTarget();
+ }
+ }
+ };
+
+ private void msgDeviceConnect(BluetoothDevice device) {
+ Log.d(TAG, "msgDeviceConnect");
+ // We are connected to a new device via A2DP now.
+ mA2dpDevice = device;
+ refreshInitialPlayingState();
+ }
+
+ private void msgProfileConnect(BluetoothProfile profile) {
+ Log.d(TAG, "msgProfileConnect");
+ if (profile != null) {
+ mAvrcpProfile = (BluetoothAvrcpController) profile;
+ }
+ refreshInitialPlayingState();
+ }
+
+ // Refresh the UI if we have a connected device and AVRCP is initialized.
+ private void refreshInitialPlayingState() {
+ if (mAvrcpProfile == null || mA2dpDevice == null) {
+ Log.d(TAG, "AVRCP Profile " + mAvrcpProfile + " device " + mA2dpDevice);
+ return;
+ }
+
+ List<BluetoothDevice> devices = mAvrcpProfile.getConnectedDevices();
+ if (devices.size() == 0) {
+ Log.w(TAG, "No devices connected yet");
+ return;
+ }
+
+ if (mA2dpDevice != null && !mA2dpDevice.equals(devices.get(0))) {
+ Log.e(TAG, "A2dp device : " + mA2dpDevice + " avrcp device " + devices.get(0));
+ }
+ mA2dpDevice = devices.get(0);
+
+ PlaybackState playbackState = mAvrcpProfile.getPlaybackState(mA2dpDevice);
+ // Add actions required for playback and rebuild the object.
+ PlaybackState.Builder pbb = new PlaybackState.Builder(playbackState);
+ playbackState = pbb.setActions(mTransportControlFlags).build();
+
+ MediaMetadata mediaMetadata = mAvrcpProfile.getMetadata(mA2dpDevice);
+ Log.d(TAG, "Media metadata " + mediaMetadata + " playback state " + playbackState);
+ mSession.setMetadata(mAvrcpProfile.getMetadata(mA2dpDevice));
+ mSession.setPlaybackState(playbackState);
+ }
+
+ private void msgDeviceDisconnect(BluetoothDevice device) {
+ Log.d(TAG, "msgDeviceDisconnect");
+ if (mA2dpDevice == null) {
+ Log.w(TAG, "Already disconnected - nothing to do here.");
+ return;
+ } else if (!mA2dpDevice.equals(device)) {
+ Log.e(TAG, "Not the right device to disconnect current " +
+ mA2dpDevice + " dc " + device);
+ return;
+ }
+
+ // Unset the session.
+ PlaybackState.Builder pbb = new PlaybackState.Builder();
+ pbb = pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+ PLAYBACK_SPEED)
+ .setActions(mTransportControlFlags)
+ .setErrorMessage(getString(R.string.bluetooth_disconnected));
+ mSession.setPlaybackState(pbb.build());
+ }
+
+ private void msgProfileDisconnect() {
+ Log.d(TAG, "msgProfileDisconnect");
+ // The profile is disconnected - even if the device is still connected we cannot really have
+ // a functioning UI so reset the session.
+ mAvrcpProfile = null;
+
+ // Unset the session.
+ PlaybackState.Builder pbb = new PlaybackState.Builder();
+ pbb = pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+ PLAYBACK_SPEED)
+ .setActions(mTransportControlFlags)
+ .setErrorMessage(getString(R.string.bluetooth_disconnected));
+ mSession.setPlaybackState(pbb.build());
+ }
+
+ private void msgTrack(PlaybackState pb, MediaMetadata mmd) {
+ Log.d(TAG, "msgTrack: playback: " + pb + " mmd: " + mmd);
+ // Log the current track position/content.
+ MediaController controller = mSession.getController();
+ PlaybackState prevPS = controller.getPlaybackState();
+ MediaMetadata prevMM = controller.getMetadata();
+
+ if (prevPS != null) {
+ Log.d(TAG, "prevPS " + prevPS);
+ }
+
+ if (prevMM != null) {
+ String title = prevMM.getString(MediaMetadata.METADATA_KEY_TITLE);
+ long trackLen = prevMM.getLong(MediaMetadata.METADATA_KEY_DURATION);
+ Log.d(TAG, "prev MM title " + title + " track len " + trackLen);
+ }
+
+ if (mmd != null) {
+ Log.d(TAG, "msgTrack() mmd " + mmd.getDescription());
+ mSession.setMetadata(mmd);
+ }
+
+ if (pb != null) {
+ Log.d(TAG, "msgTrack() playbackstate " + pb);
+ PlaybackState.Builder pbb = new PlaybackState.Builder(pb);
+ pb = pbb.setActions(mTransportControlFlags).build();
+ mSession.setPlaybackState(pb);
+ }
+ }
+
+ private void msgPassThru(int cmd) {
+ Log.d(TAG, "msgPassThru " + cmd);
+ if (mA2dpDevice == null) {
+ // We should have already disconnected - ignore this message.
+ Log.e(TAG, "Already disconnected ignoring.");
+ return;
+ }
+
+ if (mAvrcpProfile == null) {
+ // We may be disconnected with the profile but there is not much we can do for now but
+ // to wait for the profile to come back up.
+ Log.e(TAG, "Profile disconnected; ignoring.");
+ return;
+ }
+
+ // Send the pass through.
+ mAvrcpProfile.sendPassThroughCmd(
+ mA2dpDevice, cmd, BluetoothAvrcpController.KEY_STATE_PRESSED);
+ mAvrcpProfile.sendPassThroughCmd(
+ mA2dpDevice, cmd, BluetoothAvrcpController.KEY_STATE_RELEASED);
+ }
+}
diff --git a/src/com/android/bluetooth/avrcp/AvrcpControllerConstants.java b/src/com/android/bluetooth/avrcp/AvrcpControllerConstants.java
index 43e9f43..359cb4c 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpControllerConstants.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpControllerConstants.java
@@ -29,6 +29,12 @@
public static final boolean DBG = true;
public static final boolean VDBG = true;
/*
+ * Whether to push broadcast updates about metadata.
+ */
+ public static final int START_METADATA_BROADCASTS = 0;
+ public static final int STOP_METADATA_BROADCASTS = 1;
+
+ /*
* Scopes of operation
*/
public static final int AVRCP_SCOPE_NOW_PLAYING = 0;
@@ -143,6 +149,10 @@
public static final int MESSAGE_PROCESS_RC_FEATURES = 1100;
public static final int MESSAGE_PROCESS_CONNECTION_CHANGE = 1200;
+ public static final int MESSAGE_STOP_METADATA_BROADCASTS = 201;
+ public static final int MESSAGE_START_METADATA_BROADCASTS = 202;
+
+
public static String dumpMessageString(int message)
{
String str = "UNKNOWN";
diff --git a/src/com/android/bluetooth/avrcp/AvrcpControllerService.java b/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
index 0c59a35..c42d4cf 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
@@ -35,7 +35,7 @@
import android.util.Log;
import android.media.AudioManager;
-import com.android.bluetooth.a2dp.A2dpSinkService;
+import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.Utils;
@@ -295,6 +295,16 @@
}
}
+ public void startAvrcpUpdates() {
+ mHandler.obtainMessage(
+ AvrcpControllerConstants.MESSAGE_START_METADATA_BROADCASTS).sendToTarget();
+ }
+
+ public void stopAvrcpUpdates() {
+ mHandler.obtainMessage(
+ AvrcpControllerConstants.MESSAGE_STOP_METADATA_BROADCASTS).sendToTarget();
+ }
+
public MediaMetadata getMetaData(BluetoothDevice device) {
Log.d(TAG, "getMetaData = ");
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
@@ -508,10 +518,10 @@
AvrcpUtils.displayMetaData(mMetaData));
sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
}
- private void broadcastPlayBackStateChanged(PlaybackState mState) {
+ private void broadcastPlayBackStateChanged(PlaybackState state) {
Intent intent = new Intent(BluetoothAvrcpController.ACTION_TRACK_EVENT);
- intent.putExtra(BluetoothAvrcpController.EXTRA_PLAYBACK, mState);
- if(DBG) Log.d(TAG," broadcastPlayBackStateChanged = " + mState.toString());
+ intent.putExtra(BluetoothAvrcpController.EXTRA_PLAYBACK, state);
+ if(DBG) Log.d(TAG," broadcastPlayBackStateChanged = " + state.toString());
sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
}
private void broadcastPlayerAppSettingChanged(BluetoothAvrcpPlayerSettings mPlAppSetting) {
@@ -523,6 +533,8 @@
}
/** Handles Avrcp messages. */
private final class AvrcpMessageHandler extends Handler {
+ private boolean mBroadcastMetadata = false;
+
private AvrcpMessageHandler(Looper looper) {
super(looper);
}
@@ -531,15 +543,35 @@
public void handleMessage(Message msg) {
Log.d(TAG," HandleMessage: "+ AvrcpControllerConstants.dumpMessageString(msg.what) +
" Remote Connected " + !mConnectedDevices.isEmpty());
+ A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
switch (msg.what) {
+ case AvrcpControllerConstants.MESSAGE_STOP_METADATA_BROADCASTS:
+ // Any messages hence forth about play pos/play status/song info will be ignored.
+ if(mRemoteMediaPlayers != null) {
+ // Mock the current state to *look like* it is paused. The remote play state is
+ // still cached in mRemoteMediaPlayers and will be restored when the
+ // startAvrcpUpdates is called again.
+ broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState
+ ((byte) AvrcpControllerConstants.PLAY_STATUS_PAUSED,
+ mRemoteMediaPlayers.getAddressedPlayer().mPlayTime));
+ }
+ mBroadcastMetadata = false;
+ break;
+ case AvrcpControllerConstants.MESSAGE_START_METADATA_BROADCASTS:
+ // Any messages hence forth about play pos/play status/song info will be sent.
+ if(mRemoteMediaPlayers != null) {
+ broadcastPlayBackStateChanged(getCurrentPlayBackState());
+ broadcastMetaDataChanged(
+ getCurrentMetaData(AvrcpControllerConstants.AVRCP_SCOPE_NOW_PLAYING, 0));
+ }
+ mBroadcastMetadata = true;
+ break;
case AvrcpControllerConstants.MESSAGE_SEND_PASS_THROUGH_CMD:
BluetoothDevice device = (BluetoothDevice)msg.obj;
sendPassThroughCommandNative(getByteAddress(device), msg.arg1, msg.arg2);
- A2dpSinkService a2dpSnkService = A2dpSinkService.getA2dpSinkService();
- if((a2dpSnkService != null)&&(!mConnectedDevices.isEmpty())) {
+ if((a2dpSinkService != null)&&(!mConnectedDevices.isEmpty())) {
Log.d(TAG," inform AVRCP Commands to A2DP Sink ");
- a2dpSnkService.informAvrcpPassThroughCmd(device,
- msg.arg1, msg.arg2);
+ a2dpSinkService.informAvrcpPassThroughCmd(device, msg.arg1, msg.arg2);
}
break;
case AvrcpControllerConstants.MESSAGE_SEND_GROUP_NAVIGATION_CMD:
@@ -640,6 +672,12 @@
case AvrcpControllerConstants.MESSAGE_PROCESS_TRACK_CHANGED:
if(mRemoteNowPlayingList != null) {
mRemoteNowPlayingList.updateCurrentTrack((TrackInfo)msg.obj);
+
+ if (!mBroadcastMetadata) {
+ Log.d(TAG, "Metadata is not broadcasted, ignoring.");
+ return;
+ }
+
broadcastMetaDataChanged(AvrcpUtils.getMediaMetaData
(mRemoteNowPlayingList.getCurrentTrack()));
}
@@ -647,6 +685,12 @@
case AvrcpControllerConstants.MESSAGE_PROCESS_PLAY_POS_CHANGED:
if(mRemoteMediaPlayers != null) {
mRemoteMediaPlayers.getAddressedPlayer().mPlayTime = msg.arg2;
+
+ if (!mBroadcastMetadata) {
+ Log.d(TAG, "Metadata is not broadcasted, ignoring.");
+ return;
+ }
+
broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState
(mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus,
mRemoteMediaPlayers.getAddressedPlayer().mPlayTime));
@@ -657,17 +701,22 @@
break;
case AvrcpControllerConstants.MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
if(mRemoteMediaPlayers != null) {
- mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus = (byte)msg.arg1;
- broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState
- (mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus,
- mRemoteMediaPlayers.getAddressedPlayer().mPlayTime));
- if(mRemoteMediaPlayers.getPlayStatus() == AvrcpControllerConstants.
- PLAY_STATUS_PLAYING) {
- A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
- if((a2dpSinkService != null)&&(!mConnectedDevices.isEmpty())) {
- Log.d(TAG," State = PLAYING, inform A2DP SINK");
- a2dpSinkService.informAvrcpStatePlaying(mConnectedDevices.get(0));
- }
+ int status = msg.arg1;
+ mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus = (byte) status;
+ if (status == AvrcpControllerConstants.PLAY_STATUS_PLAYING) {
+ a2dpSinkService.informTGStatePlaying(mConnectedDevices.get(0), true);
+ } else if (status == AvrcpControllerConstants.PLAY_STATUS_PAUSED ||
+ status == AvrcpControllerConstants.PLAY_STATUS_STOPPED) {
+ a2dpSinkService.informTGStatePlaying(mConnectedDevices.get(0), false);
+ }
+
+ if (mBroadcastMetadata) {
+ broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState
+ (mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus,
+ mRemoteMediaPlayers.getAddressedPlayer().mPlayTime));
+ } else {
+ Log.d(TAG, "Metadata is not broadcasted, ignoring.");
+ return;
}
}
break;
@@ -756,8 +805,7 @@
};
private void handlePassthroughRsp(int id, int keyState) {
- Log.d(TAG, "passthrough response received as: key: "
- + id + " state: " + keyState);
+ Log.d(TAG, "passthrough response received as: key: " + id + " state: " + keyState);
}
private void onConnectionStateChanged(boolean connected, byte[] address) {
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 45de8dd..f2a4326 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -31,6 +31,7 @@
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.OobData;
+import android.bluetooth.UidTraffic;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -48,17 +49,23 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Base64;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
import com.android.bluetooth.a2dp.A2dpService;
-import com.android.bluetooth.a2dp.A2dpSinkService;
+import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.hid.HidService;
import com.android.bluetooth.hfp.HeadsetService;
+import com.android.bluetooth.hfpclient.HeadsetClientService;
+import com.android.bluetooth.pbapclient.PbapClientService;
import com.android.bluetooth.sdp.SdpManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.R;
@@ -71,10 +78,10 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
-import java.util.Map.Entry;
import java.util.List;
public class AdapterService extends Service {
@@ -88,11 +95,13 @@
private static int sRefCount = 0;
private long mBluetoothStartTime = 0;
+ private final Object mEnergyInfoLock = new Object();
private int mStackReportedState;
- private int mTxTimeTotalMs;
- private int mRxTimeTotalMs;
- private int mIdleTimeTotalMs;
- private int mEnergyUsedTotalVoltAmpSecMicro;
+ private long mTxTimeTotalMs;
+ private long mRxTimeTotalMs;
+ private long mIdleTimeTotalMs;
+ private long mEnergyUsedTotalVoltAmpSecMicro;
+ private SparseArray<UidTraffic> mUidTraffic = new SparseArray<>();
private final ArrayList<ProfileService> mProfiles = new ArrayList<ProfileService>();
@@ -128,6 +137,8 @@
"DUAL"
};
+ private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30;
+
static {
classInitNative();
}
@@ -202,6 +213,12 @@
debugLog("AdapterService() - REFCOUNT: CREATED. INSTANCE_COUNT" + sRefCount);
}
}
+
+ // This is initialized at the beginning in order to prevent
+ // NullPointerException from happening if AdapterService
+ // functions are called before BLE is turned on due to
+ // |mRemoteDevices| being null.
+ mRemoteDevices = new RemoteDevices(this);
}
public void onProfileConnectionStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) {
@@ -233,6 +250,8 @@
A2dpService a2dpService = A2dpService.getA2dpService();
A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
HeadsetService headsetService = HeadsetService.getHeadsetService();
+ HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
+ PbapClientService pbapClientService = PbapClientService.getPbapClientService();
// Set profile priorities only for the profiles discovered on the remote device.
// This avoids needless auto-connect attempts to profiles non-existent on the remote device
@@ -243,7 +262,14 @@
hidService.setPriority(device,BluetoothProfile.PRIORITY_ON);
}
- // If we do not have a stored priority for A2DP then default to on.
+ // If we do not have a stored priority for HFP/A2DP (all roles) then default to on.
+ if ((headsetService != null) &&
+ ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) &&
+ (headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED))) {
+ headsetService.setPriority(device,BluetoothProfile.PRIORITY_ON);
+ }
+
if ((a2dpService != null) &&
(BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink) ||
BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AdvAudioDist)) &&
@@ -251,24 +277,44 @@
a2dpService.setPriority(device,BluetoothProfile.PRIORITY_ON);
}
- if ((headsetService != null) &&
- ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP) ||
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) &&
- (headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED))){
- headsetService.setPriority(device,BluetoothProfile.PRIORITY_ON);
+ if ((headsetClientService != null) &&
+ ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) &&
+ (headsetClientService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED))) {
+ headsetClientService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+
+ if ((a2dpSinkService != null) &&
+ (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource) &&
+ (a2dpSinkService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED))) {
+ a2dpSinkService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+
+ if ((pbapClientService != null) &&
+ (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PBAP_PSE) &&
+ (pbapClientService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED))) {
+ pbapClientService.setPriority(device, BluetoothProfile.PRIORITY_ON);
}
}
private void processProfileStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) {
- if (((profileId == BluetoothProfile.A2DP) ||(profileId == BluetoothProfile.HEADSET)) &&
+ // Profiles relevant to phones.
+ if (((profileId == BluetoothProfile.A2DP) || (profileId == BluetoothProfile.HEADSET)) &&
(newState == BluetoothProfile.STATE_CONNECTED)){
debugLog( "Profile connected. Schedule missing profile connection if any");
connectOtherProfile(device, PROFILE_CONN_CONNECTED);
setProfileAutoConnectionPriority(device, profileId);
}
- if ((profileId == BluetoothProfile.A2DP_SINK) && (newState == BluetoothProfile.STATE_CONNECTED)) {
+
+ // Profiles relevant to Car Kitts.
+ if (((profileId == BluetoothProfile.A2DP_SINK) ||
+ (profileId == BluetoothProfile.HEADSET_CLIENT)) &&
+ (newState == BluetoothProfile.STATE_CONNECTED)) {
+ debugLog( "Profile connected. Schedule missing profile connection if any");
+ connectOtherProfile(device, PROFILE_CONN_CONNECTED);
setProfileAutoConnectionPriority(device, profileId);
}
+
IBluetooth.Stub binder = mBinder;
if (binder != null) {
try {
@@ -281,7 +327,9 @@
public void addProfile(ProfileService profile) {
synchronized (mProfiles) {
- mProfiles.add(profile);
+ if (!mProfiles.contains(profile)) {
+ mProfiles.add(profile);
+ }
}
}
@@ -312,7 +360,7 @@
doUpdate=true;
}
}
- debugLog("onProfileServiceStateChange() serviceName=" + serviceName
+ debugLog("processProfileServiceStateChanged() serviceName=" + serviceName
+ ", state=" + state +", doUpdate=" + doUpdate);
if (!doUpdate) {
@@ -370,6 +418,8 @@
mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STOPPED));
} else if (isTurningOn) {
+ updateInteropDatabase();
+
//Process start pending
//Check if all services are started if so, update state
synchronized (mProfileServicesState) {
@@ -395,6 +445,59 @@
}
}
+ private void updateInteropDatabase() {
+ interopDatabaseClearNative();
+
+ String interop_string = Settings.Global.getString(getContentResolver(),
+ Settings.Global.BLUETOOTH_INTEROPERABILITY_LIST);
+ if (interop_string == null) return;
+ Log.d(TAG, "updateInteropDatabase: [" + interop_string + "]");
+
+ String[] entries = interop_string.split(";");
+ for (String entry : entries) {
+ String[] tokens = entry.split(",");
+ if (tokens.length != 2) continue;
+
+ // Get feature
+ int feature = 0;
+ try {
+ feature = Integer.parseInt(tokens[1]);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "updateInteropDatabase: Invalid feature '" + tokens[1] + "'");
+ continue;
+ }
+
+ // Get address bytes and length
+ int length = (tokens[0].length() + 1) / 3;
+ if (length < 1 || length > 6) {
+ Log.e(TAG, "updateInteropDatabase: Malformed address string '" + tokens[0] + "'");
+ continue;
+ }
+
+ byte[] addr = new byte[6];
+ int offset = 0;
+ for (int i = 0; i < tokens[0].length(); ) {
+ if (tokens[0].charAt(i) == ':') {
+ i += 1;
+ } else {
+ try {
+ addr[offset++] = (byte) Integer.parseInt(tokens[0].substring(i, i + 2), 16);
+ } catch (NumberFormatException e) {
+ offset = 0;
+ break;
+ }
+ i += 2;
+ }
+ }
+
+ // Check if address was parsed ok, otherwise, move on...
+ if (offset == 0) continue;
+
+ // Add entry
+ interopDatabaseAddNative(feature, addr, length);
+ }
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -412,7 +515,7 @@
mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
- BatteryStats.SERVICE_NAME));
+ BatteryStats.SERVICE_NAME));
mSdpManager = SdpManager.init(this);
registerReceiver(mAlarmBroadcastReceiver, new IntentFilter(ACTION_ALARM_WAKEUP));
@@ -449,7 +552,17 @@
for (int i=0; i < supportedProfileServices.length;i++) {
mProfileServicesState.put(supportedProfileServices[i].getName(),BluetoothAdapter.STATE_OFF);
}
- mRemoteDevices = new RemoteDevices(this);
+
+ // Reset |mRemoteDevices| whenever BLE is turned off then on
+ // This is to replace the fact that |mRemoteDevices| was
+ // reinitialized in previous code.
+ //
+ // TODO(apanicke): The reason is unclear but
+ // I believe it is to clear the variable every time BLE was
+ // turned off then on. The same effect can be achieved by
+ // calling cleanup but this may not be necessary at all
+ // We should figure out why this is needed later
+ mRemoteDevices.cleanup();
mAdapterProperties.init(mRemoteDevices);
debugLog("BleOnProcessStart() - Make Bond State Machine");
@@ -629,8 +742,7 @@
for(int i=0; i<mUuids.length; i++) {
mUuids[i] = msg.getData().getParcelable("uuids" + i);
}
- processInitProfilePriorities((BluetoothDevice) msg.obj,
- mUuids);
+ processInitProfilePriorities((BluetoothDevice) msg.obj, mUuids);
}
break;
case MESSAGE_CONNECT_OTHER_PROFILES: {
@@ -1300,19 +1412,20 @@
return service.isActivityAndEnergyReportingSupported();
}
- public void getActivityEnergyInfoFromController() {
- AdapterService service = getService();
- if (service == null) return;
- service.getActivityEnergyInfoFromController();
- }
-
public BluetoothActivityEnergyInfo reportActivityInfo() {
AdapterService service = getService();
if (service == null) return null;
return service.reportActivityInfo();
}
- public void onLeServiceUp(){
+ public void requestActivityInfo(ResultReceiver result) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY,
+ reportActivityInfo());
+ result.send(0, bundle);
+ }
+
+ public void onLeServiceUp(){
AdapterService service = getService();
if (service == null) return;
service.onLeServiceUp();
@@ -1515,9 +1628,14 @@
}
if (isQuietModeEnabled() == false) {
debugLog( "autoConnect() - Initiate auto connection on BT on...");
+ // Phone profiles.
autoConnectHeadset();
autoConnectA2dp();
+
+ // Car Kitt profiles.
+ autoConnectHeadsetClient();
autoConnectA2dpSink();
+ autoConnectPbapClient();
}
else {
debugLog( "autoConnect() - BT is in quiet mode. Not initiating auto connections");
@@ -1562,17 +1680,53 @@
}
}
}
- private void autoConnectA2dpSink(){
+
+ private void autoConnectHeadsetClient() {
+ HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
+ BluetoothDevice bondedDevices[] = getBondedDevices();
+ if ((bondedDevices == null) || (headsetClientService == null)) {
+ return;
+ }
+
+ for (BluetoothDevice device : bondedDevices) {
+ if (headsetClientService.getPriority(device) == BluetoothProfile.PRIORITY_AUTO_CONNECT){
+ debugLog("autoConnectHeadsetClient() - Connecting Headset Client with " +
+ device.toString());
+ headsetClientService.connect(device);
+ }
+ }
+ }
+
+ private void autoConnectA2dpSink() {
A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
BluetoothDevice bondedDevices[] = getBondedDevices();
- if ((bondedDevices == null) ||(a2dpSinkService == null)) {
+ if ((bondedDevices == null) || (a2dpSinkService == null)) {
+ return;
+ }
+
+ for (BluetoothDevice device : bondedDevices) {
+ if (a2dpSinkService.getPriority(device) == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+ debugLog("autoConnectA2dpSink() - Connecting A2DP Sink with " + device.toString());
+ a2dpSinkService.connect(device);
+ }
+ }
+ }
+
+ private void autoConnectPbapClient(){
+ PbapClientService pbapClientService = PbapClientService.getPbapClientService();
+ BluetoothDevice bondedDevices[] = getBondedDevices();
+ if ((bondedDevices == null) || (pbapClientService == null)) {
return;
}
for (BluetoothDevice device : bondedDevices) {
- debugLog("autoConnectA2dp() - Connecting A2DP Sink with " + device.toString());
- a2dpSinkService.connect(device);
+ if (pbapClientService.getPriority(device) == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+ debugLog("autoConnectPbapClient() - Connecting PBAP Client with " +
+ device.toString());
+ pbapClientService.connect(device);
+ }
}
- }
+ }
+
public void connectOtherProfile(BluetoothDevice device, int firstProfileStatus){
if ((mHandler.hasMessages(MESSAGE_CONNECT_OTHER_PROFILES) == false) &&
@@ -1617,7 +1771,7 @@
}
private void adjustOtherHeadsetPriorities(HeadsetService hsService,
- List<BluetoothDevice> connectedDeviceList) {
+ List<BluetoothDevice> connectedDeviceList) {
for (BluetoothDevice device : getBondedDevices()) {
if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT &&
!connectedDeviceList.contains(device)) {
@@ -1626,8 +1780,8 @@
}
}
- private void adjustOtherSinkPriorities(A2dpService a2dpService,
- BluetoothDevice connectedDevice) {
+ private void adjustOtherSinkPriorities(A2dpService a2dpService,
+ BluetoothDevice connectedDevice) {
for (BluetoothDevice device : getBondedDevices()) {
if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT &&
!device.equals(connectedDevice)) {
@@ -1636,15 +1790,88 @@
}
}
- void setProfileAutoConnectionPriority (BluetoothDevice device, int profileId){
- if (profileId == BluetoothProfile.HEADSET) {
- HeadsetService hsService = HeadsetService.getHeadsetService();
- List<BluetoothDevice> deviceList = hsService.getConnectedDevices();
- if ((hsService != null) &&
- (BluetoothProfile.PRIORITY_AUTO_CONNECT != hsService.getPriority(device))){
- adjustOtherHeadsetPriorities(hsService, deviceList);
- hsService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT);
- }
+ private void adjustOtherHeadsetClientPriorities(HeadsetClientService hsService,
+ BluetoothDevice connectedDevice) {
+ for (BluetoothDevice device : getBondedDevices()) {
+ if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT &&
+ !device.equals(connectedDevice)) {
+ hsService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ }
+ }
+
+ private void adjustOtherA2dpSinkPriorities(A2dpSinkService a2dpService,
+ BluetoothDevice connectedDevice) {
+ for (BluetoothDevice device : getBondedDevices()) {
+ if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT &&
+ !device.equals(connectedDevice)) {
+ a2dpService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ }
+ }
+
+ private void adjustOtherPbapClientPriorities(PbapClientService pbapService,
+ BluetoothDevice connectedDevice) {
+ for (BluetoothDevice device : getBondedDevices()) {
+ if (pbapService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT &&
+ !device.equals(connectedDevice)) {
+ pbapService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ }
+ }
+
+ void setProfileAutoConnectionPriority (BluetoothDevice device, int profileId){
+ switch (profileId) {
+ case BluetoothProfile.HEADSET:
+ HeadsetService hsService = HeadsetService.getHeadsetService();
+ List<BluetoothDevice> deviceList = hsService.getConnectedDevices();
+ if ((hsService != null) &&
+ (BluetoothProfile.PRIORITY_AUTO_CONNECT != hsService.getPriority(device))) {
+ adjustOtherHeadsetPriorities(hsService, deviceList);
+ hsService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT);
+ }
+ break;
+
+ case BluetoothProfile.A2DP:
+ A2dpService a2dpService = A2dpService.getA2dpService();
+ if ((a2dpService != null) && (BluetoothProfile.PRIORITY_AUTO_CONNECT !=
+ a2dpService.getPriority(device))) {
+ adjustOtherSinkPriorities(a2dpService, device);
+ a2dpService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT);
+ }
+ break;
+
+ case BluetoothProfile.A2DP_SINK:
+ A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
+ if ((a2dpSinkService != null) && (BluetoothProfile.PRIORITY_AUTO_CONNECT !=
+ a2dpSinkService.getPriority(device))) {
+ adjustOtherA2dpSinkPriorities(a2dpSinkService, device);
+ a2dpSinkService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT);
+ }
+ break;
+
+ case BluetoothProfile.HEADSET_CLIENT:
+ HeadsetClientService headsetClientService =
+ HeadsetClientService.getHeadsetClientService();
+ if ((headsetClientService != null) && (BluetoothProfile.PRIORITY_AUTO_CONNECT !=
+ headsetClientService.getPriority(device))) {
+ adjustOtherHeadsetClientPriorities(headsetClientService, device);
+ headsetClientService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT);
+ }
+ break;
+
+ case BluetoothProfile.PBAP_CLIENT:
+ PbapClientService pbapClientService = PbapClientService.getPbapClientService();
+ if ((pbapClientService != null) && (BluetoothProfile.PRIORITY_AUTO_CONNECT !=
+ pbapClientService.getPriority(device))) {
+ adjustOtherPbapClientPriorities(pbapClientService, device);
+ pbapClientService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT);
+ }
+ break;
+
+ default:
+ Log.w(TAG, "Attempting to set Auto Connect priority on invalid profile");
+ break;
}
}
@@ -1666,7 +1893,7 @@
return true;
}
- int getBondState(BluetoothDevice device) {
+ int getBondState(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
if (deviceProp == null) {
@@ -1683,6 +1910,7 @@
String getRemoteName(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (mRemoteDevices == null) return null;
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
if (deviceProp == null) return null;
return deviceProp.getName();
@@ -1938,26 +2166,62 @@
return mAdapterProperties.isActivityAndEnergyReportingSupported();
}
- private void getActivityEnergyInfoFromController() {
- enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH permission");
- if (isActivityAndEnergyReportingSupported()) {
- readEnergyInfo();
- }
- }
-
private BluetoothActivityEnergyInfo reportActivityInfo() {
enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH permission");
- BluetoothActivityEnergyInfo info =
- new BluetoothActivityEnergyInfo(SystemClock.elapsedRealtime(), mStackReportedState,
- mTxTimeTotalMs, mRxTimeTotalMs, mIdleTimeTotalMs, mEnergyUsedTotalVoltAmpSecMicro);
- // Read on clear values; a record of data is created with
- // timstamp and new samples are collected until read again
- mStackReportedState = 0;
- mTxTimeTotalMs = 0;
- mRxTimeTotalMs = 0;
- mIdleTimeTotalMs = 0;
- mEnergyUsedTotalVoltAmpSecMicro = 0;
- return info;
+ if (mAdapterProperties.getState() != BluetoothAdapter.STATE_ON ||
+ !mAdapterProperties.isActivityAndEnergyReportingSupported()) {
+ return null;
+ }
+
+ // Pull the data. The callback will notify mEnergyInfoLock.
+ readEnergyInfo();
+
+ synchronized (mEnergyInfoLock) {
+ try {
+ mEnergyInfoLock.wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ // Just continue, the energy data may be stale but we won't miss anything next time
+ // we query.
+ }
+
+ final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(
+ SystemClock.elapsedRealtime(),
+ mStackReportedState,
+ mTxTimeTotalMs, mRxTimeTotalMs, mIdleTimeTotalMs,
+ mEnergyUsedTotalVoltAmpSecMicro);
+
+ // Count the number of entries that have byte counts > 0
+ int arrayLen = 0;
+ for (int i = 0; i < mUidTraffic.size(); i++) {
+ final UidTraffic traffic = mUidTraffic.valueAt(i);
+ if (traffic.getTxBytes() != 0 || traffic.getRxBytes() != 0) {
+ arrayLen++;
+ }
+ }
+
+ // Copy the traffic objects whose byte counts are > 0 and reset the originals.
+ final UidTraffic[] result = arrayLen > 0 ? new UidTraffic[arrayLen] : null;
+ int putIdx = 0;
+ for (int i = 0; i < mUidTraffic.size(); i++) {
+ final UidTraffic traffic = mUidTraffic.valueAt(i);
+ if (traffic.getTxBytes() != 0 || traffic.getRxBytes() != 0) {
+ result[putIdx++] = traffic.clone();
+ traffic.setRxBytes(0);
+ traffic.setTxBytes(0);
+ }
+ }
+
+ info.setUidTraffic(result);
+
+ // Read on clear values; a record of data is created with
+ // timstamp and new samples are collected until read again
+ mStackReportedState = 0;
+ mTxTimeTotalMs = 0;
+ mRxTimeTotalMs = 0;
+ mIdleTimeTotalMs = 0;
+ mEnergyUsedTotalVoltAmpSecMicro = 0;
+ return info;
+ }
}
public int getTotalNumOfTrackableAdvertisements() {
@@ -2057,29 +2321,70 @@
return true;
}
- private void energyInfoCallback (int status, int ctrl_state,
- long tx_time, long rx_time, long idle_time, long energy_used)
- throws RemoteException {
+ private void energyInfoCallback(int status, int ctrl_state, long tx_time, long rx_time,
+ long idle_time, long energy_used, UidTraffic[] data)
+ throws RemoteException {
if (ctrl_state >= BluetoothActivityEnergyInfo.BT_STACK_STATE_INVALID &&
ctrl_state <= BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_IDLE) {
- mStackReportedState = ctrl_state;
- mTxTimeTotalMs += tx_time;
- mRxTimeTotalMs += rx_time;
- mIdleTimeTotalMs += idle_time;
// Energy is product of mA, V and ms. If the chipset doesn't
// report it, we have to compute it from time
if (energy_used == 0) {
- energy_used = (long)((mTxTimeTotalMs * getTxCurrentMa()
- + mRxTimeTotalMs * getRxCurrentMa()
- + mIdleTimeTotalMs * getIdleCurrentMa()) * getOperatingVolt());
+ try {
+ final long txMah = Math.multiplyExact(tx_time, getTxCurrentMa());
+ final long rxMah = Math.multiplyExact(rx_time, getRxCurrentMa());
+ final long idleMah = Math.multiplyExact(idle_time, getIdleCurrentMa());
+ energy_used = (long) (Math.addExact(Math.addExact(txMah, rxMah), idleMah)
+ * getOperatingVolt());
+ } catch (ArithmeticException e) {
+ Slog.wtf(TAG, "overflow in bluetooth energy callback", e);
+ // Energy is already 0 if the exception was thrown.
+ }
}
- mEnergyUsedTotalVoltAmpSecMicro += energy_used;
+
+ synchronized (mEnergyInfoLock) {
+ mStackReportedState = ctrl_state;
+ long totalTxTimeMs;
+ long totalRxTimeMs;
+ long totalIdleTimeMs;
+ long totalEnergy;
+ try {
+ totalTxTimeMs = Math.addExact(mTxTimeTotalMs, tx_time);
+ totalRxTimeMs = Math.addExact(mRxTimeTotalMs, rx_time);
+ totalIdleTimeMs = Math.addExact(mIdleTimeTotalMs, idle_time);
+ totalEnergy = Math.addExact(mEnergyUsedTotalVoltAmpSecMicro, energy_used);
+ } catch (ArithmeticException e) {
+ // This could be because we accumulated a lot of time, or we got a very strange
+ // value from the controller (more likely). Discard this data.
+ Slog.wtf(TAG, "overflow in bluetooth energy callback", e);
+ totalTxTimeMs = mTxTimeTotalMs;
+ totalRxTimeMs = mRxTimeTotalMs;
+ totalIdleTimeMs = mIdleTimeTotalMs;
+ totalEnergy = mEnergyUsedTotalVoltAmpSecMicro;
+ }
+
+ mTxTimeTotalMs = totalTxTimeMs;
+ mRxTimeTotalMs = totalRxTimeMs;
+ mIdleTimeTotalMs = totalIdleTimeMs;
+ mEnergyUsedTotalVoltAmpSecMicro = totalEnergy;
+
+ for (UidTraffic traffic : data) {
+ UidTraffic existingTraffic = mUidTraffic.get(traffic.getUid());
+ if (existingTraffic == null) {
+ mUidTraffic.put(traffic.getUid(), traffic);
+ } else {
+ existingTraffic.addRxBytes(traffic.getRxBytes());
+ existingTraffic.addTxBytes(traffic.getTxBytes());
+ }
+ }
+ mEnergyInfoLock.notifyAll();
+ }
}
debugLog("energyInfoCallback() status = " + status +
- "tx_time = " + tx_time + "rx_time = " + rx_time +
- "idle_time = " + idle_time + "energy_used = " + energy_used +
- "ctrl_state = " + ctrl_state);
+ "tx_time = " + tx_time + "rx_time = " + rx_time +
+ "idle_time = " + idle_time + "energy_used = " + energy_used +
+ "ctrl_state = " + ctrl_state +
+ "traffic = " + Arrays.toString(data));
}
private int getIdleCurrentMa() {
@@ -2252,6 +2557,9 @@
private native void alarmFiredNative();
private native void dumpNative(FileDescriptor fd, String[] arguments);
+ private native void interopDatabaseClearNative();
+ private native void interopDatabaseAddNative(int feature, byte[] address, int length);
+
protected void finalize() {
cleanup();
if (TRACE_REF) {
diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java
index 1046c30..afb9d8d 100644
--- a/src/com/android/bluetooth/btservice/Config.java
+++ b/src/com/android/bluetooth/btservice/Config.java
@@ -27,7 +27,7 @@
import com.android.bluetooth.R;
import com.android.bluetooth.a2dp.A2dpService;
-import com.android.bluetooth.a2dp.A2dpSinkService;
+import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.avrcp.AvrcpControllerService;
import com.android.bluetooth.hdp.HealthService;
import com.android.bluetooth.hfp.HeadsetService;
@@ -37,6 +37,7 @@
import com.android.bluetooth.gatt.GattService;
import com.android.bluetooth.map.BluetoothMapService;
import com.android.bluetooth.sap.SapService;
+import com.android.bluetooth.pbapclient.PbapClientService;
public class Config {
private static final String TAG = "AdapterServiceConfig";
@@ -57,7 +58,8 @@
BluetoothMapService.class,
HeadsetClientService.class,
AvrcpControllerService.class,
- SapService.class
+ SapService.class,
+ PbapClientService.class
};
/**
* Resource flag to indicate whether profile is supported or not.
@@ -74,6 +76,7 @@
R.bool.profile_supported_hfpclient,
R.bool.profile_supported_avrcp_controller,
R.bool.profile_supported_sap,
+ R.bool.profile_supported_pbapclient
};
private static Class[] SUPPORTED_PROFILES = new Class[0];
@@ -129,6 +132,8 @@
profileIndex = BluetoothProfile.AVRCP_CONTROLLER;
} else if (profile == SapService.class) {
profileIndex = BluetoothProfile.SAP;
+ } else if (profile == PbapClientService.class) {
+ profileIndex = BluetoothProfile.PBAP_CLIENT;
}
if (profileIndex == -1) {
diff --git a/src/com/android/bluetooth/btservice/ProfileService.java b/src/com/android/bluetooth/btservice/ProfileService.java
index a67bc7b..d41d864 100644
--- a/src/com/android/bluetooth/btservice/ProfileService.java
+++ b/src/com/android/bluetooth/btservice/ProfileService.java
@@ -53,8 +53,6 @@
protected boolean mStartError=false;
private boolean mCleaningUp = false;
- private AdapterService mAdapterService;
-
protected String getName() {
return getClass().getSimpleName();
}
@@ -107,16 +105,17 @@
super.onCreate();
mAdapter = BluetoothAdapter.getDefaultAdapter();
mBinder = initBinder();
- mAdapterService = AdapterService.getAdapterService();
- if (mAdapterService != null) {
- mAdapterService.addProfile(this);
- } else {
- Log.w(TAG, "onCreate, null mAdapterService");
- }
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (DBG) log("onStartCommand()");
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.addProfile(this);
+ } else {
+ Log.w(TAG, "Could not add this profile because AdapterService is null.");
+ }
+
if (mStartError || mAdapter == null) {
Log.w(mName, "Stopping profile service: device does not have BT");
doStop(intent);
@@ -176,7 +175,8 @@
@Override
public void onDestroy() {
if (DBG) log("Destroying service.");
- if (mAdapterService != null) mAdapterService.removeProfile(this);
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) adapterService.removeProfile(this);
if (mCleaningUp) {
if (DBG) log("Cleanup already started... Skipping cleanup()...");
@@ -220,15 +220,17 @@
protected void notifyProfileServiceStateChanged(int state) {
//Notify adapter service
- if (mAdapterService != null) {
- mAdapterService.onProfileServiceStateChanged(getClass().getName(), state);
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.onProfileServiceStateChanged(getClass().getName(), state);
}
}
public void notifyProfileConnectionStateChanged(BluetoothDevice device,
int profileId, int newState, int prevState) {
- if (mAdapterService != null) {
- mAdapterService.onProfileConnectionStateChanged(device, profileId, newState, prevState);
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.onProfileConnectionStateChanged(device, profileId, newState, prevState);
}
}
diff --git a/src/com/android/bluetooth/gatt/ScanManager.java b/src/com/android/bluetooth/gatt/ScanManager.java
index a885a72..bb45bf7 100644
--- a/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/src/com/android/bluetooth/gatt/ScanManager.java
@@ -260,12 +260,12 @@
Utils.enforceAdminPermission(mService);
if (client == null) return;
- // The ScanClient passed in just holds the clientIf. We retrieve the real client,
- // which may have workSource set.
- client = mScanNative.getClient(client.clientIf);
- if (client == null) return;
-
if (mRegularScanClients.contains(client)) {
+ // The ScanClient passed in just holds the clientIf. We retrieve the real client,
+ // which may have workSource set.
+ client = mScanNative.getRegularScanClient(client.clientIf);
+ if (client == null) return;
+
mScanNative.stopRegularScan(client);
if (mScanNative.numRegularScanClients() == 0) {
@@ -711,8 +711,8 @@
client.settings = builder.build();
}
- // Find the scan client information
- ScanClient getClient(int clientIf) {
+ // Find the regular scan client information.
+ ScanClient getRegularScanClient(int clientIf) {
for (ScanClient client : mRegularScanClients) {
if (client.clientIf == clientIf) return client;
}
diff --git a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
index 1f1750c..0595f37 100644
--- a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
+++ b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
@@ -121,9 +121,9 @@
}
private void startListenForPhoneState() {
- if (!mListening && mSlcReady) {
+ if (!mListening && mSlcReady && mTelephonyManager != null) {
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
if (SubscriptionManager.isValidSubscriptionId(subId)) {
mPhoneStateListener = getPhoneStateListener(subId);
@@ -141,7 +141,7 @@
}
private void stopListenForPhoneState() {
- if (mListening) {
+ if (mListening && mTelephonyManager != null) {
if (mTelephonyManager == null) {
Log.e(TAG, "mTelephonyManager is null, "
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 70b1e5d..0b19f6b 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -32,11 +32,13 @@
import android.util.Log;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
import com.android.bluetooth.Utils;
import java.util.ArrayList;
import java.util.List;
+
/**
* Provides Bluetooth Headset Client (HF Role) profile, as a service in the
* Bluetooth application.
@@ -50,6 +52,8 @@
private HeadsetClientStateMachine mStateMachine;
private static HeadsetClientService sHeadsetClientService;
+ public static String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
+
@Override
protected String getName() {
return TAG;
@@ -71,6 +75,12 @@
Log.w(TAG, "Unable to register broadcat receiver", e);
}
setHeadsetClientService(this);
+
+ // Start the HfpClientConnectionService to create connection with telecom when HFP
+ // connection is available.
+ Intent startIntent = new Intent(this, HfpClientConnectionService.class);
+ startService(startIntent);
+
return true;
}
@@ -81,9 +91,15 @@
} catch (Exception e) {
Log.w(TAG, "Unable to unregister broadcast receiver", e);
}
+
if (mStateMachine != null) {
mStateMachine.doQuit();
}
+
+ // Stop the HfpClientConnectionService.
+ Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
+ stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true);
+ startService(stopIntent);
return true;
}
@@ -101,9 +117,16 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+ // We handle the volume changes for Voice calls here since HFP audio volume control does
+ // not go through audio manager (audio mixer). We check if the voice call volume has
+ // changed and subsequently change the SCO volume see
+ // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
+ // {@link HeadsetClientStateMachine} for details.
if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+ Log.d(TAG, "Volume changed for stream: " +
+ intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
- if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
+ if (streamType == AudioManager.STREAM_VOICE_CALL) {
int streamValue = intent
.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
int streamPrevValue = intent.getIntExtra(
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index 6ad0255..4d387f4 100755
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -48,13 +48,15 @@
import android.os.ParcelUuid;
import android.util.Log;
import android.util.Pair;
+import android.telecom.TelecomManager;
-import com.android.internal.util.IState;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
import java.util.ArrayList;
import java.util.Arrays;
@@ -102,6 +104,9 @@
// special action to handle terminating specific call from multiparty call
static final int TERMINATE_SPECIFIC_CALL = 53;
+ static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec.
+ static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec.
+
private static final int STACK_EVENT = 100;
private final Disconnected mDisconnected;
@@ -132,6 +137,9 @@
private int mVoiceRecognitionActive;
private int mInBandRingtone;
+ private int mMaxAmVcVol;
+ private int mMinAmVcVol;
+
// queue of send actions (pair action, action_data)
private Queue<Pair<Integer, Object>> mQueuedActions;
@@ -146,6 +154,7 @@
private boolean mAudioWbs;
private final BluetoothAdapter mAdapter;
private boolean mNativeAvailable;
+ private TelecomManager mTelecomManager;
// currently connected device
private BluetoothDevice mCurrentDevice = null;
@@ -304,20 +313,12 @@
if (state == c.getState()) {
return;
}
- //abandon focus here
- if (state == BluetoothHeadsetClientCall.CALL_STATE_TERMINATED) {
- if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- Log.d(TAG, "abandonAudioFocus ");
- // abandon audio focus after the mode has been set back to normal
- mAudioManager.abandonAudioFocusForCall();
- }
- }
c.setState(state);
sendCallChangedIntent(c);
}
private void sendCallChangedIntent(BluetoothHeadsetClientCall c) {
+ Log.d(TAG, "sendCallChangedIntent " + c);
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
@@ -1212,6 +1213,8 @@
mAudioRouteAllowed = context.getResources().getBoolean(
R.bool.headset_client_initial_audio_route_allowed);
+ mTelecomManager = (TelecomManager) context.getSystemService(context.TELECOM_SERVICE);
+
mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
mIndicatorNetworkSignal = 0;
@@ -1222,6 +1225,9 @@
mIndicatorCallSetup = -1;
mIndicatorCallHeld = -1;
+ mMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
+ mMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
+
mOperatorName = null;
mSubscriberInfo = null;
@@ -1269,6 +1275,25 @@
}
}
+ private int hfToAmVol(int hfVol) {
+ int amRange = mMaxAmVcVol - mMinAmVcVol;
+ int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
+ int amOffset =
+ (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange;
+ int amVol = mMinAmVcVol + amOffset;
+ Log.d(TAG, "HF -> AM " + hfVol + " " + amVol);
+ return amVol;
+ }
+
+ private int amToHfVol(int amVol) {
+ int amRange = mMaxAmVcVol - mMinAmVcVol;
+ int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
+ int hfOffset = (hfRange * (amVol - mMinAmVcVol)) / amRange;
+ int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset;
+ Log.d(TAG, "AM -> HF " + amVol + " " + hfVol);
+ return hfVol;
+ }
+
private class Disconnected extends State {
@Override
public void enter() {
@@ -1488,9 +1513,11 @@
}
transitionTo(mConnected);
- // TODO get max stream volume and scale 0-15
- sendMessage(obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME,
- mAudioManager.getStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO), 0));
+ int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+ sendMessage(
+ obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0));
+ // Mic is either in ON state (full volume) or OFF state. There is no way in
+ // Android to change the MIC volume.
sendMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME,
mAudioManager.isMicrophoneMute() ? 0 : 15, 0));
@@ -1610,6 +1637,7 @@
}
}
break;
+ // Called only for Mute/Un-mute - Mic volume change is not allowed.
case SET_MIC_VOLUME:
if (mVgmFromStack) {
mVgmFromStack = false;
@@ -1620,13 +1648,16 @@
}
break;
case SET_SPEAKER_VOLUME:
- Log.d(TAG,"Volume is set to " + message.arg1);
- mAudioManager.setParameters("hfp_volume=" + message.arg1);
+ // This message should always contain the volume in AudioManager max normalized.
+ int amVol = message.arg1;
+ int hfVol = amToHfVol(amVol);
+ Log.d(TAG,"HF volume is set to " + hfVol);
+ mAudioManager.setParameters("hfp_volume=" + hfVol);
if (mVgsFromStack) {
mVgsFromStack = false;
break;
}
- if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_SPK, message.arg1)) {
+ if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) {
addQueuedAction(SET_SPEAKER_VOLUME);
}
break;
@@ -1836,11 +1867,17 @@
break;
case EVENT_TYPE_VOLUME_CHANGED:
if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) {
- mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO,
- event.valueInt2, AudioManager.FLAG_SHOW_UI);
+ Log.d(TAG, "AM volume set to " +
+ hfToAmVol(event.valueInt2));
+ mAudioManager.setStreamVolume(
+ AudioManager.STREAM_VOICE_CALL,
+ hfToAmVol(event.valueInt2),
+ AudioManager.FLAG_SHOW_UI);
mVgsFromStack = true;
- } else if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
+ } else if (event.valueInt ==
+ HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
mAudioManager.setMicrophoneMute(event.valueInt2 == 0);
+
mVgmFromStack = true;
}
break;
@@ -1960,6 +1997,9 @@
mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
break;
case EVENT_TYPE_RING_INDICATION:
+ // Ringing is not handled at this indication and rather should be
+ // implemented (by the client of this service). Use the
+ // CALL_STATE_INCOMING (and similar) handle ringing.
break;
default:
Log.e(TAG, "Unknown stack event: " + event.type);
@@ -2023,23 +2063,21 @@
break;
}
+ // Audio state is split in two parts, the audio focus is maintained by the
+ // entity exercising this service (typically the Telecom stack) and audio
+ // routing is handled by the bluetooth stack itself. The only reason to do so is
+ // because Bluetooth SCO connection from the HF role is not entirely supported
+ // for routing and volume purposes.
+ // NOTE: All calls here are routed via the setParameters which changes the
+ // routing at the Audio HAL level.
mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED;
- // request audio focus for call
- int newAudioMode = AudioManager.MODE_IN_CALL;
- int currMode = mAudioManager.getMode();
- if (currMode != newAudioMode) {
- // request audio focus before setting the new mode
- mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- Log.d(TAG, "setAudioMode Setting audio mode from "
- + currMode + " to " + newAudioMode);
- mAudioManager.setMode(newAudioMode);
- }
// We need to set the volume after switching into HFP mode as some Audio HALs
// reset the volume to a known-default on mode switch.
- final int volume =
- mAudioManager.getStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO);
+ final int amVol =
+ mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+ final int hfVol = amToHfVol(amVol);
+
Log.d(TAG,"hfp_enable=true");
Log.d(TAG,"mAudioWbs is " + mAudioWbs);
if (mAudioWbs) {
@@ -2050,8 +2088,9 @@
Log.d(TAG,"Setting sampling rate as 8000");
mAudioManager.setParameters("hfp_set_sampling_rate=8000");
}
+ Log.d(TAG, "hf_volume " + hfVol);
mAudioManager.setParameters("hfp_enable=true");
- mAudioManager.setParameters("hfp_volume=" + volume);
+ mAudioManager.setParameters("hfp_volume=" + hfVol);
transitionTo(mAudioOn);
break;
case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING:
@@ -2083,9 +2122,6 @@
@Override
public void enter() {
Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what);
-
- mAudioManager.setStreamSolo(AudioManager.STREAM_BLUETOOTH_SCO, true);
-
broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED,
BluetoothHeadsetClient.STATE_AUDIO_CONNECTING);
}
@@ -2119,13 +2155,6 @@
*/
if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
- //abandon audio focus
- if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- Log.d(TAG, "abandonAudioFocus");
- // abandon audio focus after the mode has been set back to normal
- mAudioManager.abandonAudioFocusForCall();
- }
Log.d(TAG,"hfp_enable=false");
mAudioManager.setParameters("hfp_enable=false");
broadcastAudioState(mCurrentDevice,
@@ -2192,13 +2221,10 @@
case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
if (mAudioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) {
mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
- //abandon audio focus for call
- if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- Log.d(TAG, "abandonAudioFocus");
- // abandon audio focus after the mode has been set back to normal
- mAudioManager.abandonAudioFocusForCall();
- }
+ // Audio focus may still be held by the entity controlling the actual call
+ // (such as Telecom) and hence this will still keep the call around, there
+ // is not much we can do here since dropping the call without user consent
+ // even if the audio connection snapped may not be a good idea.
Log.d(TAG,"hfp_enable=false");
mAudioManager.setParameters("hfp_enable=false");
broadcastAudioState(device,
@@ -2217,8 +2243,6 @@
@Override
public void exit() {
Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what);
-
- mAudioManager.setStreamSolo(AudioManager.STREAM_BLUETOOTH_SCO, false);
}
}
@@ -2322,7 +2346,6 @@
intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
}
}
-
mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
}
diff --git a/src/com/android/bluetooth/hfpclient/connserv/HfpClientConference.java b/src/com/android/bluetooth/hfpclient/connserv/HfpClientConference.java
new file mode 100644
index 0000000..c5ebfd0
--- /dev/null
+++ b/src/com/android/bluetooth/hfpclient/connserv/HfpClientConference.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.hfpclient.connserv;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHeadsetClientCall;
+import android.os.Bundle;
+import android.telecom.Conference;
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class HfpClientConference extends Conference {
+ private static final String TAG = "HfpClientConference";
+
+ private BluetoothDevice mDevice;
+ private BluetoothHeadsetClient mHeadsetProfile;
+
+ public HfpClientConference(PhoneAccountHandle handle,
+ BluetoothDevice device, BluetoothHeadsetClient client) {
+ super(handle);
+ mDevice = device;
+ mHeadsetProfile = client;
+ boolean manage = HfpClientConnectionService.hasHfpClientEcc(client, device);
+ setConnectionCapabilities(Connection.CAPABILITY_SUPPORT_HOLD |
+ Connection.CAPABILITY_HOLD |
+ (manage ? Connection.CAPABILITY_MANAGE_CONFERENCE : 0));
+ setActive();
+ }
+
+ @Override
+ public void onDisconnect() {
+ Log.d(TAG, "onDisconnect");
+ mHeadsetProfile.terminateCall(mDevice, 0);
+ setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ }
+
+ @Override
+ public void onMerge(Connection connection) {
+ Log.d(TAG, "onMerge " + connection);
+ addConnection(connection);
+ }
+
+ @Override
+ public void onSeparate(Connection connection) {
+ Log.d(TAG, "onSeparate " + connection);
+ ((HfpClientConnection) connection).enterPrivateMode();
+ removeConnection(connection);
+ }
+
+ @Override
+ public void onHold() {
+ Log.d(TAG, "onHold");
+ mHeadsetProfile.holdCall(mDevice);
+ }
+
+ @Override
+ public void onUnhold() {
+ if (getPrimaryConnection().getConnectionService()
+ .getAllConnections().size() > 1) {
+ Log.w(TAG, "Ignoring unhold; call hold on the foreground call");
+ return;
+ }
+ Log.d(TAG, "onUnhold");
+ mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_HOLD);
+ }
+
+ @Override
+ public void onPlayDtmfTone(char c) {
+ Log.d(TAG, "onPlayDtmfTone " + c);
+ if (mHeadsetProfile != null) {
+ mHeadsetProfile.sendDTMF(mDevice, (byte) c);
+ }
+ }
+
+ @Override
+ public void onConnectionAdded(Connection connection) {
+ Log.d(TAG, "onConnectionAdded " + connection);
+ if (connection.getState() == Connection.STATE_HOLDING &&
+ getState() == Connection.STATE_ACTIVE) {
+ connection.onAnswer();
+ } else if (connection.getState() == Connection.STATE_ACTIVE &&
+ getState() == Connection.STATE_HOLDING) {
+ mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_NONE);
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnection.java b/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnection.java
new file mode 100644
index 0000000..4e4e1be
--- /dev/null
+++ b/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnection.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.hfpclient.connserv;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHeadsetClientCall;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import android.telecom.TelecomManager;
+import android.util.Log;
+
+public class HfpClientConnection extends Connection {
+ private static final String TAG = "HfpClientConnection";
+
+ private final Context mContext;
+ private final BluetoothDevice mDevice;
+
+ private BluetoothHeadsetClient mHeadsetProfile;
+ private BluetoothHeadsetClientCall mCurrentCall;
+ private boolean mClosed;
+ private boolean mLocalDisconnect;
+ private boolean mClientHasEcc;
+ private boolean mAdded;
+
+ public HfpClientConnection(Context context, BluetoothDevice device, BluetoothHeadsetClient client,
+ BluetoothHeadsetClientCall call, Uri number) {
+ mDevice = device;
+ mContext = context;
+ mHeadsetProfile = client;
+ mCurrentCall = call;
+ if (mHeadsetProfile != null) {
+ mClientHasEcc = HfpClientConnectionService.hasHfpClientEcc(mHeadsetProfile, mDevice);
+ }
+ setAudioModeIsVoip(false);
+ setAddress(number, TelecomManager.PRESENTATION_ALLOWED);
+ setInitialized();
+
+ if (mHeadsetProfile != null) {
+ finishInitializing();
+ }
+ }
+
+ public void onHfpConnected(BluetoothHeadsetClient client) {
+ mHeadsetProfile = client;
+ mClientHasEcc = HfpClientConnectionService.hasHfpClientEcc(mHeadsetProfile, mDevice);
+ finishInitializing();
+ }
+
+ public void onHfpDisconnected() {
+ mHeadsetProfile = null;
+ close(DisconnectCause.ERROR);
+ }
+
+ public void onAdded() {
+ mAdded = true;
+ }
+
+ public BluetoothHeadsetClientCall getCall() {
+ return mCurrentCall;
+ }
+
+ public boolean inConference() {
+ return mAdded && mCurrentCall != null && mCurrentCall.isMultiParty() &&
+ getState() != Connection.STATE_DISCONNECTED;
+ }
+
+ public void enterPrivateMode() {
+ mHeadsetProfile.enterPrivateMode(mDevice, mCurrentCall.getId());
+ setActive();
+ }
+
+ public void handleCallChanged(BluetoothHeadsetClientCall call) {
+ HfpClientConference conference = (HfpClientConference) getConference();
+ mCurrentCall = call;
+
+ int state = call.getState();
+ Log.d(TAG, "Got call state change to " + state);
+ switch (state) {
+ case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
+ setActive();
+ if (conference != null) {
+ conference.setActive();
+ }
+ break;
+ case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
+ case BluetoothHeadsetClientCall.CALL_STATE_HELD:
+ setOnHold();
+ if (conference != null) {
+ conference.setOnHold();
+ }
+ break;
+ case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
+ case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
+ setDialing();
+ break;
+ case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
+ case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
+ setRinging();
+ break;
+ case BluetoothHeadsetClientCall.CALL_STATE_TERMINATED:
+ // TODO Use more specific causes
+ close(mLocalDisconnect ? DisconnectCause.LOCAL : DisconnectCause.REMOTE);
+ break;
+ default:
+ Log.wtf(TAG, "Unexpected phone state " + state);
+ }
+ setConnectionCapabilities(CAPABILITY_SUPPORT_HOLD | CAPABILITY_MUTE |
+ CAPABILITY_SEPARATE_FROM_CONFERENCE | CAPABILITY_DISCONNECT_FROM_CONFERENCE |
+ (getState() == STATE_ACTIVE || getState() == STATE_HOLDING ? CAPABILITY_HOLD : 0));
+ }
+
+ private void finishInitializing() {
+ if (mCurrentCall == null) {
+ String number = getAddress().getSchemeSpecificPart();
+ Log.d(TAG, "Dialing " + number);
+ mHeadsetProfile.dial(mDevice, number);
+ setDialing();
+ // We will change state dependent on broadcasts from BluetoothHeadsetClientCall.
+ } else {
+ handleCallChanged(mCurrentCall);
+ }
+ }
+
+ private void close(int cause) {
+ Log.d(TAG, "Closing " + mClosed);
+ if (mClosed) {
+ return;
+ }
+ setDisconnected(new DisconnectCause(cause));
+
+ mClosed = true;
+ mCurrentCall = null;
+
+ destroy();
+ }
+
+ @Override
+ public void onPlayDtmfTone(char c) {
+ Log.d(TAG, "onPlayDtmfTone " + c + " " + mCurrentCall);
+ if (!mClosed && mHeadsetProfile != null) {
+ mHeadsetProfile.sendDTMF(mDevice, (byte) c);
+ }
+ }
+
+ @Override
+ public void onDisconnect() {
+ Log.d(TAG, "onDisconnect " + mCurrentCall);
+ if (!mClosed) {
+ if (mHeadsetProfile != null && mCurrentCall != null) {
+ mHeadsetProfile.terminateCall(mDevice, mClientHasEcc ? mCurrentCall.getId() : 0);
+ mLocalDisconnect = true;
+ } else {
+ close(DisconnectCause.LOCAL);
+ }
+ }
+ }
+
+ @Override
+ public void onAbort() {
+ Log.d(TAG, "onAbort " + mCurrentCall);
+ onDisconnect();
+ }
+
+ @Override
+ public void onHold() {
+ Log.d(TAG, "onHold " + mCurrentCall);
+ if (!mClosed && mHeadsetProfile != null) {
+ mHeadsetProfile.holdCall(mDevice);
+ }
+ }
+
+ @Override
+ public void onUnhold() {
+ if (getConnectionService().getAllConnections().size() > 1) {
+ Log.w(TAG, "Ignoring unhold; call hold on the foreground call");
+ return;
+ }
+ Log.d(TAG, "onUnhold " + mCurrentCall);
+ if (!mClosed && mHeadsetProfile != null) {
+ mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_HOLD);
+ }
+ }
+
+ @Override
+ public void onAnswer() {
+ Log.d(TAG, "onAnswer " + mCurrentCall);
+ if (!mClosed) {
+ mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_NONE);
+ }
+ }
+
+ @Override
+ public void onReject() {
+ Log.d(TAG, "onReject " + mCurrentCall);
+ if (!mClosed) {
+ mHeadsetProfile.rejectCall(mDevice);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof HfpClientConnection)) {
+ return false;
+ }
+ Uri otherAddr = ((HfpClientConnection) o).getAddress();
+ return getAddress() == otherAddr || otherAddr != null && otherAddr.equals(getAddress());
+ }
+
+ @Override
+ public int hashCode() {
+ return getAddress() == null ? 0 : getAddress().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "HfpClientConnection{" + getAddress() + "," + stateToString(getState()) + "," +
+ mCurrentCall + "}";
+ }
+}
diff --git a/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionService.java b/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionService.java
new file mode 100644
index 0000000..55ddf22
--- /dev/null
+++ b/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionService.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.hfpclient.connserv;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHeadsetClientCall;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.Log;
+
+import com.android.bluetooth.hfpclient.HeadsetClientService;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HfpClientConnectionService extends ConnectionService {
+ private static final String TAG = "HfpClientConnService";
+
+ public static final String HFP_SCHEME = "hfpc";
+
+ private BluetoothAdapter mAdapter;
+ // Currently active device.
+ private BluetoothDevice mDevice;
+ // Phone account associated with the above device.
+ private PhoneAccount mDevicePhoneAccount;
+ // BluetoothHeadset proxy.
+ private BluetoothHeadsetClient mHeadsetProfile;
+ private TelecomManager mTelecomManager;
+
+ private Map<Uri, HfpClientConnection> mConnections = new HashMap<>();
+ private HfpClientConference mConference;
+
+ private boolean mPendingAcceptCall;
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive " + intent);
+ String action = intent != null ? intent.getAction() : null;
+
+ if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ Log.d(TAG, "Established connection with " + device);
+ synchronized (HfpClientConnectionService.this) {
+ if (device.equals(mDevice)) {
+ // We are already connected and this message can be safeuly ignored.
+ Log.w(TAG, "Got connected for previously connected device, ignoring.");
+ } else {
+ // Since we are connected to a new device close down the previous
+ // account and register the new one.
+ if (mDevicePhoneAccount != null) {
+ mTelecomManager.unregisterPhoneAccount(
+ mDevicePhoneAccount.getAccountHandle());
+ }
+ // Reset the device and the phone account associated.
+ mDevice = device;
+ mDevicePhoneAccount =
+ getAccount(HfpClientConnectionService.this, device);
+ mTelecomManager.registerPhoneAccount(mDevicePhoneAccount);
+ mTelecomManager.enablePhoneAccount(
+ mDevicePhoneAccount.getAccountHandle(), true);
+ mTelecomManager.setUserSelectedOutgoingPhoneAccount(
+ mDevicePhoneAccount.getAccountHandle());
+ }
+ }
+
+ // Add any existing calls to the telecom stack.
+ if (mHeadsetProfile != null) {
+ List<BluetoothHeadsetClientCall> calls =
+ mHeadsetProfile.getCurrentCalls(mDevice);
+ Log.d(TAG, "Got calls " + calls);
+ for (BluetoothHeadsetClientCall call : calls) {
+ handleCall(call);
+ }
+ } else {
+ }
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ Log.d(TAG, "Disconnecting from " + device);
+ // Disconnect any inflight calls from the connection service.
+ synchronized (HfpClientConnectionService.this) {
+ if (device.equals(mDevice)) {
+ Log.d(TAG, "Resetting state for " + device);
+ mDevice = null;
+ disconnectAll();
+ mTelecomManager.unregisterPhoneAccount(
+ mDevicePhoneAccount.getAccountHandle());
+ mDevicePhoneAccount = null;
+ }
+ }
+ }
+ } else if (BluetoothHeadsetClient.ACTION_CALL_CHANGED.equals(action)) {
+ // If we are not connected, then when we actually do get connected -- the calls should
+ // be added (see ACTION_CONNECTION_STATE_CHANGED intent above).
+ handleCall((BluetoothHeadsetClientCall)
+ intent.getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL));
+ Log.d(TAG, mConnections.size() + " remaining");
+ }
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "onCreate");
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mTelecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+ mAdapter.getProfileProxy(this, mServiceListener, BluetoothProfile.HEADSET_CLIENT);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy called");
+ // Close the profile.
+ if (mHeadsetProfile != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.HEADSET_CLIENT, mHeadsetProfile);
+ }
+
+ // Unregister the broadcast receiver.
+ try {
+ unregisterReceiver(mBroadcastReceiver);
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG, "Receiver was not registered.");
+ }
+
+ // Unregister the phone account. This should ideally happen when disconnection ensues but in
+ // case the service crashes we may need to force clean.
+ synchronized (this) {
+ mDevice = null;
+ if (mDevicePhoneAccount != null) {
+ mTelecomManager.unregisterPhoneAccount(mDevicePhoneAccount.getAccountHandle());
+ mDevicePhoneAccount = null;
+ }
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "onStartCommand " + intent);
+ // In order to make sure that the service is sticky (recovers from errors when HFP
+ // connection is still active) and to stop it we need a special intent since stopService
+ // only recreates it.
+ if (intent.getBooleanExtra(HeadsetClientService.HFP_CLIENT_STOP_TAG, false)) {
+ // Stop the service.
+ stopSelf();
+ return 0;
+ } else {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
+ registerReceiver(mBroadcastReceiver, filter);
+ return START_STICKY;
+ }
+ }
+
+ private void handleCall(BluetoothHeadsetClientCall call) {
+ Log.d(TAG, "Got call " + call);
+
+ Uri number = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
+ HfpClientConnection connection = mConnections.get(number);
+ if (connection != null) {
+ connection.handleCallChanged(call);
+ }
+
+ if (connection == null) {
+ // Create the connection here, trigger Telecom to bind to us.
+ buildConnection(call.getDevice(), call, number);
+
+ PhoneAccountHandle handle = getHandle();
+ TelecomManager manager =
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+
+ // Depending on where this call originated make it an incoming call or outgoing
+ // (represented as unknown call in telecom since). Since BluetoothHeadsetClientCall is a
+ // parcelable we simply pack the entire object in there.
+ Bundle b = new Bundle();
+ if (call.getState() == BluetoothHeadsetClientCall.CALL_STATE_DIALING ||
+ call.getState() == BluetoothHeadsetClientCall.CALL_STATE_ALERTING ||
+ call.getState() == BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) {
+ // This is an outgoing call. Even if it is an active call we do not have a way of
+ // putting that parcelable in a seaprate field.
+ b.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, call);
+ manager.addNewUnknownCall(handle, b);
+ } else if (call.getState() == BluetoothHeadsetClientCall.CALL_STATE_INCOMING) {
+ // This is an incoming call.
+ b.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, call);
+ manager.addNewIncomingCall(handle, b);
+ }
+ } else if (call.getState() == BluetoothHeadsetClientCall.CALL_STATE_TERMINATED) {
+ Log.d(TAG, "Removing number " + number);
+ mConnections.remove(number);
+ }
+ updateConferenceableConnections();
+ }
+
+ // This method is called whenever there is a new incoming call (or right after BT connection).
+ @Override
+ public Connection onCreateIncomingConnection(
+ PhoneAccountHandle connectionManagerAccount,
+ ConnectionRequest request) {
+ Log.d(TAG, "onCreateIncomingConnection " + connectionManagerAccount + " req: " + request);
+ if (connectionManagerAccount != null &&
+ !getHandle().equals(connectionManagerAccount)) {
+ Log.w(TAG, "HfpClient does not support having a connection manager");
+ return null;
+ }
+
+ // We should already have a connection by this time.
+ BluetoothHeadsetClientCall call =
+ request.getExtras().getParcelable(
+ TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
+ Uri number = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
+ HfpClientConnection connection = mConnections.get(number);
+
+ if (connection != null) {
+ connection.onAdded();
+ updateConferenceableConnections();
+ return connection;
+ } else {
+ Log.e(TAG, "Connection should exist in our db, if it doesn't we dont know how to " +
+ "handle this call.");
+ return null;
+ }
+ }
+
+ // This method is called *only if* Dialer UI is used to place an outgoing call.
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle connectionManagerAccount,
+ ConnectionRequest request) {
+ Log.d(TAG, "onCreateOutgoingConnection " + connectionManagerAccount);
+ if (connectionManagerAccount != null &&
+ !getHandle().equals(connectionManagerAccount)) {
+ Log.w(TAG, "HfpClient does not support having a connection manager");
+ return null;
+ }
+
+ HfpClientConnection connection =
+ buildConnection(getDevice(request.getAccountHandle()), null, request.getAddress());
+ connection.onAdded();
+ return connection;
+ }
+
+ // This method is called when:
+ // 1. Outgoing call created from the AG.
+ // 2. Call transfer from AG -> HF (on connection when existed call present).
+ @Override
+ public Connection onCreateUnknownConnection(
+ PhoneAccountHandle connectionManagerAccount,
+ ConnectionRequest request) {
+ Log.d(TAG, "onCreateUnknownConnection " + connectionManagerAccount);
+ if (connectionManagerAccount != null &&
+ !getHandle().equals(connectionManagerAccount)) {
+ Log.w(TAG, "HfpClient does not support having a connection manager");
+ return null;
+ }
+
+ // We should already have a connection by this time.
+ BluetoothHeadsetClientCall call =
+ request.getExtras().getParcelable(
+ TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
+ Uri number = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
+ HfpClientConnection connection = mConnections.get(number);
+
+ if (connection != null) {
+ connection.onAdded();
+ updateConferenceableConnections();
+ return connection;
+ } else {
+ Log.e(TAG, "Connection should exist in our db, if it doesn't we dont know how to " +
+ "handle this call " + call);
+ return null;
+ }
+ }
+
+ @Override
+ public void onConference(Connection connection1, Connection connection2) {
+ Log.d(TAG, "onConference " + connection1 + " " + connection2);
+ if (mConference == null) {
+ BluetoothDevice device = getDevice(getHandle());
+ mConference = new HfpClientConference(getHandle(), device, mHeadsetProfile);
+ addConference(mConference);
+ }
+ mConference.setActive();
+ if (connection1.getConference() == null) {
+ mConference.addConnection(connection1);
+ }
+ if (connection2.getConference() == null) {
+ mConference.addConnection(connection2);
+ }
+ }
+
+ private void updateConferenceableConnections() {
+ Collection<HfpClientConnection> all = mConnections.values();
+
+ List<Connection> held = new ArrayList<>();
+ List<Connection> active = new ArrayList<>();
+ List<Connection> group = new ArrayList<>();
+ for (HfpClientConnection connection : all) {
+ switch (connection.getState()) {
+ case Connection.STATE_ACTIVE:
+ active.add(connection);
+ break;
+ case Connection.STATE_HOLDING:
+ held.add(connection);
+ break;
+ default:
+ break;
+ }
+ if (connection.inConference()) {
+ group.add(connection);
+ }
+ }
+ for (Connection connection : held) {
+ connection.setConferenceableConnections(active);
+ }
+ for (Connection connection : active) {
+ connection.setConferenceableConnections(held);
+ }
+ if (group.size() > 1 && mConference == null) {
+ BluetoothDevice device = getDevice(getHandle());
+ mConference = new HfpClientConference(getHandle(), device, mHeadsetProfile);
+ if (group.get(0).getState() == Connection.STATE_ACTIVE) {
+ mConference.setActive();
+ } else {
+ mConference.setOnHold();
+ }
+ for (Connection connection : group) {
+ mConference.addConnection(connection);
+ }
+ addConference(mConference);
+ }
+ if (mConference != null) {
+ List<Connection> toRemove = new ArrayList<>();
+ for (Connection connection : mConference.getConnections()) {
+ if (!((HfpClientConnection) connection).inConference()) {
+ toRemove.add(connection);
+ }
+ }
+ for (Connection connection : toRemove) {
+ mConference.removeConnection(connection);
+ }
+ if (mConference.getConnections().size() <= 1) {
+ mConference.destroy();
+ mConference = null;
+ } else {
+ List<Connection> notConferenced = new ArrayList<>();
+ for (Connection connection : all) {
+ if (connection.getConference() == null &&
+ (connection.getState() == Connection.STATE_HOLDING ||
+ connection.getState() == Connection.STATE_ACTIVE)) {
+ if (((HfpClientConnection) connection).inConference()) {
+ mConference.addConnection(connection);
+ } else {
+ notConferenced.add(connection);
+ }
+ }
+ }
+ mConference.setConferenceableConnections(notConferenced);
+ }
+ }
+ }
+
+ private void disconnectAll() {
+ for (HfpClientConnection connection : mConnections.values()) {
+ connection.onHfpDisconnected();
+ }
+ if (mConference != null) {
+ mConference.destroy();
+ mConference = null;
+ }
+ }
+
+ private BluetoothDevice getDevice(PhoneAccountHandle handle) {
+ PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
+ String btAddr = account.getAddress().getSchemeSpecificPart();
+ return mAdapter.getRemoteDevice(btAddr);
+ }
+
+ private HfpClientConnection buildConnection(
+ BluetoothDevice device, BluetoothHeadsetClientCall call, Uri number) {
+ Log.d(TAG, "Creating connection on " + device + " for " + call + "/" + number);
+ HfpClientConnection connection =
+ new HfpClientConnection(this, device, mHeadsetProfile, call, number);
+ mConnections.put(number, connection);
+ return connection;
+ }
+
+ BluetoothProfile.ServiceListener mServiceListener = new BluetoothProfile.ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ Log.d(TAG, "onServiceConnected");
+ mHeadsetProfile = (BluetoothHeadsetClient) proxy;
+
+ List<BluetoothDevice> devices = mHeadsetProfile.getConnectedDevices();
+ if (devices == null || devices.size() != 1) {
+ Log.w(TAG, "No connected or more than one connected devices found." + devices);
+ } else { // We have exactly one device connected.
+ Log.d(TAG, "Creating phone account.");
+ synchronized (HfpClientConnectionService.this) {
+ mDevice = devices.get(0);
+ mDevicePhoneAccount = getAccount(HfpClientConnectionService.this, mDevice);
+ mTelecomManager.registerPhoneAccount(mDevicePhoneAccount);
+ mTelecomManager.enablePhoneAccount(
+ mDevicePhoneAccount.getAccountHandle(), true);
+ mTelecomManager.setUserSelectedOutgoingPhoneAccount(
+ mDevicePhoneAccount.getAccountHandle());
+ }
+ }
+
+ for (HfpClientConnection connection : mConnections.values()) {
+ connection.onHfpConnected(mHeadsetProfile);
+ }
+
+ List<BluetoothHeadsetClientCall> calls = mHeadsetProfile.getCurrentCalls(mDevice);
+ Log.d(TAG, "Got calls " + calls);
+ if (calls != null) {
+ for (BluetoothHeadsetClientCall call : calls) {
+ handleCall(call);
+ }
+ }
+
+ if (mPendingAcceptCall) {
+ mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_NONE);
+ mPendingAcceptCall = false;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ Log.d(TAG, "onServiceDisconnected " + profile);
+ mHeadsetProfile = null;
+ disconnectAll();
+ }
+ };
+
+ public static boolean hasHfpClientEcc(BluetoothHeadsetClient client, BluetoothDevice device) {
+ Bundle features = client.getCurrentAgEvents(device);
+ return features == null ? false :
+ features.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, false);
+ }
+
+ public synchronized PhoneAccountHandle getHandle() {
+ if (mDevicePhoneAccount == null) throw new IllegalStateException("Handle null??");
+ return mDevicePhoneAccount.getAccountHandle();
+ }
+
+ public static PhoneAccount getAccount(Context context, BluetoothDevice device) {
+ Uri addr = Uri.fromParts(HfpClientConnectionService.HFP_SCHEME, device.getAddress(), null);
+ PhoneAccountHandle handle = new PhoneAccountHandle(
+ new ComponentName(context, HfpClientConnectionService.class), device.getAddress());
+ PhoneAccount account =
+ new PhoneAccount.Builder(handle, "HFP")
+ .setAddress(addr)
+ .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_TEL))
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .build();
+ Log.d(TAG, "phoneaccount: " + account);
+ return account;
+ }
+}
diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index c35c41a..0be7ace 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -49,7 +49,6 @@
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.text.TextUtils;
-import android.util.EventLog;
import android.util.Log;
import android.util.Xml;
@@ -3268,7 +3267,6 @@
(context.checkCallingOrSelfPermission("android.Manifest.permission.WRITE_SMS")
!= PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "actionSmsSentDisconnected: Not allowed to delete SMS/MMS messages");
- EventLog.writeEvent(0x534e4554, "b/22343270", Binder.getCallingUid(), "");
return;
}
diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java
index 567bef1..1b6155e 100644
--- a/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
+import android.Manifest;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -151,6 +152,8 @@
private MapServiceMessageHandler mSessionStatusHandler;
private boolean mStartError = true;
+ private boolean mSmsCapable = true;
+
// package and class name to which we send intent to check phone book access permission
private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
private static final String ACCESS_AUTHORITY_CLASS =
@@ -618,7 +621,10 @@
if (!mRegisteredMapReceiver) {
try {
registerReceiver(mMapReceiver, filter);
- registerReceiver(mMapReceiver, filterMessageSent);
+ // We need WRITE_SMS permission to handle messages in
+ // actionMessageSentDisconnected()
+ registerReceiver(mMapReceiver, filterMessageSent,
+ Manifest.permission.WRITE_SMS, null);
mRegisteredMapReceiver = true;
} catch (Exception e) {
Log.e(TAG,"Unable to register map receiver",e);
@@ -628,6 +634,9 @@
mAppObserver = new BluetoothMapAppObserver(this, this);
mEnabledAccounts = mAppObserver.getEnabledAccountItems();
+
+ mSmsCapable = getResources().getBoolean(
+ com.android.internal.R.bool.config_sms_capable);
// Uses mEnabledAccounts, hence getEnabledAccountItems() must be called before this.
createMasInstances();
@@ -766,17 +775,19 @@
}
private void createMasInstances() {
- int masId = MAS_ID_SMS_MMS;
+ int masId = mSmsCapable ? MAS_ID_SMS_MMS : -1;
- // Add the SMS/MMS instance
- BluetoothMapMasInstance smsMmsInst =
- new BluetoothMapMasInstance(this,
- this,
- null,
- masId,
- true);
- mMasInstances.append(masId, smsMmsInst);
- mMasInstanceMap.put(null, smsMmsInst);
+ if (mSmsCapable) {
+ // Add the SMS/MMS instance
+ BluetoothMapMasInstance smsMmsInst =
+ new BluetoothMapMasInstance(this,
+ this,
+ null,
+ masId,
+ true);
+ mMasInstances.append(masId, smsMmsInst);
+ mMasInstanceMap.put(null, smsMmsInst);
+ }
// get list of accounts already set to be visible through MAP
for(BluetoothMapAccountItem account : mEnabledAccounts) {
@@ -1098,7 +1109,8 @@
BluetoothMapMasInstance masInst = null;
int result = getResultCode();
boolean handled = false;
- if(mMasInstances != null && (masInst = mMasInstances.get(MAS_ID_SMS_MMS)) != null) {
+ if(mSmsCapable && mMasInstances != null &&
+ (masInst = mMasInstances.get(MAS_ID_SMS_MMS)) != null) {
intent.putExtra(BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_RESULT, result);
if(masInst.handleSmsSendIntent(context, intent)) {
// The intent was handled by the mas instance it self
@@ -1109,8 +1121,12 @@
{
/* We do not have a connection to a device, hence we need to move
the SMS to the correct folder. */
- BluetoothMapContentObserver
+ try {
+ BluetoothMapContentObserver
.actionMessageSentDisconnected(context, intent, result);
+ } catch(IllegalArgumentException e) {
+ return;
+ }
}
} else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) &&
mIsWaitingAuthorization) {
diff --git a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
index 0025763..1d605c0 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
@@ -121,7 +121,7 @@
} else if (extra_text != null && type != null) {
if (V) Log.v(TAG, "Get ACTION_SEND intent with Extra_text = "
+ extra_text.toString() + "; mimetype = " + type);
- final Uri fileUri = creatFileForSharedContent(this, extra_text);
+ final Uri fileUri = creatFileForSharedContent(this.createCredentialProtectedStorageContext(), extra_text);
if (fileUri != null) {
Thread t = new Thread(new Runnable() {
public void run() {
@@ -169,6 +169,16 @@
return;
}
}
+ } else if (action.equals(Constants.ACTION_OPEN)) {
+ Uri uri = getIntent().getData();
+ if (V) Log.v(TAG, "Get ACTION_OPEN intent: Uri = " + uri);
+
+ Intent intent1 = new Intent();
+ intent1.setAction(action);
+ intent1.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+ intent1.setDataAndNormalize(uri);
+ this.sendBroadcast(intent1);
+ finish();
} else {
Log.w(TAG, "Unsupported action: " + action);
finish();
diff --git a/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/src/com/android/bluetooth/opp/BluetoothOppNotification.java
index 51e8701..da7a0c6 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppNotification.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppNotification.java
@@ -41,6 +41,7 @@
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.text.format.Formatter;
import android.util.Log;
import android.os.Handler;
import android.os.Message;
@@ -496,37 +497,42 @@
}
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
- CharSequence title =
- mContext.getText(R.string.incoming_file_confirm_Notification_title);
- CharSequence caption = mContext
- .getText(R.string.incoming_file_confirm_Notification_caption);
- int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
- long timeStamp = cursor.getLong(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
- Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
+ BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
+ BluetoothOppUtility.fillRecord(mContext, cursor, info);
+ Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + info.mID);
+ Intent baseIntent = new Intent().setDataAndNormalize(contentUri)
+ .setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- Notification n = new Notification();
- n.icon = R.drawable.bt_incomming_file_notification;
- n.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
- n.flags |= Notification.FLAG_ONGOING_EVENT;
- n.defaults = Notification.DEFAULT_SOUND;
- n.tickerText = title;
-
- Intent intent = new Intent(Constants.ACTION_INCOMING_FILE_CONFIRM);
- intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent.setDataAndNormalize(contentUri);
-
- n.when = timeStamp;
- n.color = mContext.getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color);
- n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
- intent, 0));
-
- intent = new Intent(Constants.ACTION_HIDE);
- intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent.setDataAndNormalize(contentUri);
- n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-
- mNotificationMgr.notify(id, n);
+ Notification n = new Notification.Builder(mContext)
+ .setOnlyAlertOnce(true)
+ .setOngoing(true)
+ .setVibrate(new long[] { 200 })
+ .setWhen(info.mTimeStamp)
+ .setDefaults(Notification.DEFAULT_SOUND)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .addAction(R.drawable.ic_decline,
+ mContext.getText(R.string.incoming_file_confirm_cancel),
+ PendingIntent.getBroadcast(mContext, 0,
+ new Intent(baseIntent).setAction(Constants.ACTION_DECLINE), 0))
+ .addAction(R.drawable.ic_accept,
+ mContext.getText(R.string.incoming_file_confirm_ok),
+ PendingIntent.getBroadcast(mContext, 0,
+ new Intent(baseIntent).setAction(Constants.ACTION_ACCEPT), 0))
+ .setContentIntent(PendingIntent.getBroadcast(mContext, 0,
+ new Intent(baseIntent).setAction(Constants.ACTION_INCOMING_FILE_CONFIRM), 0))
+ .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
+ new Intent(baseIntent).setAction(Constants.ACTION_HIDE), 0))
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(mContext.getText(R.string.incoming_file_confirm_Notification_title))
+ .setContentText(info.mFileName)
+ .setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
+ R.string.incoming_file_confirm_Notification_content,
+ info.mDeviceName, info.mFileName)))
+ .setContentInfo(Formatter.formatFileSize(mContext, info.mTotalBytes))
+ .setSmallIcon(R.drawable.bt_incomming_file_notification)
+ .build();
+ mNotificationMgr.notify(info.mID, n);
}
cursor.close();
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
index 97dc0b3..1688c70 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
@@ -416,19 +416,9 @@
position += readLength;
- if (position != fileInfo.mLength) {
- mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
- synchronized (this) {
- mWaitingForRemote = false;
- }
- } else {
- // if file length is smaller than buffer size, only one packet
- // so block point is here
- outputStream.close();
- mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
- synchronized (this) {
- mWaitingForRemote = false;
- }
+ mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
+ synchronized (this) {
+ mWaitingForRemote = false;
}
/* check remote accept or reject */
responseCode = putOperation.getResponseCode();
@@ -446,36 +436,34 @@
}
}
- while (!mInterrupted && okToProceed && (position != fileInfo.mLength)) {
- {
- if (V) timestamp = System.currentTimeMillis();
+ while (!mInterrupted && okToProceed && (position < fileInfo.mLength)) {
+ if (V) timestamp = System.currentTimeMillis();
- readLength = a.read(buffer, 0, outputBufferSize);
- outputStream.write(buffer, 0, readLength);
+ readLength = a.read(buffer, 0, outputBufferSize);
+ outputStream.write(buffer, 0, readLength);
- /* check remote abort */
- responseCode = putOperation.getResponseCode();
- if (V) Log.v(TAG, "Response code is " + responseCode);
- if (responseCode != ResponseCodes.OBEX_HTTP_CONTINUE
- && responseCode != ResponseCodes.OBEX_HTTP_OK) {
- /* abort happens */
- okToProceed = false;
- } else {
- position += readLength;
- if (V) {
- Log.v(TAG, "Sending file position = " + position
- + " readLength " + readLength + " bytes took "
- + (System.currentTimeMillis() - timestamp) + " ms");
- }
- // Update the Progress Bar only if there is change in percentage
- percent = position * 100 / fileInfo.mLength;
- if (percent > prevPercent) {
- updateValues = new ContentValues();
- updateValues.put(BluetoothShare.CURRENT_BYTES, position);
- mContext1.getContentResolver().update(contentUri, updateValues,
- null, null);
- prevPercent = percent;
- }
+ /* check remote abort */
+ responseCode = putOperation.getResponseCode();
+ if (V) Log.v(TAG, "Response code is " + responseCode);
+ if (responseCode != ResponseCodes.OBEX_HTTP_CONTINUE
+ && responseCode != ResponseCodes.OBEX_HTTP_OK) {
+ /* abort happens */
+ okToProceed = false;
+ } else {
+ position += readLength;
+ if (V) {
+ Log.v(TAG, "Sending file position = " + position
+ + " readLength " + readLength + " bytes took "
+ + (System.currentTimeMillis() - timestamp) + " ms");
+ }
+ // Update the Progress Bar only if there is change in percentage
+ percent = position * 100 / fileInfo.mLength;
+ if (percent > prevPercent) {
+ updateValues = new ContentValues();
+ updateValues.put(BluetoothShare.CURRENT_BYTES, position);
+ mContext1.getContentResolver().update(contentUri, updateValues,
+ null, null);
+ prevPercent = percent;
}
}
}
@@ -491,7 +479,6 @@
} else if (!mInterrupted && position == fileInfo.mLength) {
Log.i(TAG, "SendFile finished send out file " + fileInfo.mFileName
+ " length " + fileInfo.mLength);
- outputStream.close();
} else {
error = true;
status = BluetoothShare.STATUS_CANCELED;
@@ -509,6 +496,10 @@
handleSendException(e.toString());
} finally {
try {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+
// Close InputStream and remove SendFileInfo from map
BluetoothOppUtility.closeSendFileInfo(mInfo.mUri);
if (!error) {
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
index ee46646..2843249 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
@@ -93,8 +93,6 @@
private BluetoothOppReceiveFileInfo mFileInfo;
- private WakeLock mWakeLock;
-
private WakeLock mPartialWakeLock;
boolean mTimeoutMsgSent = false;
@@ -103,8 +101,6 @@
mContext = context;
mTransport = transport;
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
- | PowerManager.ON_AFTER_RELEASE, TAG);
mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
@@ -276,43 +272,27 @@
values.put(BluetoothShare.DIRECTION, BluetoothShare.DIRECTION_INBOUND);
values.put(BluetoothShare.TIMESTAMP, mTimestamp);
- boolean needConfirm = true;
/** It's not first put if !serverBlocking, so we auto accept it */
if (!mServerBlocking && (mAccepted == BluetoothShare.USER_CONFIRMATION_CONFIRMED ||
mAccepted == BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED)) {
values.put(BluetoothShare.USER_CONFIRMATION,
BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED);
- needConfirm = false;
}
if (isWhitelisted) {
values.put(BluetoothShare.USER_CONFIRMATION,
BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED);
- needConfirm = false;
}
Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
mLocalShareInfoId = Integer.parseInt(contentUri.getPathSegments().get(1));
- if (needConfirm) {
- if (V) Log.d(TAG, "acquire full WakeLock");
- mWakeLock.acquire();
-
- Intent in = new Intent(BluetoothShare.INCOMING_FILE_CONFIRMATION_REQUEST_ACTION);
- in.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- mContext.sendBroadcast(in);
- }
-
if (V) Log.v(TAG, "insert contentUri: " + contentUri);
if (V) Log.v(TAG, "mLocalShareInfoId = " + mLocalShareInfoId);
synchronized (this) {
- if (mWakeLock.isHeld()) {
- if (V) Log.v(TAG, "acquire partial WakeLock");
- mPartialWakeLock.acquire();
- mWakeLock.release();
- }
+ mPartialWakeLock.acquire();
mServerBlocking = true;
try {
@@ -596,9 +576,6 @@
}
private synchronized void releaseWakeLocks() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
if (mPartialWakeLock.isHeld()) {
mPartialWakeLock.release();
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
index 1803fea..415f3ea 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
@@ -119,13 +119,33 @@
in.setDataAndNormalize(uri);
context.startActivity(in);
cancelNotification(context, uri);
- } else if (action.equals(BluetoothShare.INCOMING_FILE_CONFIRMATION_REQUEST_ACTION)) {
- if (V) Log.v(TAG, "Receiver INCOMING_FILE_NOTIFICATION");
- Toast.makeText(context, context.getString(R.string.incoming_file_toast_msg),
- Toast.LENGTH_SHORT).show();
- } else if (action.equals(Constants.ACTION_LIST)) {
- Log.v(TAG, "Receiver list for " + intent.getData());
+ } else if (action.equals(Constants.ACTION_DECLINE)) {
+ if (V) Log.v(TAG, "Receiver ACTION_DECLINE");
+
+ Uri uri = intent.getData();
+ ContentValues values = new ContentValues();
+ values.put(BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_DENIED);
+ context.getContentResolver().update(uri, values, null, null);
+ cancelNotification(context, uri);
+
+ } else if (action.equals(Constants.ACTION_ACCEPT)) {
+ if (V) Log.v(TAG, "Receiver ACTION_ACCEPT");
+
+ Uri uri = intent.getData();
+ ContentValues values = new ContentValues();
+ values.put(BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_CONFIRMED);
+ context.getContentResolver().update(uri, values, null, null);
+ cancelNotification(context, uri);
+
+ } else if (action.equals(Constants.ACTION_OPEN) || action.equals(Constants.ACTION_LIST)) {
+ if (V) {
+ if (action.equals(Constants.ACTION_OPEN)) {
+ Log.v(TAG, "Receiver open for " + intent.getData());
+ } else {
+ Log.v(TAG, "Receiver list for " + intent.getData());
+ }
+ }
BluetoothOppTransferInfo transInfo = new BluetoothOppTransferInfo();
Uri uri = intent.getData();
diff --git a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
index 081efa0..b9d2b9f 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
@@ -125,8 +125,10 @@
if (metadataCursor != null) {
try {
if (metadataCursor.moveToFirst()) {
- fileName = metadataCursor.getString(0);
- length = metadataCursor.getLong(1);
+ fileName = metadataCursor.getString(
+ metadataCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ length = metadataCursor.getLong(
+ metadataCursor.getColumnIndex(OpenableColumns.SIZE));
if (D) Log.d(TAG, "fileName = " + fileName + " length = " + length);
}
} finally {
@@ -160,11 +162,23 @@
"), using stat length (" + Long.toString(statLength) + ")");
length = statLength;
}
+
try {
// This creates an auto-closing input-stream, so
// the file descriptor will be closed whenever the InputStream
// is closed.
is = fd.createInputStream();
+
+ // If the database doesn't contain the file size, get the size
+ // by reading through the entire stream
+ if (length == 0) {
+ length = getStreamSize(is);
+ Log.w(TAG, "File length not provided. Length from stream = "
+ + length);
+ // Reset the stream
+ fd = contentResolver.openAssetFileDescriptor(uri, "r");
+ is = fd.createInputStream();
+ }
} catch (IOException e) {
try {
fd.close();
@@ -176,25 +190,39 @@
// Ignore
}
}
+
if (is == null) {
try {
is = (FileInputStream) contentResolver.openInputStream(uri);
+
+ // If the database doesn't contain the file size, get the size
+ // by reading through the entire stream
+ if (length == 0) {
+ length = getStreamSize(is);
+ // Reset the stream
+ is = (FileInputStream) contentResolver.openInputStream(uri);
+ }
} catch (FileNotFoundException e) {
return SEND_FILE_INFO_ERROR;
- }
- }
- // If we can not get file length from content provider, we can try to
- // get the length via the opened stream.
- if (length == 0) {
- try {
- length = is.available();
- if (V) Log.v(TAG, "file length is " + length);
} catch (IOException e) {
- Log.e(TAG, "Read stream exception: ", e);
return SEND_FILE_INFO_ERROR;
}
}
+ if (length == 0) {
+ Log.e(TAG, "Could not determine size of file");
+ return SEND_FILE_INFO_ERROR;
+ }
+
return new BluetoothOppSendFileInfo(fileName, contentType, length, is, 0);
}
+
+ private static long getStreamSize(FileInputStream is) throws IOException {
+ long length = 0;
+ byte unused[] = new byte[4096];
+ while (is.available() > 0) {
+ length += is.read(unused, 0, 4096);
+ }
+ return length;
+ }
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
index 0c97983..0bf6c3f 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppUtility.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
@@ -66,59 +66,11 @@
= new ConcurrentHashMap<Uri, BluetoothOppSendFileInfo>();
public static BluetoothOppTransferInfo queryRecord(Context context, Uri uri) {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
- info.mID = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
- info.mStatus = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
- info.mDirection = cursor.getInt(cursor
- .getColumnIndexOrThrow(BluetoothShare.DIRECTION));
- info.mTotalBytes = cursor.getLong(cursor
- .getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
- info.mCurrentBytes = cursor.getLong(cursor
- .getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
- info.mTimeStamp = cursor.getLong(cursor
- .getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
- info.mDestAddr = cursor.getString(cursor
- .getColumnIndexOrThrow(BluetoothShare.DESTINATION));
-
- info.mFileName = cursor.getString(cursor
- .getColumnIndexOrThrow(BluetoothShare._DATA));
- if (info.mFileName == null) {
- info.mFileName = cursor.getString(cursor
- .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
- }
- if (info.mFileName == null) {
- info.mFileName = context.getString(R.string.unknown_file);
- }
-
- info.mFileUri = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.URI));
-
- if (info.mFileUri != null) {
- Uri u = Uri.parse(info.mFileUri);
- info.mFileType = context.getContentResolver().getType(u);
- } else {
- Uri u = Uri.parse(info.mFileName);
- info.mFileType = context.getContentResolver().getType(u);
- }
- if (info.mFileType == null) {
- info.mFileType = cursor.getString(cursor
- .getColumnIndexOrThrow(BluetoothShare.MIMETYPE));
- }
-
- BluetoothDevice remoteDevice = adapter.getRemoteDevice(info.mDestAddr);
- info.mDeviceName =
- BluetoothOppManager.getInstance(context).getDeviceName(remoteDevice);
-
- int confirmationType = cursor.getInt(
- cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION));
- info.mHandoverInitiated =
- confirmationType == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED;
-
- if (V) Log.v(TAG, "Get data from db:" + info.mFileName + info.mFileType
- + info.mDestAddr);
+ fillRecord(context, cursor, info);
}
cursor.close();
} else {
@@ -128,6 +80,58 @@
return info;
}
+ public static void fillRecord(Context context, Cursor cursor, BluetoothOppTransferInfo info) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ info.mID = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
+ info.mStatus = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
+ info.mDirection = cursor.getInt(cursor
+ .getColumnIndexOrThrow(BluetoothShare.DIRECTION));
+ info.mTotalBytes = cursor.getLong(cursor
+ .getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
+ info.mCurrentBytes = cursor.getLong(cursor
+ .getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
+ info.mTimeStamp = cursor.getLong(cursor
+ .getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
+ info.mDestAddr = cursor.getString(cursor
+ .getColumnIndexOrThrow(BluetoothShare.DESTINATION));
+
+ info.mFileName = cursor.getString(cursor
+ .getColumnIndexOrThrow(BluetoothShare._DATA));
+ if (info.mFileName == null) {
+ info.mFileName = cursor.getString(cursor
+ .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
+ }
+ if (info.mFileName == null) {
+ info.mFileName = context.getString(R.string.unknown_file);
+ }
+
+ info.mFileUri = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.URI));
+
+ if (info.mFileUri != null) {
+ Uri u = Uri.parse(info.mFileUri);
+ info.mFileType = context.getContentResolver().getType(u);
+ } else {
+ Uri u = Uri.parse(info.mFileName);
+ info.mFileType = context.getContentResolver().getType(u);
+ }
+ if (info.mFileType == null) {
+ info.mFileType = cursor.getString(cursor
+ .getColumnIndexOrThrow(BluetoothShare.MIMETYPE));
+ }
+
+ BluetoothDevice remoteDevice = adapter.getRemoteDevice(info.mDestAddr);
+ info.mDeviceName =
+ BluetoothOppManager.getInstance(context).getDeviceName(remoteDevice);
+
+ int confirmationType = cursor.getInt(
+ cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION));
+ info.mHandoverInitiated =
+ confirmationType == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED;
+
+ if (V) Log.v(TAG, "Get data from db:" + info.mFileName + info.mFileType
+ + info.mDestAddr);
+ }
+
/**
* Organize Array list for transfers in one batch
*/
diff --git a/src/com/android/bluetooth/opp/BluetoothShare.java b/src/com/android/bluetooth/opp/BluetoothShare.java
index d033325..94c1fc2 100644
--- a/src/com/android/bluetooth/opp/BluetoothShare.java
+++ b/src/com/android/bluetooth/opp/BluetoothShare.java
@@ -64,12 +64,6 @@
/**
* This is sent by the Bluetooth Share component to indicate there is an
- * incoming file need user to confirm.
- */
- public static final String INCOMING_FILE_CONFIRMATION_REQUEST_ACTION = "android.btopp.intent.action.INCOMING_FILE_NOTIFICATION";
-
- /**
- * This is sent by the Bluetooth Share component to indicate there is an
* incoming file request timeout and need update UI.
*/
public static final String USER_CONFIRMATION_TIMEOUT_ACTION = "android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT";
diff --git a/src/com/android/bluetooth/opp/Constants.java b/src/com/android/bluetooth/opp/Constants.java
index cb017c8..acabb49 100644
--- a/src/com/android/bluetooth/opp/Constants.java
+++ b/src/com/android/bluetooth/opp/Constants.java
@@ -56,6 +56,9 @@
*/
public static final String ACTION_RETRY = "android.btopp.intent.action.RETRY";
+ /** the intent that gets sent when clicking a successful transfer */
+ public static final String ACTION_OPEN = "android.btopp.intent.action.OPEN";
+
/** the intent that gets sent when clicking outbound transfer notification */
public static final String ACTION_OPEN_OUTBOUND_TRANSFER = "android.btopp.intent.action.OPEN_OUTBOUND";
@@ -150,12 +153,21 @@
"com.android.intent.extra.CONNECTION_HANDOVER";
/**
- * the intent that gets sent when deleting the incoming file confirmation
- * notification
+ * the intent that gets sent when deleting the incoming file confirmation notification
*/
public static final String ACTION_HIDE = "android.btopp.intent.action.HIDE";
/**
+ * the intent that gets sent when accepting the incoming file confirmation notification
+ */
+ public static final String ACTION_ACCEPT = "android.btopp.intent.action.ACCEPT";
+
+ /**
+ * the intent that gets sent when declining the incoming file confirmation notification
+ */
+ public static final String ACTION_DECLINE = "android.btopp.intent.action.DECLINE";
+
+ /**
* the intent that gets sent when deleting the notifications of outbound and
* inbound completed transfer
*/
diff --git a/src/com/android/bluetooth/pan/BluetoothTetheringNetworkFactory.java b/src/com/android/bluetooth/pan/BluetoothTetheringNetworkFactory.java
index b9e372c..51058e1 100644
--- a/src/com/android/bluetooth/pan/BluetoothTetheringNetworkFactory.java
+++ b/src/com/android/bluetooth/pan/BluetoothTetheringNetworkFactory.java
@@ -26,7 +26,8 @@
import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkUtils;
+import android.net.ip.IpManager;
+import android.net.ip.IpManager.WaitForProvisioningCallback;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Slog;
@@ -52,7 +53,8 @@
// All accesses to these must be synchronized(this).
private final NetworkInfo mNetworkInfo;
- private LinkProperties mLinkProperties;
+ private IpManager mIpManager;
+ private String mInterfaceName;
private NetworkAgent mNetworkAgent;
public BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService) {
@@ -62,54 +64,72 @@
mPanService = panService;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORK_TYPE, "");
- mLinkProperties = new LinkProperties();
mNetworkCapabilities = new NetworkCapabilities();
initNetworkCapabilities();
setCapabilityFilter(mNetworkCapabilities);
}
+ private void stopIpManagerLocked() {
+ if (mIpManager != null) {
+ mIpManager.shutdown();
+ mIpManager = null;
+ }
+ }
+
// Called by NetworkFactory when PanService and NetworkFactory both desire a Bluetooth
// reverse-tether connection. A network interface for Bluetooth reverse-tethering can be
// assumed to be available because we only register our NetworkFactory when it is so.
@Override
protected void startNetwork() {
- // TODO: Handle DHCP renew.
- Thread dhcpThread = new Thread(new Runnable() {
+ // TODO: Figure out how to replace this thread with simple invocations
+ // of IpManager. This will likely necessitate a rethink about
+ // NetworkAgent, NetworkInfo, and associated instance lifetimes.
+ Thread ipProvisioningThread = new Thread(new Runnable() {
public void run() {
LinkProperties linkProperties;
+ final WaitForProvisioningCallback ipmCallback = new WaitForProvisioningCallback() {
+ @Override
+ public void onLinkPropertiesChange(LinkProperties newLp) {
+ synchronized (BluetoothTetheringNetworkFactory.this) {
+ if (mNetworkAgent != null && mNetworkInfo.isConnected()) {
+ mNetworkAgent.sendLinkProperties(newLp);
+ }
+ }
+ }
+ };
+
synchronized (BluetoothTetheringNetworkFactory.this) {
- linkProperties = mLinkProperties;
- if (linkProperties.getInterfaceName() == null) {
+ if (TextUtils.isEmpty(mInterfaceName)) {
Slog.e(TAG, "attempted to reverse tether without interface name");
return;
}
- log("dhcpThread(+" + linkProperties.getInterfaceName() +
- "): mNetworkInfo=" + mNetworkInfo);
+ log("ipProvisioningThread(+" + mInterfaceName + "): " +
+ "mNetworkInfo=" + mNetworkInfo);
+ mIpManager = new IpManager(mContext, mInterfaceName, ipmCallback);
+ mIpManager.startProvisioning(
+ mIpManager.buildProvisioningConfiguration()
+ .withoutIpReachabilityMonitor()
+ .build());
+ mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, null);
}
- DhcpResults dhcpResults = new DhcpResults();
- // TODO: Handle DHCP renewals better.
- // In general runDhcp handles DHCP renewals for us, because
- // the dhcp client stays running, but if the renewal fails,
- // we will lose our IP address and connectivity without
- // noticing.
- if (!NetworkUtils.runDhcp(linkProperties.getInterfaceName(), dhcpResults)) {
- Slog.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+ linkProperties = ipmCallback.waitForProvisioning();
+ if (linkProperties == null) {
+ Slog.e(TAG, "IP provisioning error.");
synchronized(BluetoothTetheringNetworkFactory.this) {
+ stopIpManagerLocked();
setScoreFilter(-1);
}
return;
}
synchronized(BluetoothTetheringNetworkFactory.this) {
- mLinkProperties = dhcpResults.toLinkProperties(
- linkProperties.getInterfaceName());
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
// Create our NetworkAgent.
mNetworkAgent = new NetworkAgent(getLooper(), mContext, NETWORK_TYPE,
- mNetworkInfo, mNetworkCapabilities, mLinkProperties, NETWORK_SCORE) {
+ mNetworkInfo, mNetworkCapabilities, linkProperties, NETWORK_SCORE) {
public void unwanted() {
BluetoothTetheringNetworkFactory.this.onCancelRequest();
};
@@ -117,7 +137,7 @@
}
}
});
- dhcpThread.start();
+ ipProvisioningThread.start();
}
// Called from NetworkFactory to indicate ConnectivityService no longer desires a Bluetooth
@@ -129,10 +149,9 @@
// Called by the NetworkFactory, NetworkAgent or PanService to tear down network.
private synchronized void onCancelRequest() {
- if (!TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
- NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName());
- }
- mLinkProperties.clear();
+ stopIpManagerLocked();
+ mInterfaceName = "";
+
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -151,12 +170,11 @@
return;
}
synchronized(this) {
- if (mLinkProperties.getInterfaceName() != null) {
+ if (!TextUtils.isEmpty(mInterfaceName)) {
Slog.e(TAG, "attempted to reverse tether while already in process");
return;
}
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(iface);
+ mInterfaceName = iface;
// Advertise ourselves to ConnectivityService.
register();
setScoreFilter(NETWORK_SCORE);
@@ -166,7 +184,7 @@
// Called by PanService when a network interface for Bluetooth reverse-tethering
// goes away. We stop advertising ourselves to ConnectivityService at this point.
public synchronized void stopReverseTether() {
- if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
+ if (TextUtils.isEmpty(mInterfaceName)) {
Slog.e(TAG, "attempted to stop reverse tether with nothing tethered");
return;
}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
index f4b5f10..a35b26c 100644
--- a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
@@ -33,39 +33,37 @@
package com.android.bluetooth.pbap;
+import com.android.bluetooth.R;
+import com.android.bluetooth.util.DevicePolicyUtils;
+import com.android.vcard.VCardComposer;
+import com.android.vcard.VCardConfig;
+import com.android.vcard.VCardPhoneNumberTranslationCallback;
+
import android.content.ContentResolver;
import android.content.Context;
-import android.database.CursorWindowAllocationException;
import android.database.Cursor;
+import android.database.CursorWindowAllocationException;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.CallLog;
-import android.provider.ContactsContract;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.RawContactsEntity;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
-import com.android.bluetooth.R;
-import com.android.vcard.VCardComposer;
-import com.android.vcard.VCardConfig;
-import com.android.vcard.VCardPhoneNumberTranslationCallback;
-
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
-import javax.obex.ServerOperation;
import javax.obex.Operation;
import javax.obex.ResponseCodes;
-
-import com.android.bluetooth.util.DevicePolicyUtils;
+import javax.obex.ServerOperation;
public class BluetoothPbapVcardManager {
private static final String TAG = "BluetoothPbapVcardManager";
diff --git a/src/com/android/bluetooth/pbapclient/AuthenticationService.java b/src/com/android/bluetooth/pbapclient/AuthenticationService.java
new file mode 100644
index 0000000..cddfe15
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/AuthenticationService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.pbapsink;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class AuthenticationService extends Service {
+ private Authenticator mAuthenticator;
+
+ @Override
+ public void onCreate() {
+ mAuthenticator = new Authenticator(this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mAuthenticator.getIBinder();
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/Authenticator.java b/src/com/android/bluetooth/pbapclient/Authenticator.java
new file mode 100644
index 0000000..4070ad2
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/Authenticator.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.pbapsink;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+
+public class Authenticator extends AbstractAccountAuthenticator {
+ private static final String TAG = "PbapAuthenticator";
+
+ public Authenticator(Context context) {
+ super(context);
+ }
+
+ // Editing properties is not supported
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse r, String s) {
+ Log.d(TAG, "got call", new Exception());
+ throw new UnsupportedOperationException();
+ }
+
+ // Don't add additional accounts
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse r, String s, String s2, String[] strings,
+ Bundle bundle) throws NetworkErrorException {
+ Log.d(TAG, "got call", new Exception());
+ // Don't allow accounts to be added.
+ throw new UnsupportedOperationException();
+ }
+
+ // Ignore attempts to confirm credentials
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse r, Account account,
+ Bundle bundle) throws NetworkErrorException {
+ Log.d(TAG, "got call", new Exception());
+ return null;
+ }
+
+ // Getting an authentication token is not supported
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse r, Account account, String s,
+ Bundle bundle) throws NetworkErrorException {
+ Log.d(TAG, "got call", new Exception());
+ throw new UnsupportedOperationException();
+ }
+
+ // Getting a label for the auth token is not supported
+ @Override
+ public String getAuthTokenLabel(String s) {
+ Log.d(TAG, "got call", new Exception());
+ return null;
+ }
+
+ // Updating user credentials is not supported
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse r, Account account, String s,
+ Bundle bundle) throws NetworkErrorException {
+ Log.d(TAG, "got call", new Exception());
+ return null;
+ }
+
+ // Checking features for the account is not supported
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse r, Account account, String[] strings)
+ throws NetworkErrorException {
+ Log.d(TAG, "got call", new Exception());
+
+ final Bundle result = new Bundle();
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+ return result;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapCard.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapCard.java
new file mode 100644
index 0000000..96422c5
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapCard.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntry.EmailData;
+import com.android.vcard.VCardEntry.NameData;
+import com.android.vcard.VCardEntry.PhoneData;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.List;
+
+/**
+ * Entry representation of folder listing
+ */
+public class BluetoothPbapCard {
+
+ public final String handle;
+
+ //Name to be parsed (N)
+ public final String N;
+ public final String lastName;
+ public final String firstName;
+ public final String middleName;
+ public final String prefix;
+ public final String suffix;
+
+ public BluetoothPbapCard(String handle, String name) {
+ this.handle = handle;
+
+ N = name;
+
+ /*
+ * format is as for vCard N field, so we have up to 5 tokens: LastName;
+ * FirstName; MiddleName; Prefix; Suffix
+ */
+ String[] parsedName = name.split(";", 5);
+
+ lastName = parsedName.length < 1 ? null : parsedName[0];
+ firstName = parsedName.length < 2 ? null : parsedName[1];
+ middleName = parsedName.length < 3 ? null : parsedName[2];
+ prefix = parsedName.length < 4 ? null : parsedName[3];
+ suffix = parsedName.length < 5 ? null : parsedName[4];
+ }
+
+ @Override
+ public String toString() {
+ JSONObject json = new JSONObject();
+
+ try {
+ json.put("handle", handle);
+ json.put("N", N);
+ json.put("lastName", lastName);
+ json.put("firstName", firstName);
+ json.put("middleName", middleName);
+ json.put("prefix", prefix);
+ json.put("suffix", suffix);
+ } catch (JSONException e) {
+ // do nothing
+ }
+
+ return json.toString();
+ }
+
+ static public String jsonifyVcardEntry(VCardEntry vcard) {
+ JSONObject json = new JSONObject();
+
+ try {
+ NameData name = vcard.getNameData();
+ json.put("formatted", name.getFormatted());
+ json.put("family", name.getFamily());
+ json.put("given", name.getGiven());
+ json.put("middle", name.getMiddle());
+ json.put("prefix", name.getPrefix());
+ json.put("suffix", name.getSuffix());
+ } catch (JSONException e) {
+ // do nothing
+ }
+
+ try {
+ JSONArray jsonPhones = new JSONArray();
+
+ List<PhoneData> phones = vcard.getPhoneList();
+
+ if (phones != null) {
+ for (PhoneData phone : phones) {
+ JSONObject jsonPhone = new JSONObject();
+ jsonPhone.put("type", phone.getType());
+ jsonPhone.put("number", phone.getNumber());
+ jsonPhone.put("label", phone.getLabel());
+ jsonPhone.put("is_primary", phone.isPrimary());
+
+ jsonPhones.put(jsonPhone);
+ }
+
+ json.put("phones", jsonPhones);
+ }
+ } catch (JSONException e) {
+ // do nothing
+ }
+
+ try {
+ JSONArray jsonEmails = new JSONArray();
+
+ List<EmailData> emails = vcard.getEmailList();
+
+ if (emails != null) {
+ for (EmailData email : emails) {
+ JSONObject jsonEmail = new JSONObject();
+ jsonEmail.put("type", email.getType());
+ jsonEmail.put("address", email.getAddress());
+ jsonEmail.put("label", email.getLabel());
+ jsonEmail.put("is_primary", email.isPrimary());
+
+ jsonEmails.put(jsonEmail);
+ }
+
+ json.put("emails", jsonEmails);
+ }
+ } catch (JSONException e) {
+ // do nothing
+ }
+
+ return json.toString();
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapClient.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapClient.java
new file mode 100644
index 0000000..6103928
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapClient.java
@@ -0,0 +1,857 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.bluetooth.BluetoothDevice;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Internal API to control Phone Book Profile (PCE role only).
+ * <p>
+ * This class defines methods that shall be used by application for the
+ * retrieval of phone book objects from remote device.
+ * <p>
+ * How to connect to remote device which is acting in PSE role:
+ * <ul>
+ * <li>Create a <code>BluetoothDevice</code> object which corresponds to remote
+ * device in PSE role;
+ * <li>Create an instance of <code>BluetoothPbapClient</code> class, passing
+ * <code>BluetothDevice</code> object along with a <code>Handler</code> to it;
+ * <li>Use {@link #setPhoneBookFolderRoot}, {@link #setPhoneBookFolderUp} and
+ * {@link #setPhoneBookFolderDown} to navigate in virtual phone book folder
+ * structure
+ * <li>Use {@link #pullPhoneBookSize} or {@link #pullVcardListingSize} to
+ * retrieve the size of selected phone book
+ * <li>Use {@link #pullPhoneBook} to retrieve phone book entries
+ * <li>Use {@link #pullVcardListing} to retrieve list of entries in the phone
+ * book
+ * <li>Use {@link #pullVcardEntry} to pull single entry from the phone book
+ * </ul>
+ * Upon completion of each call above PCE will notify application if operation
+ * completed successfully (along with results) or failed.
+ * <p>
+ * Therefore, application should handle following events in its message queue
+ * handler:
+ * <ul>
+ * <li><code>EVENT_PULL_PHONE_BOOK_SIZE_DONE</code>
+ * <li><code>EVENT_PULL_VCARD_LISTING_SIZE_DONE</code>
+ * <li><code>EVENT_PULL_PHONE_BOOK_DONE</code>
+ * <li><code>EVENT_PULL_VCARD_LISTING_DONE</code>
+ * <li><code>EVENT_PULL_VCARD_ENTRY_DONE</code>
+ * <li><code>EVENT_SET_PHONE_BOOK_DONE</code>
+ * </ul>
+ * and
+ * <ul>
+ * <li><code>EVENT_PULL_PHONE_BOOK_SIZE_ERROR</code>
+ * <li><code>EVENT_PULL_VCARD_LISTING_SIZE_ERROR</code>
+ * <li><code>EVENT_PULL_PHONE_BOOK_ERROR</code>
+ * <li><code>EVENT_PULL_VCARD_LISTING_ERROR</code>
+ * <li><code>EVENT_PULL_VCARD_ENTRY_ERROR</code>
+ * <li><code>EVENT_SET_PHONE_BOOK_ERROR</code>
+ * </ul>
+ * <code>connect</code> and <code>disconnect</code> methods are introduced for
+ * testing purposes. An application does not need to use them as the session
+ * connection and disconnection happens automatically internally.
+ */
+public class BluetoothPbapClient {
+ private static final boolean DBG = true;
+
+ private static final String TAG = "BluetoothPbapClient";
+
+ /**
+ * Path to local incoming calls history object
+ */
+ public static final String ICH_PATH = "telecom/ich.vcf";
+
+ /**
+ * Path to local outgoing calls history object
+ */
+ public static final String OCH_PATH = "telecom/och.vcf";
+
+ /**
+ * Path to local missed calls history object
+ */
+ public static final String MCH_PATH = "telecom/mch.vcf";
+
+ /**
+ * Path to local combined calls history object
+ */
+ public static final String CCH_PATH = "telecom/cch.vcf";
+
+ /**
+ * Path to local main phone book object
+ */
+ public static final String PB_PATH = "telecom/pb.vcf";
+
+ /**
+ * Path to incoming calls history object stored on the phone's SIM card
+ */
+ public static final String SIM_ICH_PATH = "SIM1/telecom/ich.vcf";
+
+ /**
+ * Path to outgoing calls history object stored on the phone's SIM card
+ */
+ public static final String SIM_OCH_PATH = "SIM1/telecom/och.vcf";
+
+ /**
+ * Path to missed calls history object stored on the phone's SIM card
+ */
+ public static final String SIM_MCH_PATH = "SIM1/telecom/mch.vcf";
+
+ /**
+ * Path to combined calls history object stored on the phone's SIM card
+ */
+ public static final String SIM_CCH_PATH = "SIM1/telecom/cch.vcf";
+
+ /**
+ * Path to main phone book object stored on the phone's SIM card
+ */
+ public static final String SIM_PB_PATH = "SIM1/telecom/pb.vcf";
+
+ /**
+ * Indicates to server that default sorting order shall be used for vCard
+ * listing.
+ */
+ public static final byte ORDER_BY_DEFAULT = -1;
+
+ /**
+ * Indicates to server that indexed sorting order shall be used for vCard
+ * listing.
+ */
+ public static final byte ORDER_BY_INDEXED = 0;
+
+ /**
+ * Indicates to server that alphabetical sorting order shall be used for the
+ * vCard listing.
+ */
+ public static final byte ORDER_BY_ALPHABETICAL = 1;
+
+ /**
+ * Indicates to server that phonetical (based on sound attribute) sorting
+ * order shall be used for the vCard listing.
+ */
+ public static final byte ORDER_BY_PHONETIC = 2;
+
+ /**
+ * Indicates to server that Name attribute of vCard shall be used to carry
+ * out the search operation on
+ */
+ public static final byte SEARCH_ATTR_NAME = 0;
+
+ /**
+ * Indicates to server that Number attribute of vCard shall be used to carry
+ * out the search operation on
+ */
+ public static final byte SEARCH_ATTR_NUMBER = 1;
+
+ /**
+ * Indicates to server that Sound attribute of vCard shall be used to carry
+ * out the search operation
+ */
+ public static final byte SEARCH_ATTR_SOUND = 2;
+
+ /**
+ * VCard format version 2.1
+ */
+ public static final byte VCARD_TYPE_21 = 0;
+
+ /**
+ * VCard format version 3.0
+ */
+ public static final byte VCARD_TYPE_30 = 1;
+
+ /* 64-bit mask used to filter out VCard fields */
+ // TODO: Think of extracting to separate class
+ public static final long VCARD_ATTR_VERSION = 0x000000000000000001;
+ public static final long VCARD_ATTR_FN = 0x000000000000000002;
+ public static final long VCARD_ATTR_N = 0x000000000000000004;
+ public static final long VCARD_ATTR_PHOTO = 0x000000000000000008;
+ public static final long VCARD_ATTR_BDAY = 0x000000000000000010;
+ public static final long VCARD_ATTR_ADDR = 0x000000000000000020;
+ public static final long VCARD_ATTR_LABEL = 0x000000000000000040;
+ public static final long VCARD_ATTR_TEL = 0x000000000000000080;
+ public static final long VCARD_ATTR_EMAIL = 0x000000000000000100;
+ public static final long VCARD_ATTR_MAILER = 0x000000000000000200;
+ public static final long VCARD_ATTR_TZ = 0x000000000000000400;
+ public static final long VCARD_ATTR_GEO = 0x000000000000000800;
+ public static final long VCARD_ATTR_TITLE = 0x000000000000001000;
+ public static final long VCARD_ATTR_ROLE = 0x000000000000002000;
+ public static final long VCARD_ATTR_LOGO = 0x000000000000004000;
+ public static final long VCARD_ATTR_AGENT = 0x000000000000008000;
+ public static final long VCARD_ATTR_ORG = 0x000000000000010000;
+ public static final long VCARD_ATTR_NOTE = 0x000000000000020000;
+ public static final long VCARD_ATTR_REV = 0x000000000000040000;
+ public static final long VCARD_ATTR_SOUND = 0x000000000000080000;
+ public static final long VCARD_ATTR_URL = 0x000000000000100000;
+ public static final long VCARD_ATTR_UID = 0x000000000000200000;
+ public static final long VCARD_ATTR_KEY = 0x000000000000400000;
+ public static final long VCARD_ATTR_NICKNAME = 0x000000000000800000;
+ public static final long VCARD_ATTR_CATEGORIES = 0x000000000001000000;
+ public static final long VCARD_ATTR_PROID = 0x000000000002000000;
+ public static final long VCARD_ATTR_CLASS = 0x000000000004000000;
+ public static final long VCARD_ATTR_SORT_STRING = 0x000000000008000000;
+ public static final long VCARD_ATTR_X_IRMC_CALL_DATETIME =
+ 0x000000000010000000;
+
+ /**
+ * Maximal number of entries of the phone book that PCE can handle
+ */
+ public static final short MAX_LIST_COUNT = (short) 0xFFFF;
+
+ /**
+ * Event propagated upon completion of <code>setPhoneBookFolderRoot</code>,
+ * <code>setPhoneBookFolderUp</code> or <code>setPhoneBookFolderDown</code>
+ * request.
+ * <p>
+ * This event indicates that request completed successfully.
+ * @see #setPhoneBookFolderRoot
+ * @see #setPhoneBookFolderUp
+ * @see #setPhoneBookFolderDown
+ */
+ public static final int EVENT_SET_PHONE_BOOK_DONE = 1;
+
+ /**
+ * Event propagated upon completion of <code>pullPhoneBook</code> request.
+ * <p>
+ * This event carry on results of the request.
+ * <p>
+ * The resulting message contains:
+ * <table>
+ * <tr>
+ * <td><code>msg.arg1</code></td>
+ * <td>newMissedCalls parameter (only in case of missed calls history object
+ * request)</td>
+ * </tr>
+ * <tr>
+ * <td><code>msg.obj</code></td>
+ * <td>which is a list of <code>VCardEntry</code> objects</td>
+ * </tr>
+ * </table>
+ * @see #pullPhoneBook
+ */
+ public static final int EVENT_PULL_PHONE_BOOK_DONE = 2;
+
+ /**
+ * Event propagated upon completion of <code>pullVcardListing</code>
+ * request.
+ * <p>
+ * This event carry on results of the request.
+ * <p>
+ * The resulting message contains:
+ * <table>
+ * <tr>
+ * <td><code>msg.arg1</code></td>
+ * <td>newMissedCalls parameter (only in case of missed calls history object
+ * request)</td>
+ * </tr>
+ * <tr>
+ * <td><code>msg.obj</code></td>
+ * <td>which is a list of <code>BluetoothPbapCard</code> objects</td>
+ * </tr>
+ * </table>
+ * @see #pullVcardListing
+ */
+ public static final int EVENT_PULL_VCARD_LISTING_DONE = 3;
+
+ /**
+ * Event propagated upon completion of <code>pullVcardEntry</code> request.
+ * <p>
+ * This event carry on results of the request.
+ * <p>
+ * The resulting message contains:
+ * <table>
+ * <tr>
+ * <td><code>msg.obj</code></td>
+ * <td>vCard as and object of type <code>VCardEntry</code></td>
+ * </tr>
+ * </table>
+ * @see #pullVcardEntry
+ */
+ public static final int EVENT_PULL_VCARD_ENTRY_DONE = 4;
+
+ /**
+ * Event propagated upon completion of <code>pullPhoneBookSize</code>
+ * request.
+ * <p>
+ * This event carry on results of the request.
+ * <p>
+ * The resulting message contains:
+ * <table>
+ * <tr>
+ * <td><code>msg.arg1</code></td>
+ * <td>size of the phone book</td>
+ * </tr>
+ * </table>
+ * @see #pullPhoneBookSize
+ */
+ public static final int EVENT_PULL_PHONE_BOOK_SIZE_DONE = 5;
+
+ /**
+ * Event propagated upon completion of <code>pullVcardListingSize</code>
+ * request.
+ * <p>
+ * This event carry on results of the request.
+ * <p>
+ * The resulting message contains:
+ * <table>
+ * <tr>
+ * <td><code>msg.arg1</code></td>
+ * <td>size of the phone book listing</td>
+ * </tr>
+ * </table>
+ * @see #pullVcardListingSize
+ */
+ public static final int EVENT_PULL_VCARD_LISTING_SIZE_DONE = 6;
+
+ /**
+ * Event propagated upon completion of <code>setPhoneBookFolderRoot</code>,
+ * <code>setPhoneBookFolderUp</code> or <code>setPhoneBookFolderDown</code>
+ * request. This event indicates an error during operation.
+ */
+ public static final int EVENT_SET_PHONE_BOOK_ERROR = 101;
+
+ /**
+ * Event propagated upon completion of <code>pullPhoneBook</code> request.
+ * This event indicates an error during operation.
+ */
+ public static final int EVENT_PULL_PHONE_BOOK_ERROR = 102;
+
+ /**
+ * Event propagated upon completion of <code>pullVcardListing</code>
+ * request. This event indicates an error during operation.
+ */
+ public static final int EVENT_PULL_VCARD_LISTING_ERROR = 103;
+
+ /**
+ * Event propagated upon completion of <code>pullVcardEntry</code> request.
+ * This event indicates an error during operation.
+ */
+ public static final int EVENT_PULL_VCARD_ENTRY_ERROR = 104;
+
+ /**
+ * Event propagated upon completion of <code>pullPhoneBookSize</code>
+ * request. This event indicates an error during operation.
+ */
+ public static final int EVENT_PULL_PHONE_BOOK_SIZE_ERROR = 105;
+
+ /**
+ * Event propagated upon completion of <code>pullVcardListingSize</code>
+ * request. This event indicates an error during operation.
+ */
+ public static final int EVENT_PULL_VCARD_LISTING_SIZE_ERROR = 106;
+
+ /**
+ * Event propagated when PCE has been connected to PSE
+ */
+ public static final int EVENT_SESSION_CONNECTED = 201;
+
+ /**
+ * Event propagated when PCE has been disconnected from PSE
+ */
+ public static final int EVENT_SESSION_DISCONNECTED = 202;
+ public static final int EVENT_SESSION_AUTH_REQUESTED = 203;
+ public static final int EVENT_SESSION_AUTH_TIMEOUT = 204;
+
+ public enum ConnectionState {
+ DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING;
+ }
+
+ private final Account mAccount;
+ private final Handler mClientHandler;
+ private final BluetoothPbapSession mSession;
+ private ConnectionState mConnectionState = ConnectionState.DISCONNECTED;
+
+ private SessionHandler mSessionHandler;
+
+ private static class SessionHandler extends Handler {
+
+ private final WeakReference<BluetoothPbapClient> mClient;
+
+ SessionHandler(BluetoothPbapClient client) {
+ mClient = new WeakReference<BluetoothPbapClient>(client);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "handleMessage: what=" + msg.what);
+
+ BluetoothPbapClient client = mClient.get();
+ if (client == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case BluetoothPbapSession.REQUEST_FAILED:
+ {
+ BluetoothPbapRequest req = (BluetoothPbapRequest) msg.obj;
+
+ if (req instanceof BluetoothPbapRequestPullPhoneBookSize) {
+ client.sendToClient(EVENT_PULL_PHONE_BOOK_SIZE_ERROR);
+ } else if (req instanceof BluetoothPbapRequestPullVcardListingSize) {
+ client.sendToClient(EVENT_PULL_VCARD_LISTING_SIZE_ERROR);
+ } else if (req instanceof BluetoothPbapRequestPullPhoneBook) {
+ client.sendToClient(EVENT_PULL_PHONE_BOOK_ERROR);
+ } else if (req instanceof BluetoothPbapRequestPullVcardListing) {
+ client.sendToClient(EVENT_PULL_VCARD_LISTING_ERROR);
+ } else if (req instanceof BluetoothPbapRequestPullVcardEntry) {
+ client.sendToClient(EVENT_PULL_VCARD_ENTRY_ERROR);
+ } else if (req instanceof BluetoothPbapRequestSetPath) {
+ client.sendToClient(EVENT_SET_PHONE_BOOK_ERROR);
+ }
+
+ break;
+ }
+
+ case BluetoothPbapSession.REQUEST_COMPLETED:
+ {
+ BluetoothPbapRequest req = (BluetoothPbapRequest) msg.obj;
+
+ if (req instanceof BluetoothPbapRequestPullPhoneBookSize) {
+ int size = ((BluetoothPbapRequestPullPhoneBookSize) req).getSize();
+ client.sendToClient(EVENT_PULL_PHONE_BOOK_SIZE_DONE, size);
+
+ } else if (req instanceof BluetoothPbapRequestPullVcardListingSize) {
+ int size = ((BluetoothPbapRequestPullVcardListingSize) req).getSize();
+ client.sendToClient(EVENT_PULL_VCARD_LISTING_SIZE_DONE, size);
+
+ } else if (req instanceof BluetoothPbapRequestPullPhoneBook) {
+ BluetoothPbapRequestPullPhoneBook r = (BluetoothPbapRequestPullPhoneBook) req;
+ client.sendToClient(EVENT_PULL_PHONE_BOOK_DONE, r.getNewMissedCalls(),
+ r.getList());
+
+ } else if (req instanceof BluetoothPbapRequestPullVcardListing) {
+ BluetoothPbapRequestPullVcardListing r = (BluetoothPbapRequestPullVcardListing) req;
+ client.sendToClient(EVENT_PULL_VCARD_LISTING_DONE, r.getNewMissedCalls(),
+ r.getList());
+
+ } else if (req instanceof BluetoothPbapRequestPullVcardEntry) {
+ BluetoothPbapRequestPullVcardEntry r = (BluetoothPbapRequestPullVcardEntry) req;
+ client.sendToClient(EVENT_PULL_VCARD_ENTRY_DONE, r.getVcard());
+
+ } else if (req instanceof BluetoothPbapRequestSetPath) {
+ client.sendToClient(EVENT_SET_PHONE_BOOK_DONE);
+ }
+
+ break;
+ }
+
+ case BluetoothPbapSession.AUTH_REQUESTED:
+ client.sendToClient(EVENT_SESSION_AUTH_REQUESTED);
+ break;
+
+ case BluetoothPbapSession.AUTH_TIMEOUT:
+ client.sendToClient(EVENT_SESSION_AUTH_TIMEOUT);
+ break;
+
+ /*
+ * app does not need to know when session is connected since
+ * OBEX session is managed inside BluetoothPbapSession
+ * automatically - we add this only so app can visualize PBAP
+ * connection status in case it wants to
+ */
+
+ case BluetoothPbapSession.SESSION_CONNECTING:
+ client.mConnectionState = ConnectionState.CONNECTING;
+ break;
+
+ case BluetoothPbapSession.SESSION_CONNECTED:
+ client.mConnectionState = ConnectionState.CONNECTED;
+ client.sendToClient(EVENT_SESSION_CONNECTED);
+ break;
+
+ case BluetoothPbapSession.SESSION_DISCONNECTED:
+ client.mConnectionState = ConnectionState.DISCONNECTED;
+ client.sendToClient(EVENT_SESSION_DISCONNECTED);
+ break;
+ }
+ }
+ };
+
+ private void sendToClient(int eventId) {
+ sendToClient(eventId, 0, null);
+ }
+
+ private void sendToClient(int eventId, int param) {
+ sendToClient(eventId, param, null);
+ }
+
+ private void sendToClient(int eventId, Object param) {
+ sendToClient(eventId, 0, param);
+ }
+
+ private void sendToClient(int eventId, int param1, Object param2) {
+ mClientHandler.obtainMessage(eventId, param1, 0, param2).sendToTarget();
+ }
+
+ /**
+ * Constructs PCE object
+ *
+ * @param device BluetoothDevice that corresponds to remote acting in PSE
+ * role
+ * @param account the account to which contacts will be added {@see #pullPhoneBook}.
+ * @param handler the handle that will be used by PCE to notify events and
+ * results to application
+ * @throws NullPointerException
+ */
+ public BluetoothPbapClient(BluetoothDevice device, Account account, Handler handler) {
+ if (DBG) {
+ Log.d(TAG, " device " + device + " account " + account);
+ }
+ if (device == null) {
+ throw new NullPointerException("BluetoothDevice is null");
+ }
+
+ mAccount = account;
+
+ mClientHandler = handler;
+
+ mSessionHandler = new SessionHandler(this);
+
+ mSession = new BluetoothPbapSession(device, mSessionHandler);
+ }
+
+ /**
+ * Starts a pbap session. <pb> This method set up rfcomm session, obex
+ * session and waits for requests to be transfered to PSE.
+ */
+ public void connect() {
+ mSession.start();
+ }
+
+ @Override
+ public void finalize() {
+ if (mSession != null) {
+ mSession.stop();
+ }
+ }
+
+ /**
+ * Stops all the active transactions and disconnects from the server.
+ */
+ public void disconnect() {
+ mSession.stop();
+ }
+
+ /**
+ * Aborts current request, if any
+ */
+ public void abort() {
+ mSession.abort();
+ }
+
+ public ConnectionState getState() {
+ return mConnectionState;
+ }
+
+ /**
+ * Sets current folder to root
+ *
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_SET_PHONE_BOOK_DONE} or
+ * {@link #EVENT_SET_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean setPhoneBookFolderRoot() {
+ BluetoothPbapRequest req = new BluetoothPbapRequestSetPath(false);
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Sets current folder to parent
+ *
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_SET_PHONE_BOOK_DONE} or
+ * {@link #EVENT_SET_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean setPhoneBookFolderUp() {
+ BluetoothPbapRequest req = new BluetoothPbapRequestSetPath(true);
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Sets current folder to selected sub-folder
+ *
+ * @param folder the name of the sub-folder
+ * @return @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_SET_PHONE_BOOK_DONE} or
+ * {@link #EVENT_SET_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean setPhoneBookFolderDown(String folder) {
+ BluetoothPbapRequest req = new BluetoothPbapRequestSetPath(folder);
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Requests for the number of entries in the phone book.
+ *
+ * @param pbName absolute path to the phone book
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_PHONE_BOOK_SIZE_DONE} or
+ * {@link #EVENT_PULL_PHONE_BOOK_SIZE_ERROR} in case of failure
+ */
+ public boolean pullPhoneBookSize(String pbName) {
+ BluetoothPbapRequestPullPhoneBookSize req = new BluetoothPbapRequestPullPhoneBookSize(
+ pbName);
+
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Requests for the number of entries in the phone book listing.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_SIZE_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_SIZE_ERROR} in case of failure
+ */
+ public boolean pullVcardListingSize(String folder) {
+ BluetoothPbapRequestPullVcardListingSize req = new BluetoothPbapRequestPullVcardListingSize(
+ folder);
+
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Pulls complete phone book. This method pulls phone book which entries are
+ * of <code>VCARD_TYPE_21</code> type and each single vCard contains minimal
+ * required set of fields and the number of entries in response is not
+ * limited.
+ *
+ * @param pbName absolute path to the phone book
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_PHONE_BOOK_DONE} or
+ * {@link #EVENT_PULL_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean pullPhoneBook(String pbName) {
+ return pullPhoneBook(pbName, 0, VCARD_TYPE_21, 0, 0);
+ }
+
+ /**
+ * Pulls complete phone book. This method pulls all entries from the phone
+ * book.
+ *
+ * @param pbName absolute path to the phone book
+ * @param filter bit mask which indicates which fields of the vCard shall be
+ * included in each entry of the resulting list
+ * @param format vCard format of entries in the resulting list
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_PHONE_BOOK_DONE} or
+ * {@link #EVENT_PULL_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean pullPhoneBook(String pbName, long filter, byte format) {
+ return pullPhoneBook(pbName, filter, format, 0, 0);
+ }
+
+ /**
+ * Pulls complete phone book. This method pulls entries from the phone book
+ * limited to the number of <code>maxListCount</code> starting from the
+ * position of <code>listStartOffset</code>.
+ * <p>
+ * The resulting list contains vCard objects in version
+ * <code>VCARD_TYPE_21</code> which in turns contain minimal required set of
+ * vCard fields.
+ *
+ * @param pbName absolute path to the phone book
+ * @param maxListCount limits number of entries in the response
+ * @param listStartOffset offset to the first entry of the list that would
+ * be returned
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_PHONE_BOOK_DONE} or
+ * {@link #EVENT_PULL_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean pullPhoneBook(String pbName, int maxListCount, int listStartOffset) {
+ return pullPhoneBook(pbName, 0, VCARD_TYPE_21, maxListCount, listStartOffset);
+ }
+
+ /**
+ * Pulls complete phone book.
+ *
+ * @param pbName absolute path to the phone book
+ * @param filter bit mask which indicates which fields of the vCard hall be
+ * included in each entry of the resulting list
+ * @param format vCard format of entries in the resulting list
+ * @param maxListCount limits number of entries in the response
+ * @param listStartOffset offset to the first entry of the list that would
+ * be returned
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_PHONE_BOOK_DONE} or
+ * {@link #EVENT_PULL_PHONE_BOOK_ERROR} in case of failure
+ */
+ public boolean pullPhoneBook(String pbName, long filter, byte format, int maxListCount,
+ int listStartOffset) {
+ BluetoothPbapRequest req = new BluetoothPbapRequestPullPhoneBook(
+ pbName, mAccount, filter, format, maxListCount, listStartOffset);
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Pulls list of entries in the phone book.
+ * <p>
+ * This method pulls the list of entries in the <code>folder</code>.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_ERROR} in case of failure
+ */
+ public boolean pullVcardListing(String folder) {
+ return pullVcardListing(folder, ORDER_BY_DEFAULT, SEARCH_ATTR_NAME, null, 0, 0);
+ }
+
+ /**
+ * Pulls list of entries in the <code>folder</code>.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @param order the sorting order of the resulting list of entries
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_ERROR} in case of failure
+ */
+ public boolean pullVcardListing(String folder, byte order) {
+ return pullVcardListing(folder, order, SEARCH_ATTR_NAME, null, 0, 0);
+ }
+
+ /**
+ * Pulls list of entries in the <code>folder</code>. Only entries where
+ * <code>searchAttr</code> attribute of vCard matches <code>searchVal</code>
+ * will be listed.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @param searchAttr vCard attribute which shall be used to carry out search
+ * operation on
+ * @param searchVal text string used by matching routine to match the value
+ * of the attribute indicated by SearchAttr
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_ERROR} in case of failure
+ */
+ public boolean pullVcardListing(String folder, byte searchAttr, String searchVal) {
+ return pullVcardListing(folder, ORDER_BY_DEFAULT, searchAttr, searchVal, 0, 0);
+ }
+
+ /**
+ * Pulls list of entries in the <code>folder</code>.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @param order the sorting order of the resulting list of entries
+ * @param maxListCount limits number of entries in the response
+ * @param listStartOffset offset to the first entry of the list that would
+ * be returned
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_ERROR} in case of failure
+ */
+ public boolean pullVcardListing(String folder, byte order, int maxListCount,
+ int listStartOffset) {
+ return pullVcardListing(folder, order, SEARCH_ATTR_NAME, null, maxListCount,
+ listStartOffset);
+ }
+
+ /**
+ * Pulls list of entries in the <code>folder</code>.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @param maxListCount limits number of entries in the response
+ * @param listStartOffset offset to the first entry of the list that would
+ * be returned
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_ERROR} in case of failure
+ */
+ public boolean pullVcardListing(String folder, int maxListCount, int listStartOffset) {
+ return pullVcardListing(folder, ORDER_BY_DEFAULT, SEARCH_ATTR_NAME, null, maxListCount,
+ listStartOffset);
+ }
+
+ /**
+ * Pulls list of entries in the <code>folder</code>.
+ *
+ * @param folder the name of the folder to be retrieved
+ * @param order the sorting order of the resulting list of entries
+ * @param searchAttr vCard attribute which shall be used to carry out search
+ * operation on
+ * @param searchVal text string used by matching routine to match the value
+ * of the attribute indicated by SearchAttr
+ * @param maxListCount limits number of entries in the response
+ * @param listStartOffset offset to the first entry of the list that would
+ * be returned
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_LISTING_DONE} or
+ * {@link #EVENT_PULL_VCARD_LISTING_ERROR} in case of failure
+ */
+ public boolean pullVcardListing(String folder, byte order, byte searchAttr,
+ String searchVal, int maxListCount, int listStartOffset) {
+ BluetoothPbapRequest req = new BluetoothPbapRequestPullVcardListing(folder, order,
+ searchAttr, searchVal, maxListCount, listStartOffset);
+ return mSession.makeRequest(req);
+ }
+
+ /**
+ * Pulls single vCard object
+ *
+ * @param handle handle to the vCard which shall be pulled
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_DONE} or
+ * @link #EVENT_PULL_VCARD_ERROR} in case of failure
+ */
+ public boolean pullVcardEntry(String handle) {
+ return pullVcardEntry(handle, (byte) 0, VCARD_TYPE_21);
+ }
+
+ /**
+ * Pulls single vCard object
+ *
+ * @param handle handle to the vCard which shall be pulled
+ * @param filter bit mask of the vCard fields that shall be included in the
+ * resulting vCard
+ * @param format resulting vCard version
+ * @return <code>true</code> if request has been sent successfully;
+ * <code>false</code> otherwise; upon completion PCE sends
+ * {@link #EVENT_PULL_VCARD_DONE}
+ * @link #EVENT_PULL_VCARD_ERROR} in case of failure
+ */
+ public boolean pullVcardEntry(String handle, long filter, byte format) {
+ BluetoothPbapRequest req =
+ new BluetoothPbapRequestPullVcardEntry(handle, mAccount, filter, format);
+ return mSession.makeRequest(req);
+ }
+
+ public boolean setAuthResponse(String key) {
+ Log.d(TAG, " setAuthResponse key=" + key);
+ return mSession.setAuthResponse(key);
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticator.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticator.java
new file mode 100644
index 0000000..e471d15
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.os.Handler;
+import android.util.Log;
+
+import javax.obex.Authenticator;
+import javax.obex.PasswordAuthentication;
+
+class BluetoothPbapObexAuthenticator implements Authenticator {
+
+ private final static String TAG = "BluetoothPbapObexAuthenticator";
+
+ private String mSessionKey;
+
+ private boolean mReplied;
+
+ private final Handler mCallback;
+
+ public BluetoothPbapObexAuthenticator(Handler callback) {
+ mCallback = callback;
+ }
+
+ public synchronized void setReply(String key) {
+ Log.d(TAG, "setReply key=" + key);
+
+ mSessionKey = key;
+ mReplied = true;
+
+ notify();
+ }
+
+ @Override
+ public PasswordAuthentication onAuthenticationChallenge(String description,
+ boolean isUserIdRequired, boolean isFullAccess) {
+ PasswordAuthentication pa = null;
+
+ mReplied = false;
+
+ Log.d(TAG, "onAuthenticationChallenge: sending request");
+ mCallback.obtainMessage(BluetoothPbapObexSession.OBEX_SESSION_AUTHENTICATION_REQUEST)
+ .sendToTarget();
+
+ synchronized (this) {
+ while (!mReplied) {
+ try {
+ Log.v(TAG, "onAuthenticationChallenge: waiting for response");
+ this.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while waiting for challenge response");
+ }
+ }
+ }
+
+ if (mSessionKey != null && mSessionKey.length() != 0) {
+ Log.v(TAG, "onAuthenticationChallenge: mSessionKey=" + mSessionKey);
+ pa = new PasswordAuthentication(null, mSessionKey.getBytes());
+ } else {
+ Log.v(TAG, "onAuthenticationChallenge: mSessionKey is empty, timeout/cancel occured");
+ }
+
+ return pa;
+ }
+
+ @Override
+ public byte[] onAuthenticationResponse(byte[] userName) {
+ /* required only in case PCE challenges PSE which we don't do now */
+ return null;
+ }
+
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapObexSession.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapObexSession.java
new file mode 100644
index 0000000..55393d5
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapObexSession.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+
+import javax.obex.ClientSession;
+import javax.obex.HeaderSet;
+import javax.obex.ObexTransport;
+import javax.obex.ResponseCodes;
+
+final class BluetoothPbapObexSession {
+ private static final boolean DBG = true;
+ private static final String TAG = "BluetoothPbapObexSession";
+
+ private static final byte[] PBAP_TARGET = new byte[] {
+ 0x79, 0x61, 0x35, (byte) 0xf0, (byte) 0xf0, (byte) 0xc5, 0x11, (byte) 0xd8, 0x09, 0x66,
+ 0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66
+ };
+
+ final static int OBEX_SESSION_CONNECTED = 100;
+ final static int OBEX_SESSION_FAILED = 101;
+ final static int OBEX_SESSION_DISCONNECTED = 102;
+ final static int OBEX_SESSION_REQUEST_COMPLETED = 103;
+ final static int OBEX_SESSION_REQUEST_FAILED = 104;
+ final static int OBEX_SESSION_AUTHENTICATION_REQUEST = 105;
+ final static int OBEX_SESSION_AUTHENTICATION_TIMEOUT = 106;
+
+ final static int MSG_CONNECT = 0;
+ final static int MSG_REQUEST = 1;
+
+ final static int CONNECTED = 0;
+ final static int CONNECTING = 1;
+ final static int DISCONNECTED = 2;
+
+ private Handler mSessionHandler;
+ private final ObexTransport mTransport;
+ // private ObexClientThread mObexClientThread;
+ private BluetoothPbapObexAuthenticator mAuth = null;
+ private HandlerThread mThread;
+ private Handler mHandler;
+ private ClientSession mClientSession;
+
+ private int mState = DISCONNECTED;
+
+ public BluetoothPbapObexSession(ObexTransport transport) {
+ mTransport = transport;
+ }
+
+ public synchronized boolean start(Handler handler) {
+ Log.d(TAG, "start");
+
+ if (mState == CONNECTED || mState == CONNECTING) {
+ return false;
+ }
+ mState = CONNECTING;
+ mSessionHandler = handler;
+
+ mAuth = new BluetoothPbapObexAuthenticator(mSessionHandler);
+
+ // Start the thread to process requests (see {@link schedule()}.
+ mThread = new HandlerThread("BluetoothPbapObexSessionThread");
+ mThread.start();
+ mHandler = new ObexClientHandler(mThread.getLooper(), this);
+
+ // Make connect call non blocking.
+ boolean status = mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT));
+ if (!status) {
+ mState = DISCONNECTED;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public void stop() {
+ if (DBG) {
+ Log.d(TAG, "stop");
+ }
+
+ // This will essentially stop the handler and ignore any inflight requests.
+ mThread.quit();
+
+ // We clean up the state here.
+ disconnect(false /* no callback */);
+ }
+
+ public void abort() {
+ stop();
+ }
+
+ public boolean schedule(BluetoothPbapRequest request) {
+ if (DBG) {
+ Log.d(TAG, "schedule() called with: " + request);
+ }
+
+ boolean status = mHandler.sendMessage(mHandler.obtainMessage(MSG_REQUEST, request));
+ if (!status) {
+ Log.e(TAG, "Adding messages failed, obex must be disconnected.");
+ return false;
+ }
+ return true;
+ }
+
+ public int isConnected() {
+ return mState;
+ }
+
+ private void connect() {
+ if (DBG) {
+ Log.d(TAG, "connect()");
+ }
+
+ boolean success = true;
+ try {
+ mClientSession = new ClientSession(mTransport);
+ mClientSession.setAuthenticator(mAuth);
+ } catch (IOException e) {
+ Log.d(TAG, "connect() exception: " + e);
+ success = false;
+ }
+
+ HeaderSet hs = new HeaderSet();
+ hs.setHeader(HeaderSet.TARGET, PBAP_TARGET);
+ try {
+ hs = mClientSession.connect(hs);
+
+ if (hs.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
+ disconnect(true /* callback */);
+ success = false;
+ }
+ } catch (IOException e) {
+ success = false;
+ }
+
+ synchronized (this) {
+ if (success) {
+ mSessionHandler.obtainMessage(OBEX_SESSION_CONNECTED).sendToTarget();
+ mState = CONNECTED;
+ } else {
+ mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget();
+ mState = DISCONNECTED;
+ }
+ }
+ }
+
+ private synchronized void disconnect(boolean callback) {
+ if (DBG) {
+ Log.d(TAG, "disconnect()");
+ }
+
+ if (mState != DISCONNECTED) {
+ return;
+ }
+
+ if (mClientSession != null) {
+ try {
+ mClientSession.disconnect(null);
+ mClientSession.close();
+ } catch (IOException e) {
+ }
+ }
+
+ if (callback) {
+ mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget();
+ }
+
+ mState = DISCONNECTED;
+ }
+
+ private void executeRequest(BluetoothPbapRequest req) {
+ try {
+ req.execute(mClientSession);
+ } catch (IOException e) {
+ Log.e(TAG, "Error during executeRequest " + e);
+ disconnect(true);
+ }
+
+ if (req.isSuccess()) {
+ mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_COMPLETED, req).sendToTarget();
+ } else {
+ mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_FAILED, req).sendToTarget();
+ }
+ }
+
+ public boolean setAuthReply(String key) {
+ Log.d(TAG, "setAuthReply key=" + key);
+
+ if (mAuth == null) {
+ return false;
+ }
+
+ mAuth.setReply(key);
+
+ return true;
+ }
+
+ private static class ObexClientHandler extends Handler {
+ WeakReference<BluetoothPbapObexSession> mInst;
+
+ ObexClientHandler(Looper looper, BluetoothPbapObexSession inst) {
+ super(looper);
+ mInst = new WeakReference<BluetoothPbapObexSession>(inst);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ BluetoothPbapObexSession inst = mInst.get();
+ if (inst == null) {
+ Log.e(TAG, "The instance class is no longer around; terminating.");
+ return;
+ }
+
+ if (inst.isConnected() != CONNECTED && msg.what != MSG_CONNECT) {
+ Log.w(TAG, "Cannot execute " + msg + " when not CONNECTED.");
+ return;
+ }
+
+ switch (msg.what) {
+ case MSG_CONNECT:
+ inst.connect();
+ break;
+ case MSG_REQUEST:
+ inst.executeRequest((BluetoothPbapRequest) msg.obj);
+ break;
+ default:
+ Log.e(TAG, "Unknwown message type: " + msg.what);
+ }
+ }
+ }
+}
+
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapObexTransport.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapObexTransport.java
new file mode 100644
index 0000000..8c2ff80
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapObexTransport.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.bluetooth.BluetoothSocket;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.obex.ObexTransport;
+
+class BluetoothPbapObexTransport implements ObexTransport {
+
+ private BluetoothSocket mSocket = null;
+
+ public BluetoothPbapObexTransport(BluetoothSocket rfs) {
+ super();
+ mSocket = rfs;
+ }
+
+ @Override
+ public void close() throws IOException {
+ mSocket.close();
+ }
+
+ @Override
+ public DataInputStream openDataInputStream() throws IOException {
+ return new DataInputStream(openInputStream());
+ }
+
+ @Override
+ public DataOutputStream openDataOutputStream() throws IOException {
+ return new DataOutputStream(openOutputStream());
+ }
+
+ @Override
+ public InputStream openInputStream() throws IOException {
+ return mSocket.getInputStream();
+ }
+
+ @Override
+ public OutputStream openOutputStream() throws IOException {
+ return mSocket.getOutputStream();
+ }
+
+ @Override
+ public void connect() throws IOException {
+ }
+
+ @Override
+ public void create() throws IOException {
+ }
+
+ @Override
+ public void disconnect() throws IOException {
+ }
+
+ @Override
+ public void listen() throws IOException {
+ }
+
+ public boolean isConnected() throws IOException {
+ // return true;
+ return mSocket.isConnected();
+ }
+
+ @Override
+ public int getMaxTransmitPacketSize() {
+ return -1;
+ }
+
+ @Override
+ public int getMaxReceivePacketSize() {
+ return -1;
+ }
+
+ @Override
+ public boolean isSrmSupported() {
+ return false;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequest.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequest.java
new file mode 100644
index 0000000..249cb8f
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.obex.ClientOperation;
+import javax.obex.ClientSession;
+import javax.obex.HeaderSet;
+import javax.obex.ResponseCodes;
+
+abstract class BluetoothPbapRequest {
+
+ private static final String TAG = "BluetoothPbapRequest";
+
+ protected static final byte OAP_TAGID_ORDER = 0x01;
+ protected static final byte OAP_TAGID_SEARCH_VALUE = 0x02;
+ protected static final byte OAP_TAGID_SEARCH_ATTRIBUTE = 0x03;
+ protected static final byte OAP_TAGID_MAX_LIST_COUNT = 0x04;
+ protected static final byte OAP_TAGID_LIST_START_OFFSET = 0x05;
+ protected static final byte OAP_TAGID_FILTER = 0x06;
+ protected static final byte OAP_TAGID_FORMAT = 0x07;
+ protected static final byte OAP_TAGID_PHONEBOOK_SIZE = 0x08;
+ protected static final byte OAP_TAGID_NEW_MISSED_CALLS = 0x09;
+
+ protected HeaderSet mHeaderSet;
+
+ protected int mResponseCode;
+
+ private boolean mAborted = false;
+
+ private ClientOperation mOp = null;
+
+ public BluetoothPbapRequest() {
+ mHeaderSet = new HeaderSet();
+ }
+
+ final public boolean isSuccess() {
+ return (mResponseCode == ResponseCodes.OBEX_HTTP_OK);
+ }
+
+ public void execute(ClientSession session) throws IOException {
+ Log.v(TAG, "execute");
+
+ /* in case request is aborted before can be executed */
+ if (mAborted) {
+ mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ return;
+ }
+
+ try {
+ mOp = (ClientOperation) session.get(mHeaderSet);
+
+ /* make sure final flag for GET is used (PBAP spec 6.2.2) */
+ mOp.setGetFinalFlag(true);
+
+ /*
+ * this will trigger ClientOperation to use non-buffered stream so
+ * we can abort operation
+ */
+ mOp.continueOperation(true, false);
+
+ readResponseHeaders(mOp.getReceivedHeader());
+
+ InputStream is = mOp.openInputStream();
+ readResponse(is);
+ is.close();
+
+ mOp.close();
+
+ mResponseCode = mOp.getResponseCode();
+
+ Log.d(TAG, "mResponseCode=" + mResponseCode);
+
+ checkResponseCode(mResponseCode);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException occured when processing request", e);
+ mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+
+ throw e;
+ }
+ }
+
+ public void abort() {
+ mAborted = true;
+
+ if (mOp != null) {
+ try {
+ mOp.abort();
+ } catch (IOException e) {
+ Log.e(TAG, "Exception occured when trying to abort", e);
+ }
+ }
+ }
+
+ protected void readResponse(InputStream stream) throws IOException {
+ Log.v(TAG, "readResponse");
+
+ /* nothing here by default */
+ }
+
+ protected void readResponseHeaders(HeaderSet headerset) {
+ Log.v(TAG, "readResponseHeaders");
+
+ /* nothing here by dafault */
+ }
+
+ protected void checkResponseCode(int responseCode) throws IOException {
+ Log.v(TAG, "checkResponseCode");
+
+ /* nothing here by dafault */
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
new file mode 100644
index 0000000..6538bd5
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.util.Log;
+
+import com.android.vcard.VCardEntry;
+import com.android.bluetooth.pbapclient.utils.ObexAppParameters;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import javax.obex.HeaderSet;
+
+final class BluetoothPbapRequestPullPhoneBook extends BluetoothPbapRequest {
+
+ private static final boolean DBG = true;
+
+ private static final String TAG = "BluetoothPbapRequestPullPhoneBook";
+
+ private static final String TYPE = "x-bt/phonebook";
+
+ private BluetoothPbapVcardList mResponse;
+
+ private Account mAccount;
+
+ private int mNewMissedCalls = -1;
+
+ private final byte mFormat;
+
+ public BluetoothPbapRequestPullPhoneBook(
+ String pbName, Account account, long filter, byte format,
+ int maxListCount, int listStartOffset) {
+ mAccount = account;
+ if (maxListCount < 0 || maxListCount > 65535) {
+ throw new IllegalArgumentException("maxListCount should be [0..65535]");
+ }
+
+ if (listStartOffset < 0 || listStartOffset > 65535) {
+ throw new IllegalArgumentException("listStartOffset should be [0..65535]");
+ }
+
+ mHeaderSet.setHeader(HeaderSet.NAME, pbName);
+
+ mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+ ObexAppParameters oap = new ObexAppParameters();
+
+ /* make sure format is one of allowed values */
+ if (format != BluetoothPbapClient.VCARD_TYPE_21
+ && format != BluetoothPbapClient.VCARD_TYPE_30) {
+ format = BluetoothPbapClient.VCARD_TYPE_21;
+ }
+
+ if (filter != 0) {
+ oap.add(OAP_TAGID_FILTER, filter);
+ }
+
+ oap.add(OAP_TAGID_FORMAT, format);
+
+ /*
+ * maxListCount is a special case which is handled in
+ * BluetoothPbapRequestPullPhoneBookSize
+ */
+ if (maxListCount > 0) {
+ oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) maxListCount);
+ } else {
+ oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 65535);
+ }
+
+ if (listStartOffset > 0) {
+ oap.add(OAP_TAGID_LIST_START_OFFSET, (short) listStartOffset);
+ }
+
+ oap.addToHeaderSet(mHeaderSet);
+
+ mFormat = format;
+ }
+
+ @Override
+ protected void readResponse(InputStream stream) throws IOException {
+ Log.v(TAG, "readResponse");
+
+ mResponse = new BluetoothPbapVcardList(mAccount, stream, mFormat);
+ if (DBG) {
+ Log.d(TAG, "Read " + mResponse.getCount() + " entries.");
+ }
+ }
+
+ @Override
+ protected void readResponseHeaders(HeaderSet headerset) {
+ Log.v(TAG, "readResponseHeaders");
+
+ ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
+
+ if (oap.exists(OAP_TAGID_NEW_MISSED_CALLS)) {
+ mNewMissedCalls = oap.getByte(OAP_TAGID_NEW_MISSED_CALLS);
+ }
+ }
+
+ public ArrayList<VCardEntry> getList() {
+ return mResponse.getList();
+ }
+
+ public int getNewMissedCalls() {
+ return mNewMissedCalls;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java
new file mode 100644
index 0000000..29581d7
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import com.android.bluetooth.pbapclient.utils.ObexAppParameters;
+
+import javax.obex.HeaderSet;
+
+class BluetoothPbapRequestPullPhoneBookSize extends BluetoothPbapRequest {
+
+ private static final String TAG = "BluetoothPbapRequestPullPhoneBookSize";
+
+ private static final String TYPE = "x-bt/phonebook";
+
+ private int mSize;
+
+ public BluetoothPbapRequestPullPhoneBookSize(String pbName) {
+ mHeaderSet.setHeader(HeaderSet.NAME, pbName);
+
+ mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+ ObexAppParameters oap = new ObexAppParameters();
+ oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 0);
+ oap.addToHeaderSet(mHeaderSet);
+ }
+
+ @Override
+ protected void readResponseHeaders(HeaderSet headerset) {
+ Log.v(TAG, "readResponseHeaders");
+
+ ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
+
+ mSize = oap.getShort(OAP_TAGID_PHONEBOOK_SIZE);
+ }
+
+ public int getSize() {
+ return mSize;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardEntry.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardEntry.java
new file mode 100644
index 0000000..128cdc2
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardEntry.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.util.Log;
+
+import com.android.vcard.VCardEntry;
+import com.android.bluetooth.pbapclient.utils.ObexAppParameters;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.obex.HeaderSet;
+import javax.obex.ResponseCodes;
+
+final class BluetoothPbapRequestPullVcardEntry extends BluetoothPbapRequest {
+
+ private static final String TAG = "BluetoothPbapRequestPullVcardEntry";
+
+ private static final String TYPE = "x-bt/vcard";
+
+ private BluetoothPbapVcardList mResponse;
+
+ private final Account mAccount;
+
+ private final byte mFormat;
+
+ public BluetoothPbapRequestPullVcardEntry(
+ String handle, Account account, long filter, byte format) {
+ mAccount = account;
+
+ mHeaderSet.setHeader(HeaderSet.NAME, handle);
+
+ mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+ /* make sure format is one of allowed values */
+ if (format != BluetoothPbapClient.VCARD_TYPE_21
+ && format != BluetoothPbapClient.VCARD_TYPE_30) {
+ format = BluetoothPbapClient.VCARD_TYPE_21;
+ }
+
+ ObexAppParameters oap = new ObexAppParameters();
+
+ if (filter != 0) {
+ oap.add(OAP_TAGID_FILTER, filter);
+ }
+
+ oap.add(OAP_TAGID_FORMAT, format);
+ oap.addToHeaderSet(mHeaderSet);
+
+ mFormat = format;
+ }
+
+ @Override
+ protected void readResponse(InputStream stream) throws IOException {
+ Log.v(TAG, "readResponse");
+
+ mResponse = new BluetoothPbapVcardList(mAccount, stream, mFormat);
+ }
+ @Override
+ protected void checkResponseCode(int responseCode) throws IOException {
+ Log.v(TAG, "checkResponseCode");
+
+ if (mResponse.getCount() == 0) {
+ if (responseCode != ResponseCodes.OBEX_HTTP_NOT_FOUND &&
+ responseCode != ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
+ throw new IOException("Invalid response received");
+ } else {
+ Log.v(TAG, "Vcard Entry not found");
+ }
+ }
+ }
+
+ public VCardEntry getVcard() {
+ return mResponse.getFirst();
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardListing.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardListing.java
new file mode 100644
index 0000000..ff4c617
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardListing.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import com.android.bluetooth.pbapclient.utils.ObexAppParameters;
+import com.android.bluetooth.pbapclient.BluetoothPbapVcardListing;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import javax.obex.HeaderSet;
+
+final class BluetoothPbapRequestPullVcardListing extends BluetoothPbapRequest {
+
+ private static final String TAG = "BluetoothPbapRequestPullVcardListing";
+
+ private static final String TYPE = "x-bt/vcard-listing";
+
+ private BluetoothPbapVcardListing mResponse = null;
+
+ private int mNewMissedCalls = -1;
+
+ public BluetoothPbapRequestPullVcardListing(String folder, byte order, byte searchAttr,
+ String searchVal, int maxListCount, int listStartOffset) {
+
+ if (maxListCount < 0 || maxListCount > 65535) {
+ throw new IllegalArgumentException("maxListCount should be [0..65535]");
+ }
+
+ if (listStartOffset < 0 || listStartOffset > 65535) {
+ throw new IllegalArgumentException("listStartOffset should be [0..65535]");
+ }
+
+ if (folder == null) {
+ folder = "";
+ }
+
+ mHeaderSet.setHeader(HeaderSet.NAME, folder);
+
+ mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+ ObexAppParameters oap = new ObexAppParameters();
+
+ if (order >= 0) {
+ oap.add(OAP_TAGID_ORDER, order);
+ }
+
+ if (searchVal != null) {
+ oap.add(OAP_TAGID_SEARCH_ATTRIBUTE, searchAttr);
+ oap.add(OAP_TAGID_SEARCH_VALUE, searchVal);
+ }
+
+ /*
+ * maxListCount is a special case which is handled in
+ * BluetoothPbapRequestPullVcardListingSize
+ */
+ if (maxListCount > 0) {
+ oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) maxListCount);
+ }
+
+ if (listStartOffset > 0) {
+ oap.add(OAP_TAGID_LIST_START_OFFSET, (short) listStartOffset);
+ }
+
+ oap.addToHeaderSet(mHeaderSet);
+ }
+
+ @Override
+ protected void readResponse(InputStream stream) throws IOException {
+ Log.v(TAG, "readResponse");
+
+ mResponse = new BluetoothPbapVcardListing(stream);
+ }
+
+ @Override
+ protected void readResponseHeaders(HeaderSet headerset) {
+ Log.v(TAG, "readResponseHeaders");
+
+ ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
+
+ if (oap.exists(OAP_TAGID_NEW_MISSED_CALLS)) {
+ mNewMissedCalls = oap.getByte(OAP_TAGID_NEW_MISSED_CALLS);
+ }
+ }
+
+ public ArrayList<BluetoothPbapCard> getList() {
+ return mResponse.getList();
+ }
+
+ public int getNewMissedCalls() {
+ return mNewMissedCalls;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardListingSize.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardListingSize.java
new file mode 100644
index 0000000..3ad289b
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullVcardListingSize.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import com.android.bluetooth.pbapclient.utils.ObexAppParameters;
+
+import javax.obex.HeaderSet;
+
+class BluetoothPbapRequestPullVcardListingSize extends BluetoothPbapRequest {
+
+ private static final String TAG = "BluetoothPbapRequestPullVcardListingSize";
+
+ private static final String TYPE = "x-bt/vcard-listing";
+
+ private int mSize;
+
+ public BluetoothPbapRequestPullVcardListingSize(String folder) {
+ mHeaderSet.setHeader(HeaderSet.NAME, folder);
+
+ mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+ ObexAppParameters oap = new ObexAppParameters();
+ oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 0);
+ oap.addToHeaderSet(mHeaderSet);
+ }
+
+ @Override
+ protected void readResponseHeaders(HeaderSet headerset) {
+ Log.v(TAG, "readResponseHeaders");
+
+ ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
+
+ mSize = oap.getShort(OAP_TAGID_PHONEBOOK_SIZE);
+ }
+
+ public int getSize() {
+ return mSize;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestSetPath.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestSetPath.java
new file mode 100644
index 0000000..55d64dd
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestSetPath.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+import javax.obex.ClientSession;
+import javax.obex.HeaderSet;
+import javax.obex.ResponseCodes;
+
+final class BluetoothPbapRequestSetPath extends BluetoothPbapRequest {
+
+ private final static String TAG = "BluetoothPbapRequestSetPath";
+
+ private enum SetPathDir {
+ ROOT, UP, DOWN
+ };
+
+ private SetPathDir mDir;
+
+ public BluetoothPbapRequestSetPath(String name) {
+ mDir = SetPathDir.DOWN;
+ mHeaderSet.setHeader(HeaderSet.NAME, name);
+ }
+
+ public BluetoothPbapRequestSetPath(boolean goUp) {
+ mHeaderSet.setEmptyNameHeader();
+ if (goUp) {
+ mDir = SetPathDir.UP;
+ } else {
+ mDir = SetPathDir.ROOT;
+ }
+ }
+
+ @Override
+ public void execute(ClientSession session) {
+ Log.v(TAG, "execute");
+
+ HeaderSet hs = null;
+
+ try {
+ switch (mDir) {
+ case ROOT:
+ case DOWN:
+ hs = session.setPath(mHeaderSet, false, false);
+ break;
+ case UP:
+ hs = session.setPath(mHeaderSet, true, false);
+ break;
+ }
+
+ mResponseCode = hs.getResponseCode();
+ } catch (IOException e) {
+ mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapSession.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapSession.java
new file mode 100644
index 0000000..79aa95d
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapSession.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.UUID;
+
+class BluetoothPbapSession implements Callback {
+ //TODO consider cleaning file organization and naming.
+ private static final String TAG = "com.android.bluetooth.pbapclient.BluetoothPbapSession";
+
+ /* local use only */
+ private static final int RFCOMM_CONNECTED = 1;
+ private static final int RFCOMM_FAILED = 2;
+
+ /* to BluetoothPbapClient */
+ public static final int REQUEST_COMPLETED = 3;
+ public static final int REQUEST_FAILED = 4;
+ public static final int SESSION_CONNECTING = 5;
+ public static final int SESSION_CONNECTED = 6;
+ public static final int SESSION_DISCONNECTED = 7;
+ public static final int AUTH_REQUESTED = 8;
+ public static final int AUTH_TIMEOUT = 9;
+
+ public static final int ACTION_LISTING = 14;
+ public static final int ACTION_VCARD = 15;
+ public static final int ACTION_PHONEBOOK_SIZE = 16;
+
+ private static final String PBAP_UUID =
+ "0000112f-0000-1000-8000-00805f9b34fb";
+
+ private final BluetoothAdapter mAdapter;
+ private final BluetoothDevice mDevice;
+
+ private final Handler mParentHandler;
+
+ private final HandlerThread mHandlerThread;
+ private final Handler mSessionHandler;
+
+ private RfcommConnectThread mConnectThread;
+ private BluetoothPbapObexTransport mTransport;
+
+ private BluetoothPbapObexSession mObexSession;
+
+ private BluetoothPbapRequest mPendingRequest = null;
+
+ public BluetoothPbapSession(BluetoothDevice device, Handler handler) {
+
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mAdapter == null) {
+ throw new NullPointerException("No Bluetooth adapter in the system");
+ }
+
+ mDevice = device;
+ mParentHandler = handler;
+ mConnectThread = null;
+ mTransport = null;
+ mObexSession = null;
+
+ mHandlerThread = new HandlerThread("PBAP session handler",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mHandlerThread.start();
+ mSessionHandler = new Handler(mHandlerThread.getLooper(), this);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ Log.d(TAG, "Handler: msg: " + msg.what);
+
+ switch (msg.what) {
+ case RFCOMM_FAILED:
+ mConnectThread = null;
+
+ mParentHandler.obtainMessage(SESSION_DISCONNECTED).sendToTarget();
+
+ if (mPendingRequest != null) {
+ mParentHandler.obtainMessage(REQUEST_FAILED, mPendingRequest).sendToTarget();
+ mPendingRequest = null;
+ }
+ break;
+
+ case RFCOMM_CONNECTED:
+ mConnectThread = null;
+ mTransport = (BluetoothPbapObexTransport) msg.obj;
+ startObexSession();
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_FAILED:
+ stopObexSession();
+
+ mParentHandler.obtainMessage(SESSION_DISCONNECTED).sendToTarget();
+
+ if (mPendingRequest != null) {
+ mParentHandler.obtainMessage(REQUEST_FAILED, mPendingRequest).sendToTarget();
+ mPendingRequest = null;
+ }
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_CONNECTED:
+ mParentHandler.obtainMessage(SESSION_CONNECTED).sendToTarget();
+
+ if (mPendingRequest != null) {
+ mObexSession.schedule(mPendingRequest);
+ mPendingRequest = null;
+ }
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_DISCONNECTED:
+ mParentHandler.obtainMessage(SESSION_DISCONNECTED).sendToTarget();
+ stopRfcomm();
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_REQUEST_COMPLETED:
+ /* send to parent, process there */
+ mParentHandler.obtainMessage(REQUEST_COMPLETED, msg.obj).sendToTarget();
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_REQUEST_FAILED:
+ /* send to parent, process there */
+ mParentHandler.obtainMessage(REQUEST_FAILED, msg.obj).sendToTarget();
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_AUTHENTICATION_REQUEST:
+ /* send to parent, process there */
+ mParentHandler.obtainMessage(AUTH_REQUESTED).sendToTarget();
+
+ mSessionHandler
+ .sendMessageDelayed(
+ mSessionHandler
+ .obtainMessage(BluetoothPbapObexSession.OBEX_SESSION_AUTHENTICATION_TIMEOUT),
+ 30000);
+ break;
+
+ case BluetoothPbapObexSession.OBEX_SESSION_AUTHENTICATION_TIMEOUT:
+ /* stop authentication */
+ setAuthResponse(null);
+
+ mParentHandler.obtainMessage(AUTH_TIMEOUT).sendToTarget();
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ public void start() {
+ Log.d(TAG, "start");
+
+ startRfcomm();
+ }
+
+ public void stop() {
+ Log.d(TAG, "Stop");
+
+ stopObexSession();
+ stopRfcomm();
+ }
+
+ public void abort() {
+ Log.d(TAG, "abort");
+
+ /* fail pending request immediately */
+ if (mPendingRequest != null) {
+ mParentHandler.obtainMessage(REQUEST_FAILED, mPendingRequest).sendToTarget();
+ mPendingRequest = null;
+ }
+
+ if (mObexSession != null) {
+ mObexSession.abort();
+ }
+ }
+
+ public boolean makeRequest(BluetoothPbapRequest request) {
+ Log.v(TAG, "makeRequest: " + request.getClass().getSimpleName());
+
+ if (mPendingRequest != null) {
+ Log.w(TAG, "makeRequest: request already queued, exiting");
+ return false;
+ }
+
+ if (mObexSession == null) {
+ mPendingRequest = request;
+
+ /*
+ * since there is no pending request and no session it's safe to
+ * assume that RFCOMM does not exist either and we should start
+ * connecting it
+ */
+ startRfcomm();
+
+ return true;
+ }
+
+ return mObexSession.schedule(request);
+ }
+
+ public boolean setAuthResponse(String key) {
+ Log.d(TAG, "setAuthResponse key=" + key);
+
+ mSessionHandler
+ .removeMessages(BluetoothPbapObexSession.OBEX_SESSION_AUTHENTICATION_TIMEOUT);
+
+ /* does not make sense to set auth response when OBEX session is down */
+ if (mObexSession == null) {
+ return false;
+ }
+
+ return mObexSession.setAuthReply(key);
+ }
+
+ private void startRfcomm() {
+ Log.d(TAG, "startRfcomm");
+
+ if (mConnectThread == null && mObexSession == null) {
+ mParentHandler.obtainMessage(SESSION_CONNECTING).sendToTarget();
+
+ mConnectThread = new RfcommConnectThread();
+ mConnectThread.start();
+ }
+
+ /*
+ * don't care if mConnectThread is not null - it means RFCOMM is being
+ * connected anyway
+ */
+ }
+
+ private void stopRfcomm() {
+ Log.d(TAG, "stopRfcomm");
+
+ if (mConnectThread != null) {
+ try {
+ // Force close the socket in case the thread is stuck doing the connect()
+ // call.
+ mConnectThread.closeSocket();
+ // TODO: Add timed join if closeSocket does not clean up the state.
+ mConnectThread.join();
+ } catch (InterruptedException e) {
+ }
+
+ mConnectThread = null;
+ }
+
+ if (mTransport != null) {
+ try {
+ mTransport.close();
+ } catch (IOException e) {
+ }
+
+ mTransport = null;
+ }
+ }
+
+ private void startObexSession() {
+ Log.d(TAG, "startObexSession");
+
+ mObexSession = new BluetoothPbapObexSession(mTransport);
+ mObexSession.start(mSessionHandler);
+ }
+
+ private void stopObexSession() {
+ Log.d(TAG, "stopObexSession");
+
+ if (mObexSession != null) {
+ mObexSession.stop();
+ mObexSession = null;
+ }
+ }
+
+ private class RfcommConnectThread extends Thread {
+ private static final String TAG = "RfcommConnectThread";
+
+ private BluetoothSocket mSocket;
+
+ public RfcommConnectThread() {
+ super("RfcommConnectThread");
+ }
+
+ @Override
+ public void run() {
+ if (mAdapter.isDiscovering()) {
+ mAdapter.cancelDiscovery();
+ }
+
+ try {
+ mSocket = mDevice.createRfcommSocketToServiceRecord(UUID.fromString(PBAP_UUID));
+ mSocket.connect();
+
+ BluetoothPbapObexTransport transport;
+ transport = new BluetoothPbapObexTransport(mSocket);
+
+ mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport).sendToTarget();
+ } catch (IOException e) {
+ closeSocket();
+ mSessionHandler.obtainMessage(RFCOMM_FAILED).sendToTarget();
+ }
+
+ }
+
+ // This method may be called from outside the thread if the connect() call above is stuck.
+ public void closeSocket() {
+ try {
+ if (mSocket != null) {
+ mSocket.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error when closing socket", e);
+ }
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardList.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardList.java
new file mode 100644
index 0000000..1f22c14
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardList.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+
+import com.android.vcard.VCardConfig;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntryConstructor;
+import com.android.vcard.VCardEntryCounter;
+import com.android.vcard.VCardEntryHandler;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.exception.VCardException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+class BluetoothPbapVcardList {
+
+ private final ArrayList<VCardEntry> mCards = new ArrayList<VCardEntry>();
+ private final Account mAccount;
+
+ class CardEntryHandler implements VCardEntryHandler {
+ @Override
+ public void onStart() {
+ }
+
+ @Override
+ public void onEntryCreated(VCardEntry entry) {
+ mCards.add(entry);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+ }
+
+ public BluetoothPbapVcardList(Account account, InputStream in, byte format) throws IOException {
+ mAccount = account;
+ parse(in, format);
+ }
+
+ private void parse(InputStream in, byte format) throws IOException {
+ VCardParser parser;
+
+ if (format == BluetoothPbapClient.VCARD_TYPE_30) {
+ parser = new VCardParser_V30();
+ } else {
+ parser = new VCardParser_V21();
+ }
+
+ VCardEntryConstructor constructor =
+ new VCardEntryConstructor(VCardConfig.VCARD_TYPE_V21_GENERIC, mAccount);
+ VCardEntryCounter counter = new VCardEntryCounter();
+ CardEntryHandler handler = new CardEntryHandler();
+
+ constructor.addEntryHandler(handler);
+
+ parser.addInterpreter(constructor);
+ parser.addInterpreter(counter);
+
+ try {
+ parser.parse(in);
+ } catch (VCardException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public int getCount() {
+ return mCards.size();
+ }
+
+ public ArrayList<VCardEntry> getList() {
+ return mCards;
+ }
+
+ public VCardEntry getFirst() {
+ return mCards.get(0);
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardListing.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardListing.java
new file mode 100644
index 0000000..906cf65
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardListing.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+class BluetoothPbapVcardListing {
+
+ private static final String TAG = "BluetoothPbapVcardListing";
+
+ ArrayList<BluetoothPbapCard> mCards = new ArrayList<BluetoothPbapCard>();
+
+ public BluetoothPbapVcardListing(InputStream in) throws IOException {
+ parse(in);
+ }
+
+ private void parse(InputStream in) throws IOException {
+ XmlPullParser parser = Xml.newPullParser();
+
+ try {
+ parser.setInput(in, "UTF-8");
+
+ int eventType = parser.getEventType();
+
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+
+ if (eventType == XmlPullParser.START_TAG && parser.getName().equals("card")) {
+ BluetoothPbapCard card = new BluetoothPbapCard(
+ parser.getAttributeValue(null, "handle"),
+ parser.getAttributeValue(null, "name"));
+ mCards.add(card);
+ }
+
+ eventType = parser.next();
+ }
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "XML parser error when parsing XML", e);
+ }
+ }
+
+ public ArrayList<BluetoothPbapCard> getList() {
+ return mCards;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java b/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java
new file mode 100644
index 0000000..6d1f3c0
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import com.android.bluetooth.pbapclient.BluetoothPbapClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+import android.provider.CallLog;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntry.PhoneData;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class CallLogPullRequest extends PullRequest {
+ private static boolean DBG = true;
+ private static String TAG = "PbapCallLogPullRequest";
+ private static final String TIMESTAMP_PROPERTY = "X-IRMC-CALL-DATETIME";
+ private static final String TIMESTAMP_FORMAT = "yyyyMMdd'T'HHmmss";
+
+ private Context mContext;
+
+ public CallLogPullRequest(Context context, String path) {
+ mContext = context;
+ this.path = path;
+ }
+
+ @Override
+ public void onPullComplete() {
+ if (mEntries == null) {
+ Log.e(TAG, "onPullComplete entries is null.");
+ return;
+ }
+
+ if (DBG) {
+ Log.d(TAG, "onPullComplete with " + mEntries.size() + " count.");
+ }
+ int type;
+ try {
+ if (path.equals(BluetoothPbapClient.ICH_PATH)) {
+ type = CallLog.Calls.INCOMING_TYPE;
+ } else if (path.equals(BluetoothPbapClient.OCH_PATH)) {
+ type = CallLog.Calls.OUTGOING_TYPE;
+ } else if (path.equals(BluetoothPbapClient.MCH_PATH)) {
+ type = CallLog.Calls.MISSED_TYPE;
+ } else {
+ Log.w(TAG, "Unknown path type:" + path);
+ return;
+ }
+
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+ for (VCardEntry vcard : mEntries) {
+ List<PhoneData> phones = vcard.getPhoneList();
+ if (phones == null || phones.size() != 1) {
+ Log.d(TAG, "Incorrect number of phones: " + vcard);
+ continue;
+ }
+
+ List<Pair<String, String>> irmc = vcard.getUnknownXData();
+ Date date = null;
+ SimpleDateFormat parser = new SimpleDateFormat(TIMESTAMP_FORMAT);
+ for (Pair<String, String> pair : irmc) {
+ if (pair.first.startsWith(TIMESTAMP_PROPERTY)) {
+ try {
+ date = parser.parse(pair.second);
+ } catch (ParseException e) {
+ Log.d(TAG, "Failed to parse date " + pair.second);
+ }
+ }
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(CallLog.Calls.TYPE, type);
+ values.put(CallLog.Calls.NUMBER, phones.get(0).getNumber());
+ if (date != null) {
+ values.put(CallLog.Calls.DATE, date.getTime());
+ }
+ ops.add(ContentProviderOperation.newInsert(CallLog.Calls.CONTENT_URI)
+ .withValues(values).withYieldAllowed(true).build());
+ }
+ mContext.getContentResolver().applyBatch(CallLog.AUTHORITY, ops);
+ Log.d(TAG, "Updated call logs.");
+ } catch (RemoteException | OperationApplicationException e) {
+ Log.d(TAG, "Failed to update call log for path=" + path, e);
+ } finally {
+ synchronized (this) {
+ this.notify();
+ }
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientService.java b/src/com/android/bluetooth/pbapclient/PbapClientService.java
new file mode 100644
index 0000000..8af451c
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothPbapClient;
+import android.bluetooth.IBluetoothHeadsetClient;
+import android.content.BroadcastReceiver;
+import android.content.ContentProviderOperation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.OperationApplicationException;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+import android.provider.ContactsContract;
+
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.Utils;
+import com.android.vcard.VCardEntry;
+
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * Provides Bluetooth Phone Book Access Profile Client profile.
+ *
+ * @hide
+ */
+public class PbapClientService extends ProfileService {
+ private static final boolean DBG = false;
+ private static final String TAG = "PbapClientService";
+ private PbapPCEClient mClient;
+ private HandlerThread mHandlerThread;
+ private AccountManager mAccountManager;
+ private static PbapClientService sPbapClientService;
+ private PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver();
+
+ @Override
+ protected String getName() {
+ return TAG;
+ }
+
+ @Override
+ public IProfileServiceBinder initBinder() {
+ return new BluetoothPbapClientBinder(this);
+ }
+
+ @Override
+ protected boolean start() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ try {
+ registerReceiver(mPbapBroadcastReceiver, filter);
+ } catch (Exception e) {
+ Log.w(TAG,"Unable to register pbapclient receiver",e);
+ }
+ mClient = new PbapPCEClient(this);
+ mAccountManager = AccountManager.get(this);
+ setPbapClientService(this);
+ mClient.start();
+ return true;
+ }
+
+ @Override
+ protected boolean stop() {
+ try {
+ unregisterReceiver(mPbapBroadcastReceiver);
+ } catch (Exception e) {
+ Log.w(TAG,"Unable to unregister sap receiver",e);
+ }
+ mClient.disconnect(null);
+ return true;
+ }
+
+ @Override
+ protected boolean cleanup() {
+ clearPbapClientService();
+ return true;
+ }
+
+ private class PbapBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.v(TAG, "onReceive");
+ String action = intent.getAction();
+ if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if(getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
+ connect(device);
+ }
+ } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ disconnect(device);
+ }
+ }
+ }
+
+ /**
+ * Handler for incoming service calls
+ */
+ private static class BluetoothPbapClientBinder extends IBluetoothPbapClient.Stub
+ implements IProfileServiceBinder {
+ private PbapClientService mService;
+
+ public BluetoothPbapClientBinder(PbapClientService svc) {
+ mService = svc;
+ }
+
+ @Override
+ public boolean cleanup() {
+ mService = null;
+ return true;
+ }
+
+ private PbapClientService getService() {
+ if (!Utils.checkCaller()) {
+ Log.w(TAG, "PbapClient call not allowed for non-active user");
+ return null;
+ }
+
+ if (mService != null && mService.isAvailable()) {
+ return mService;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean connect(BluetoothDevice device) {
+ PbapClientService service = getService();
+ if (service == null) {
+ return false;
+ }
+ return service.connect(device);
+ }
+
+ @Override
+ public boolean disconnect(BluetoothDevice device) {
+ PbapClientService service = getService();
+ if (service == null) {
+ return false;
+ }
+ return service.disconnect(device);
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ PbapClientService service = getService();
+ if (service == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
+ return service.getConnectedDevices();
+ }
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ PbapClientService service = getService();
+ if (service == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ PbapClientService service = getService();
+ if (service == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return service.getConnectionState(device);
+ }
+
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ PbapClientService service = getService();
+ if (service == null) {
+ return false;
+ }
+ return service.setPriority(device, priority);
+ }
+
+ public int getPriority(BluetoothDevice device) {
+ PbapClientService service = getService();
+ if (service == null) {
+ return BluetoothProfile.PRIORITY_UNDEFINED;
+ }
+ return service.getPriority(device);
+ }
+
+
+ }
+
+
+ // API methods
+ public static synchronized PbapClientService getPbapClientService() {
+ if (sPbapClientService != null && sPbapClientService.isAvailable()) {
+ if (DBG) {
+ Log.d(TAG, "getPbapClientService(): returning " + sPbapClientService);
+ }
+ return sPbapClientService;
+ }
+ if (DBG) {
+ if (sPbapClientService == null) {
+ Log.d(TAG, "getPbapClientService(): service is NULL");
+ } else if (!(sPbapClientService.isAvailable())) {
+ Log.d(TAG, "getPbapClientService(): service is not available");
+ }
+ }
+ return null;
+ }
+
+ private static synchronized void setPbapClientService(PbapClientService instance) {
+ if (instance != null && instance.isAvailable()) {
+ if (DBG) {
+ Log.d(TAG, "setPbapClientService(): set to: " + sPbapClientService);
+ }
+ sPbapClientService = instance;
+ } else {
+ if (DBG) {
+ if (sPbapClientService == null) {
+ Log.d(TAG, "setPbapClientService(): service not available");
+ } else if (!sPbapClientService.isAvailable()) {
+ Log.d(TAG, "setPbapClientService(): service is cleaning up");
+ }
+ }
+ }
+ }
+
+ private static synchronized void clearPbapClientService() {
+ sPbapClientService = null;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ Log.d(TAG,"Received request to ConnectPBAPPhonebook " + device.getAddress());
+ int connectionState = mClient.getConnectionState();
+ if (connectionState == BluetoothProfile.STATE_CONNECTED ||
+ connectionState == BluetoothProfile.STATE_CONNECTING) {
+ return false;
+ }
+ if (getPriority(device)>BluetoothProfile.PRIORITY_OFF) {
+ mClient.connect(device);
+ return true;
+ }
+ return false;
+ }
+
+ boolean disconnect(BluetoothDevice device) {
+ enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ mClient.disconnect(device);
+ return true;
+ }
+ public List<BluetoothDevice> getConnectedDevices() {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ int[] desiredStates = {BluetoothProfile.STATE_CONNECTED};
+ return getDevicesMatchingConnectionStates(desiredStates);
+ }
+
+ private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ int clientState = mClient.getConnectionState();
+ Log.d(TAG,"getDevicesMatchingConnectionStates " + Arrays.toString(states) + " == " + clientState);
+ List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
+ for (int state : states) {
+ if (clientState == state) {
+ BluetoothDevice currentDevice = mClient.getDevice();
+ if (currentDevice != null) {
+ deviceList.add(currentDevice);
+ }
+ }
+ }
+ return deviceList;
+ }
+
+ int getConnectionState(BluetoothDevice device) {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (device == mClient.getDevice()) {
+ return mClient.getConnectionState();
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ Settings.Global.putInt(getContentResolver(),
+ Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
+ priority);
+ if (DBG) {
+ Log.d(TAG,"Saved priority " + device + " = " + priority);
+ }
+ return true;
+ }
+
+ public int getPriority(BluetoothDevice device) {
+ enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ int priority = Settings.Global.getInt(getContentResolver(),
+ Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
+ BluetoothProfile.PRIORITY_UNDEFINED);
+ return priority;
+ }
+
+}
diff --git a/src/com/android/bluetooth/pbapclient/PbapHandler.java b/src/com/android/bluetooth/pbapclient/PbapHandler.java
new file mode 100644
index 0000000..36e7c0a
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/PbapHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import com.android.bluetooth.pbapclient.BluetoothPbapCard;
+import com.android.bluetooth.pbapclient.BluetoothPbapClient;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.vcard.VCardEntry;
+
+import java.util.List;
+
+public class PbapHandler extends Handler {
+ private static final String TAG = "PbapHandler";
+
+ private PbapListener mListener;
+
+ public PbapHandler(PbapListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "handleMessage " + msg.what);
+ switch (msg.what) {
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE:
+ Log.d(TAG, "EVENT_PULL_PHONE_BOOK_DONE with entries " +
+ ((List<VCardEntry>)(msg.obj)).size());
+ mListener.onPhoneBookPullDone((List<VCardEntry>) msg.obj);
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_ERROR:
+ Log.d(TAG, "EVENT_PULL_PHONE_BOOK_ERROR");
+ mListener.onPhoneBookError();
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_CONNECTED:
+ Log.d(TAG, "EVENT_SESSION_CONNECTED");
+ mListener.onPbapClientConnected(true);
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_DISCONNECTED:
+ Log.d(TAG, "EVENT_SESSION_DISCONNECTED");
+ mListener.onPbapClientConnected(false);
+ break;
+
+ // TODO(rni): Actually handle these cases.
+ case BluetoothPbapClient.EVENT_SET_PHONE_BOOK_DONE:
+ Log.d(TAG, "EVENT_SET_PHONE_BOOK_DONE");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_DONE:
+ Log.d(TAG, "EVENT_PULL_VCARD_LISTING_DONE");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_DONE:
+ Log.d(TAG, "EVENT_PULL_VCARD_ENTRY_DONE");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_SIZE_DONE:
+ Log.d(TAG, "EVENT_PULL_PHONE_BOOK_SIZE_DONE");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_SIZE_DONE:
+ Log.d(TAG, "EVENT_PULL_VCARD_LISTING_SIZE_DONE");
+ break;
+ case BluetoothPbapClient.EVENT_SET_PHONE_BOOK_ERROR:
+ Log.d(TAG, "EVENT_SET_PHONE_BOOK_ERROR");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_ERROR:
+ Log.d(TAG, "EVENT_PULL_VCARD_LISTING_ERROR");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_ERROR:
+ Log.d(TAG, "EVENT_PULL_VCARD_ENTRY_ERROR");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_SIZE_ERROR:
+ Log.d(TAG, "EVENT_PULL_PHONE_BOOK_SIZE_ERROR");
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_SIZE_ERROR:
+ Log.d(TAG, "EVENT_PULL_VCARD_LISTING_SIZE_ERROR");
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_AUTH_REQUESTED:
+ Log.d(TAG, "EVENT_SESSION_AUTH_REQUESTED");
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_AUTH_TIMEOUT:
+ Log.d(TAG, "EVENT_SESSION_AUTH_TIMEOUT");
+ break;
+ default:
+ Log.e(TAG, "unknown message " + msg);
+ break;
+ }
+ }
+
+ public interface PbapListener {
+ void onPhoneBookPullDone(List<VCardEntry> entries);
+ void onPhoneBookError();
+ void onPbapClientConnected(boolean status);
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/PbapPCEClient.java b/src/com/android/bluetooth/pbapclient/PbapPCEClient.java
new file mode 100644
index 0000000..d04fabc
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/PbapPCEClient.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.net.Uri;
+import android.provider.CallLog;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.vcard.VCardEntry;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.R;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.lang.InterruptedException;
+import java.lang.Thread;
+/**
+ * These are the possible paths that can be pulled:
+ * BluetoothPbapClient.PB_PATH;
+ * BluetoothPbapClient.SIM_PB_PATH;
+ * BluetoothPbapClient.ICH_PATH;
+ * BluetoothPbapClient.SIM_ICH_PATH;
+ * BluetoothPbapClient.OCH_PATH;
+ * BluetoothPbapClient.SIM_OCH_PATH;
+ * BluetoothPbapClient.MCH_PATH;
+ * BluetoothPbapClient.SIM_MCH_PATH;
+ */
+public class PbapPCEClient implements PbapHandler.PbapListener {
+ private static final String TAG = "PbapPCEClient";
+ private static final boolean DBG = false;
+ private final Queue<PullRequest> mPendingRequests = new ArrayDeque<PullRequest>();
+ private BluetoothDevice mDevice;
+ private BluetoothPbapClient mClient;
+ private boolean mClientConnected = false;
+ private PbapHandler mHandler;
+ private ConnectionHandler mConnectionHandler;
+ private PullRequest mLastPull;
+ private HandlerThread mContactHandlerThread;
+ private Handler mContactHandler;
+ private Account mAccount = null;
+ private Context mContext = null;
+ private AccountManager mAccountManager;
+
+ PbapPCEClient(Context context) {
+ mContext = context;
+ mConnectionHandler = new ConnectionHandler(mContext.getMainLooper());
+ mHandler = new PbapHandler(this);
+ mAccountManager = AccountManager.get(mContext);
+ mContactHandlerThread = new HandlerThread("PBAP contact handler",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mContactHandlerThread.start();
+ mContactHandler = new ContactHandler(mContactHandlerThread.getLooper());
+ }
+
+ public int getConnectionState() {
+ if (mDevice == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ BluetoothPbapClient.ConnectionState currentState = mClient.getState();
+ int bluetoothConnectionState;
+ switch(currentState) {
+ case DISCONNECTED:
+ bluetoothConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+ break;
+ case CONNECTING:
+ bluetoothConnectionState = BluetoothProfile.STATE_CONNECTING;
+ break;
+ case CONNECTED:
+ bluetoothConnectionState = BluetoothProfile.STATE_CONNECTED;
+ break;
+ case DISCONNECTING:
+ bluetoothConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+ break;
+ default:
+ bluetoothConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return bluetoothConnectionState;
+ }
+
+ public BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
+ private boolean processNextRequest() {
+ if (DBG) {
+ Log.d(TAG,"processNextRequest()");
+ }
+ if (mPendingRequests.isEmpty()) {
+ return false;
+ }
+ if (mClient != null && mClient.getState() ==
+ BluetoothPbapClient.ConnectionState.CONNECTED) {
+ mLastPull = mPendingRequests.remove();
+ if (DBG) {
+ Log.d(TAG, "Pulling phone book from: " + mLastPull.path);
+ }
+ return mClient.pullPhoneBook(mLastPull.path);
+ }
+ return false;
+ }
+
+
+ @Override
+ public void onPhoneBookPullDone(List<VCardEntry> entries) {
+ mLastPull.setResults(entries);
+ mContactHandler.obtainMessage(ContactHandler.EVENT_ADD_CONTACTS,mLastPull).sendToTarget();
+ processNextRequest();
+ }
+
+ @Override
+ public void onPhoneBookError() {
+ if (DBG) {
+ Log.d(TAG, "Error, mLastPull = " + mLastPull);
+ }
+ processNextRequest();
+ }
+
+ @Override
+ public synchronized void onPbapClientConnected(boolean status) {
+ mClientConnected = status;
+ if (mClientConnected == false) {
+ // If we are disconnected then whatever the current device is we should simply clean up.
+ onConnectionStateChanged(mDevice, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED);
+ disconnect(null);
+ }
+ if (mClientConnected == true) {
+ onConnectionStateChanged(mDevice, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_CONNECTED);
+ processNextRequest();
+ }
+ }
+
+ private class ConnectionHandler extends Handler {
+ public static final int EVENT_CONNECT = 1;
+ public static final int EVENT_DISCONNECT = 2;
+
+ public ConnectionHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) {
+ Log.d(TAG, "Connection Handler Message " + msg.what + " with " + msg.obj);
+ }
+ switch (msg.what) {
+ case EVENT_CONNECT:
+ if (msg.obj instanceof BluetoothDevice) {
+ BluetoothDevice device = (BluetoothDevice) msg.obj;
+ int oldState = getConnectionState();
+ if (oldState != BluetoothProfile.STATE_DISCONNECTED) {
+ return;
+ }
+ handleConnect(device);
+ } else {
+ Log.e(TAG, "Invalid instance in Connection Handler:Connect");
+ }
+ break;
+
+ case EVENT_DISCONNECT:
+ if (mDevice == null) {
+ return;
+ }
+ if (msg.obj == null || msg.obj instanceof BluetoothDevice) {
+ BluetoothDevice device = (BluetoothDevice) msg.obj;
+ if (!mDevice.equals(device)) {
+ return;
+ }
+ int oldState = getConnectionState();
+ handleDisconnect(device);
+ int newState = getConnectionState();
+ if (device != null) {
+ onConnectionStateChanged(device, oldState, newState);
+ }
+ } else {
+ Log.e(TAG, "Invalid instance in Connection Handler:Disconnect");
+ }
+ break;
+
+ default:
+ Log.e(TAG, "Unknown Request to Connection Handler");
+ break;
+ }
+ }
+
+ private void handleConnect(BluetoothDevice device) {
+ Log.d(TAG,"HANDLECONNECT" + device);
+ if (device == null) {
+ throw new IllegalStateException(TAG + ":Connect with null device!");
+ } else if (mDevice != null && !mDevice.equals(device)) {
+ // Check that we are not already connected to an existing different device.
+ // Since the device can be connected to multiple external devices -- we use the honor
+ // protocol and only accept the first connecting device.
+ Log.e(TAG, ":Got a connected event when connected to a different device. " +
+ "existing = " + mDevice + " new = " + device);
+ return;
+ } else if (device.equals(mDevice)) {
+ Log.w(TAG, "Got a connected event for the same device. Ignoring!");
+ return;
+ }
+ // Update the device.
+ mDevice = device;
+ onConnectionStateChanged(mDevice,BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ // Add the account. This should give us a place to stash the data.
+ mAccount = new Account(device.getAddress(),
+ mContext.getString(R.string.pbap_account_type));
+ mContactHandler.obtainMessage(ContactHandler.EVENT_ADD_ACCOUNT, mAccount)
+ .sendToTarget();
+ mClient = new BluetoothPbapClient(mDevice, mAccount, mHandler);
+ downloadPhoneBook();
+ downloadCallLogs();
+ mClient.connect();
+ }
+
+ private void handleDisconnect(BluetoothDevice device) {
+ Log.w(TAG, "pbap disconnecting from = " + device);
+
+ if (device == null) {
+ // If we have a null device then disconnect the current device.
+ device = mDevice;
+ } else if (mDevice == null) {
+ Log.w(TAG, "No existing device connected to service - ignoring device = " + device);
+ return;
+ } else if (!mDevice.equals(device)) {
+ Log.w(TAG, "Existing device different from disconnected device. existing = " + mDevice +
+ " disconnecting device = " + device);
+ return;
+ }
+ resetState();
+ }
+ }
+
+ private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) {
+ Intent intent = new Intent(android.bluetooth.BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
+ Log.d(TAG,"Connection state " + device + ": " + prevState + "->" + state);
+ }
+
+ public void connect(BluetoothDevice device) {
+ mConnectionHandler.obtainMessage(ConnectionHandler.EVENT_CONNECT,device).sendToTarget();
+ }
+
+ public void disconnect(BluetoothDevice device) {
+ mConnectionHandler.obtainMessage(ConnectionHandler.EVENT_DISCONNECT,device).sendToTarget();
+ }
+
+ public void start() {
+ if (mDevice != null) {
+ // We are already connected -Ignore.
+ Log.w(TAG, "Already started, ignoring request to start again.");
+ return;
+ }
+ // Device is NULL, we go on remove any unclean shutdown accounts.
+ mContactHandler.obtainMessage(ContactHandler.EVENT_CLEANUP).sendToTarget();
+ }
+
+ private void resetState() {
+ if (DBG) {
+ Log.d(TAG,"resetState()");
+ }
+ if (mClient != null) {
+ // This should abort any inflight messages.
+ mClient.disconnect();
+ }
+ mClient = null;
+ mClientConnected = false;
+
+ mContactHandler.removeCallbacksAndMessages(null);
+ mContactHandlerThread.interrupt();
+ mContactHandler.obtainMessage(ContactHandler.EVENT_CLEANUP).sendToTarget();
+
+ mDevice = null;
+ mAccount = null;
+ mPendingRequests.clear();
+ if (DBG) {
+ Log.d(TAG,"resetState Complete");
+ }
+
+ }
+
+ private void downloadCallLogs() {
+ // Download Incoming Call Logs.
+ CallLogPullRequest ichCallLog =
+ new CallLogPullRequest(mContext, BluetoothPbapClient.ICH_PATH);
+ addPullRequest(ichCallLog);
+
+ // Downoad Outgoing Call Logs.
+ CallLogPullRequest ochCallLog =
+ new CallLogPullRequest(mContext, BluetoothPbapClient.OCH_PATH);
+ addPullRequest(ochCallLog);
+
+ // Downoad Missed Call Logs.
+ CallLogPullRequest mchCallLog =
+ new CallLogPullRequest(mContext, BluetoothPbapClient.MCH_PATH);
+ addPullRequest(mchCallLog);
+ }
+
+ private void downloadPhoneBook() {
+ // Download the phone book.
+ PhonebookPullRequest pb = new PhonebookPullRequest(mContext, mAccount);
+ addPullRequest(pb);
+ }
+
+ private void addPullRequest(PullRequest r) {
+ if (DBG) {
+ Log.d(TAG, "pull request mClient=" + mClient + " connected= " +
+ mClientConnected + " mDevice=" + mDevice + " path= " + r.path);
+ }
+ if (mClient == null || mDevice == null) {
+ // It seems we want to pull but the bt connection isn't up, fail it
+ // immediately.
+ Log.w(TAG, "aborting pull request.");
+ return;
+ }
+ mPendingRequests.add(r);
+ }
+
+ private class ContactHandler extends Handler {
+ public static final int EVENT_ADD_ACCOUNT = 1;
+ public static final int EVENT_ADD_CONTACTS = 2;
+ public static final int EVENT_CLEANUP = 3;
+
+ public ContactHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) {
+ Log.d(TAG, "Contact Handler Message " + msg.what + " with " + msg.obj);
+ }
+ switch (msg.what) {
+ case EVENT_ADD_ACCOUNT:
+ if (msg.obj instanceof Account) {
+ Account account = (Account) msg.obj;
+ addAccount(account);
+ } else {
+ Log.e(TAG, "invalid Instance in Contact Handler: Add Account");
+ }
+ break;
+
+ case EVENT_ADD_CONTACTS:
+ if (msg.obj instanceof PullRequest) {
+ PullRequest req = (PullRequest) msg.obj;
+ req.onPullComplete();
+ } else {
+ Log.e(TAG, "invalid Instance in Contact Handler: Add Contacts");
+ }
+ break;
+
+ case EVENT_CLEANUP:
+ Thread.currentThread().interrupted(); //clear state of interrupt.
+ removeUncleanAccounts();
+ mContext.getContentResolver().delete(CallLog.Calls.CONTENT_URI, null, null);
+ if (DBG) {
+ Log.d(TAG, "Call logs deleted.");
+ }
+ break;
+
+ default:
+ Log.e(TAG, "Unknown Request to Contact Handler");
+ break;
+ }
+ }
+
+ private void removeUncleanAccounts() {
+ // Find all accounts that match the type "pbap" and delete them. This section is
+ // executed only if the device was shut down in an unclean state and contacts persisted.
+ Account[] accounts =
+ mAccountManager.getAccountsByType(mContext.getString(R.string.pbap_account_type));
+ Log.w(TAG, "Found " + accounts.length + " unclean accounts");
+ for (Account acc : accounts) {
+ Log.w(TAG, "Deleting " + acc);
+ // The device ID is the name of the account.
+ removeAccount(acc);
+ }
+ }
+
+ private boolean addAccount(Account account) {
+ if (mAccountManager.addAccountExplicitly(account, null, null)) {
+ if (DBG) {
+ Log.d(TAG, "Added account " + mAccount);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean removeAccount(Account acc) {
+ if (mAccountManager.removeAccountExplicitly(acc)) {
+ if (DBG) {
+ Log.d(TAG, "Removed account " + acc);
+ }
+ return true;
+ }
+ Log.e(TAG, "Failed to remove account " + mAccount);
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/PhonebookEntry.java b/src/com/android/bluetooth/pbapclient/PhonebookEntry.java
new file mode 100644
index 0000000..4d38f35
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/PhonebookEntry.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import com.android.vcard.VCardEntry;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A simpler more public version of VCardEntry.
+ */
+public class PhonebookEntry {
+ public static class Name {
+ public String family;
+ public String given;
+ public String middle;
+ public String prefix;
+ public String suffix;
+
+ public Name() { }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Name)) {
+ return false;
+ }
+
+ Name n = ((Name) o);
+ return (family == n.family || family != null && family.equals(n.family)) &&
+ (given == n.given || given != null && given.equals(n.given)) &&
+ (middle == n.middle || middle != null && middle.equals(n.middle)) &&
+ (prefix == n.prefix || prefix != null && prefix.equals(n.prefix)) &&
+ (suffix == n.suffix || suffix != null && suffix.equals(n.suffix));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 23 * (family == null ? 0 : family.hashCode());
+ result = 23 * result + (given == null ? 0 : given.hashCode());
+ result = 23 * result + (middle == null ? 0 : middle.hashCode());
+ result = 23 * result + (prefix == null ? 0 : prefix.hashCode());
+ result = 23 * result + (suffix == null ? 0 : suffix.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Name: { family: ");
+ sb.append(family);
+ sb.append(" given: ");
+ sb.append(given);
+ sb.append(" middle: ");
+ sb.append(middle);
+ sb.append(" prefix: ");
+ sb.append(prefix);
+ sb.append(" suffix: ");
+ sb.append(suffix);
+ sb.append(" }");
+ return sb.toString();
+ }
+ }
+
+ public static class Phone {
+ public int type;
+ public String number;
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Phone)) {
+ return false;
+ }
+
+ Phone p = (Phone) o;
+ return (number == p.number || number != null && number.equals(p.number))
+ && type == p.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return 23 * type + number.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" Phone: { number: ");
+ sb.append(number);
+ sb.append(" type: " + type);
+ sb.append(" }");
+ return sb.toString();
+ }
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof PhonebookEntry) {
+ return equals((PhonebookEntry) object);
+ }
+ return false;
+ }
+
+ public PhonebookEntry() {
+ name = new Name();
+ phones = new ArrayList<Phone>();
+ }
+
+ public PhonebookEntry(VCardEntry v) {
+ name = new Name();
+ phones = new ArrayList<Phone>();
+
+ VCardEntry.NameData n = v.getNameData();
+ name.family = n.getFamily();
+ name.given = n.getGiven();
+ name.middle = n.getMiddle();
+ name.prefix = n.getPrefix();
+ name.suffix = n.getSuffix();
+
+ List<VCardEntry.PhoneData> vp = v.getPhoneList();
+ if (vp == null || vp.isEmpty()) {
+ return;
+ }
+
+ for (VCardEntry.PhoneData p : vp) {
+ Phone phone = new Phone();
+ phone.type = p.getType();
+ phone.number = p.getNumber();
+ phones.add(phone);
+ }
+ }
+
+ private boolean equals(PhonebookEntry p) {
+ return name.equals(p.name) && phones.equals(p.phones);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() + 23 * phones.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("PhonebookEntry { id: ");
+ sb.append(id);
+ sb.append(" ");
+ sb.append(name.toString());
+ sb.append(phones.toString());
+ sb.append(" }");
+ return sb.toString();
+ }
+
+ public Name name;
+ public List<Phone> phones;
+ public String id;
+}
diff --git a/src/com/android/bluetooth/pbapclient/PhonebookPullRequest.java b/src/com/android/bluetooth/pbapclient/PhonebookPullRequest.java
new file mode 100644
index 0000000..5e95aef
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/PhonebookPullRequest.java
@@ -0,0 +1,238 @@
+package com.android.bluetooth.pbapclient;
+
+import com.android.vcard.VCardEntry;
+
+import android.accounts.Account;
+import com.android.bluetooth.pbapclient.BluetoothPbapClient;
+import android.content.ContentProviderOperation;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.provider.ContactsContract;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.RawContactsEntity;
+import android.provider.ContactsContract.Contacts.Entity;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.util.Log;
+
+import com.android.vcard.VCardEntry;
+
+import java.lang.InterruptedException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class PhonebookPullRequest extends PullRequest {
+ private static final int MAX_OPS = 200;
+ private static final boolean DBG = true;
+ private static final String TAG = "PbapPhonebookPullRequest";
+
+ private final Account mAccount;
+ private final Context mContext;
+ public boolean complete = false;
+
+ public PhonebookPullRequest(Context context, Account account) {
+ mContext = context;
+ mAccount = account;
+ path = BluetoothPbapClient.PB_PATH;
+ }
+
+ private PhonebookEntry fetchContact(String id) {
+ PhonebookEntry entry = new PhonebookEntry();
+ entry.id = id;
+ Cursor c = null;
+ try {
+ c = mContext.getContentResolver().query(
+ Data.CONTENT_URI,
+ null,
+ Data.RAW_CONTACT_ID + " = ?",
+ new String[] { id },
+ null);
+ if (c != null) {
+ int mimeTypeIndex = c.getColumnIndex(Data.MIMETYPE);
+ int familyNameIndex = c.getColumnIndex(StructuredName.FAMILY_NAME);
+ int givenNameIndex = c.getColumnIndex(StructuredName.GIVEN_NAME);
+ int middleNameIndex = c.getColumnIndex(StructuredName.MIDDLE_NAME);
+ int prefixIndex = c.getColumnIndex(StructuredName.PREFIX);
+ int suffixIndex = c.getColumnIndex(StructuredName.SUFFIX);
+
+ int phoneTypeIndex = c.getColumnIndex(Phone.TYPE);
+ int phoneNumberIndex = c.getColumnIndex(Phone.NUMBER);
+
+ while (c.moveToNext()) {
+ String mimeType = c.getString(mimeTypeIndex);
+ if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
+ entry.name.family = c.getString(familyNameIndex);
+ entry.name.given = c.getString(givenNameIndex);
+ entry.name.middle = c.getString(middleNameIndex);
+ entry.name.prefix = c.getString(prefixIndex);
+ entry.name.suffix = c.getString(suffixIndex);
+ } else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
+ PhonebookEntry.Phone p = new PhonebookEntry.Phone();
+ p.type = c.getInt(phoneTypeIndex);
+ p.number = c.getString(phoneNumberIndex);
+ entry.phones.add(p);
+ }
+ }
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return entry;
+ }
+
+ private HashMap<PhonebookEntry.Name, PhonebookEntry> fetchExistingContacts() {
+ HashMap<PhonebookEntry.Name, PhonebookEntry> entries = new HashMap<>();
+
+ Cursor c = null;
+ try {
+ // First find all the contacts present. Fetch all rows.
+ Uri uri = RawContacts.CONTENT_URI.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.name)
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccount.type)
+ .build();
+ // First get all the raw contact ids.
+ c = mContext.getContentResolver().query(uri,
+ new String[] { RawContacts._ID },
+ null, null, null);
+
+ if (c != null) {
+ while (c.moveToNext()) {
+ // For each raw contact id, fetch all the data.
+ PhonebookEntry e = fetchContact(c.getString(0));
+ entries.put(e.name, e);
+ }
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ return entries;
+ }
+
+ private void addContacts(List<PhonebookEntry> entries)
+ throws RemoteException, OperationApplicationException, InterruptedException {
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+ for (PhonebookEntry e : entries) {
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedException();
+ }
+ int index = ops.size();
+ // Add an entry.
+ ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
+ .withValue(RawContacts.ACCOUNT_TYPE, mAccount.type)
+ .withValue(RawContacts.ACCOUNT_NAME, mAccount.name)
+ .build());
+
+ // Populate the name.
+ ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
+ .withValueBackReference(Data.RAW_CONTACT_ID, index)
+ .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(StructuredName.FAMILY_NAME , e.name.family)
+ .withValue(StructuredName.GIVEN_NAME , e.name.given)
+ .withValue(StructuredName.MIDDLE_NAME , e.name.middle)
+ .withValue(StructuredName.PREFIX , e.name.prefix)
+ .withValue(StructuredName.SUFFIX , e.name.suffix)
+ .build());
+
+ // Populate the phone number(s) if any.
+ for (PhonebookEntry.Phone p : e.phones) {
+ ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
+ .withValueBackReference(Data.RAW_CONTACT_ID, index)
+ .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
+ .withValue(Phone.NUMBER, p.number)
+ .withValue(Phone.TYPE, p.type)
+ .build());
+ }
+
+ // Commit MAX_OPS at a time so that the binder transaction doesn't get too large.
+ if (ops.size() > MAX_OPS) {
+ mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+ ops.clear();
+ }
+ }
+
+ if (ops.size() > 0) {
+ // Commit remaining entries.
+ mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+ }
+ }
+
+ private void deleteContacts(List<PhonebookEntry> entries)
+ throws RemoteException, OperationApplicationException {
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+ for (PhonebookEntry e : entries) {
+ ops.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI.buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .build())
+ .withSelection(RawContacts._ID + "=?", new String[] { e.id })
+ .build());
+ }
+ mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+ }
+
+ @Override
+ public void onPullComplete() {
+ if (mEntries == null) {
+ Log.e(TAG, "onPullComplete entries is null.");
+ return;
+ }
+
+ if (DBG) {
+ Log.d(TAG, "onPullComplete with " + mEntries.size() + " count.");
+ }
+ try {
+
+ HashMap<PhonebookEntry.Name, PhonebookEntry> contacts = fetchExistingContacts();
+
+ List<PhonebookEntry> contactsToAdd = new ArrayList<PhonebookEntry>();
+ List<PhonebookEntry> contactsToDelete = new ArrayList<PhonebookEntry>();
+
+ for (VCardEntry e : mEntries) {
+ PhonebookEntry current = new PhonebookEntry(e);
+ PhonebookEntry.Name key = current.name;
+
+ PhonebookEntry contact = contacts.get(key);
+ if (contact == null) {
+ contactsToAdd.add(current);
+ } else if (!contact.equals(current)) {
+ // Instead of trying to figure out what changed on an update, do a delete
+ // and an add. Sure, it churns contact ids but a contact being updated
+ // while someone is connected is a low enough frequency event that the
+ // complexity of doing an update is just not worth it.
+ contactsToAdd.add(current);
+ // Don't remove it from the hashmap so it will get deleted.
+ } else {
+ contacts.remove(key);
+ }
+ }
+ contactsToDelete.addAll(contacts.values());
+
+ if (!contactsToDelete.isEmpty()) {
+ deleteContacts(contactsToDelete);
+ }
+
+ if (!contactsToAdd.isEmpty()) {
+ addContacts(contactsToAdd);
+ }
+
+ Log.d(TAG, "Sync complete: add=" + contactsToAdd.size()
+ + " delete=" + contactsToDelete.size());
+ } catch (OperationApplicationException | RemoteException | NumberFormatException e) {
+ Log.d(TAG, "Got exception: ", e);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Interrupted durring insert.");
+ } finally {
+ complete = true;
+ }
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/PullRequest.java b/src/com/android/bluetooth/pbapclient/PullRequest.java
new file mode 100644
index 0000000..4944f85
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/PullRequest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import com.android.vcard.VCardEntry;
+
+import java.util.List;
+
+public abstract class PullRequest {
+ public String path;
+ protected List<VCardEntry> mEntries;
+ public abstract void onPullComplete();
+
+ @Override
+ public String toString() {
+ return "PullRequest: { path=" + path + " }";
+ }
+
+ public void setResults(List<VCardEntry> results) {
+ mEntries = results;
+ }
+}
+
diff --git a/src/com/android/bluetooth/pbapclient/utils/BmsgTokenizer.java b/src/com/android/bluetooth/pbapclient/utils/BmsgTokenizer.java
new file mode 100644
index 0000000..3888f3f
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/utils/BmsgTokenizer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient.utils;
+
+import android.util.Log;
+
+import java.text.ParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class BmsgTokenizer {
+
+ private final String mStr;
+
+ private final Matcher mMatcher;
+
+ private int mPos = 0;
+
+ private final int mOffset;
+
+ static public class Property {
+ public final String name;
+ public final String value;
+
+ public Property(String name, String value) {
+ if (name == null || value == null) {
+ throw new IllegalArgumentException();
+ }
+
+ this.name = name;
+ this.value = value;
+
+ Log.v("BMSG >> ", toString());
+ }
+
+ @Override
+ public String toString() {
+ return name + ":" + value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return ((o instanceof Property) && ((Property) o).name.equals(name) && ((Property) o).value
+ .equals(value));
+ }
+ };
+
+ public BmsgTokenizer(String str) {
+ this(str, 0);
+ }
+
+ public BmsgTokenizer(String str, int offset) {
+ mStr = str;
+ mOffset = offset;
+ mMatcher = Pattern.compile("(([^:]*):(.*))?\r\n").matcher(str);
+ mPos = mMatcher.regionStart();
+ }
+
+ public Property next(boolean alwaysReturn) throws ParseException {
+ boolean found = false;
+
+ do {
+ mMatcher.region(mPos, mMatcher.regionEnd());
+
+ if (!mMatcher.lookingAt()) {
+ if (alwaysReturn) {
+ return null;
+ }
+
+ throw new ParseException("Property or empty line expected", pos());
+ }
+
+ mPos = mMatcher.end();
+
+ if (mMatcher.group(1) != null) {
+ found = true;
+ }
+ } while (!found);
+
+ return new Property(mMatcher.group(2), mMatcher.group(3));
+ }
+
+ public Property next() throws ParseException {
+ return next(false);
+ }
+
+ public String remaining() {
+ return mStr.substring(mPos);
+ }
+
+ public int pos() {
+ return mPos + mOffset;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/utils/ObexAppParameters.java b/src/com/android/bluetooth/pbapclient/utils/ObexAppParameters.java
new file mode 100644
index 0000000..29027ee
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/utils/ObexAppParameters.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient.utils;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.obex.HeaderSet;
+
+public final class ObexAppParameters {
+
+ private final HashMap<Byte, byte[]> mParams;
+
+ public ObexAppParameters() {
+ mParams = new HashMap<Byte, byte[]>();
+ }
+
+ public ObexAppParameters(byte[] raw) {
+ mParams = new HashMap<Byte, byte[]>();
+
+ if (raw != null) {
+ for (int i = 0; i < raw.length;) {
+ if (raw.length - i < 2) {
+ break;
+ }
+
+ byte tag = raw[i++];
+ byte len = raw[i++];
+
+ if (raw.length - i - len < 0) {
+ break;
+ }
+
+ byte[] val = new byte[len];
+
+ System.arraycopy(raw, i, val, 0, len);
+ this.add(tag, val);
+
+ i += len;
+ }
+ }
+ }
+
+ public static ObexAppParameters fromHeaderSet(HeaderSet headerset) {
+ try {
+ byte[] raw = (byte[]) headerset.getHeader(HeaderSet.APPLICATION_PARAMETER);
+ return new ObexAppParameters(raw);
+ } catch (IOException e) {
+ // won't happen
+ }
+
+ return null;
+ }
+
+ public byte[] getHeader() {
+ int length = 0;
+
+ for (Map.Entry<Byte, byte[]> entry : mParams.entrySet()) {
+ length += (entry.getValue().length + 2);
+ }
+
+ byte[] ret = new byte[length];
+
+ int idx = 0;
+ for (Map.Entry<Byte, byte[]> entry : mParams.entrySet()) {
+ length = entry.getValue().length;
+
+ ret[idx++] = entry.getKey();
+ ret[idx++] = (byte) length;
+ System.arraycopy(entry.getValue(), 0, ret, idx, length);
+ idx += length;
+ }
+
+ return ret;
+ }
+
+ public void addToHeaderSet(HeaderSet headerset) {
+ if (mParams.size() > 0) {
+ headerset.setHeader(HeaderSet.APPLICATION_PARAMETER, getHeader());
+ }
+ }
+
+ public boolean exists(byte tag) {
+ return mParams.containsKey(tag);
+ }
+
+ public void add(byte tag, byte val) {
+ byte[] bval = ByteBuffer.allocate(1).put(val).array();
+ mParams.put(tag, bval);
+ }
+
+ public void add(byte tag, short val) {
+ byte[] bval = ByteBuffer.allocate(2).putShort(val).array();
+ mParams.put(tag, bval);
+ }
+
+ public void add(byte tag, int val) {
+ byte[] bval = ByteBuffer.allocate(4).putInt(val).array();
+ mParams.put(tag, bval);
+ }
+
+ public void add(byte tag, long val) {
+ byte[] bval = ByteBuffer.allocate(8).putLong(val).array();
+ mParams.put(tag, bval);
+ }
+
+ public void add(byte tag, String val) {
+ byte[] bval = val.getBytes();
+ mParams.put(tag, bval);
+ }
+
+ public void add(byte tag, byte[] bval) {
+ mParams.put(tag, bval);
+ }
+
+ public byte getByte(byte tag) {
+ byte[] bval = mParams.get(tag);
+
+ if (bval == null || bval.length < 1) {
+ return 0;
+ }
+
+ return ByteBuffer.wrap(bval).get();
+ }
+
+ public short getShort(byte tag) {
+ byte[] bval = mParams.get(tag);
+
+ if (bval == null || bval.length < 2) {
+ return 0;
+ }
+
+ return ByteBuffer.wrap(bval).getShort();
+ }
+
+ public int getInt(byte tag) {
+ byte[] bval = mParams.get(tag);
+
+ if (bval == null || bval.length < 4) {
+ return 0;
+ }
+
+ return ByteBuffer.wrap(bval).getInt();
+ }
+
+ public String getString(byte tag) {
+ byte[] bval = mParams.get(tag);
+
+ if (bval == null) {
+ return null;
+ }
+
+ return new String(bval);
+ }
+
+ public byte[] getByteArray(byte tag) {
+ byte[] bval = mParams.get(tag);
+
+ return bval;
+ }
+
+ @Override
+ public String toString() {
+ return mParams.toString();
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/utils/ObexTime.java b/src/com/android/bluetooth/pbapclient/utils/ObexTime.java
new file mode 100644
index 0000000..d4ee4e6
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/utils/ObexTime.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient.utils;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class ObexTime {
+
+ private Date mDate;
+
+ public ObexTime(String time) {
+ /*
+ * match OBEX time string: YYYYMMDDTHHMMSS with optional UTF offset
+ * +/-hhmm
+ */
+ Pattern p = Pattern
+ .compile("(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})(([+-])(\\d{2})(\\d{2}))?");
+ Matcher m = p.matcher(time);
+
+ if (m.matches()) {
+
+ /*
+ * matched groups are numberes as follows: YYYY MM DD T HH MM SS +
+ * hh mm ^^^^ ^^ ^^ ^^ ^^ ^^ ^ ^^ ^^ 1 2 3 4 5 6 8 9 10 all groups
+ * are guaranteed to be numeric so conversion will always succeed
+ * (except group 8 which is either + or -)
+ */
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)) - 1,
+ Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)),
+ Integer.parseInt(m.group(5)), Integer.parseInt(m.group(6)));
+
+ /*
+ * if 7th group is matched then we have UTC offset information
+ * included
+ */
+ if (m.group(7) != null) {
+ int ohh = Integer.parseInt(m.group(9));
+ int omm = Integer.parseInt(m.group(10));
+
+ /* time zone offset is specified in miliseconds */
+ int offset = (ohh * 60 + omm) * 60 * 1000;
+
+ if (m.group(8).equals("-")) {
+ offset = -offset;
+ }
+
+ TimeZone tz = TimeZone.getTimeZone("UTC");
+ tz.setRawOffset(offset);
+
+ cal.setTimeZone(tz);
+ }
+
+ mDate = cal.getTime();
+ }
+ }
+
+ public ObexTime(Date date) {
+ mDate = date;
+ }
+
+ public Date getTime() {
+ return mDate;
+ }
+
+ @Override
+ public String toString() {
+ if (mDate == null) {
+ return null;
+ }
+
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(mDate);
+
+ /* note that months are numbered stating from 0 */
+ return String.format(Locale.US, "%04d%02d%02dT%02d%02d%02d",
+ cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1,
+ cal.get(Calendar.DATE), cal.get(Calendar.HOUR_OF_DAY),
+ cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
+ }
+}
diff --git a/tests/src/com/android/bluetooth/tests/pbap/BluetoothPhabVcardManagerTest.java b/tests/src/com/android/bluetooth/tests/pbap/BluetoothPbapVcardManagerTest.java
similarity index 86%
rename from tests/src/com/android/bluetooth/tests/pbap/BluetoothPhabVcardManagerTest.java
rename to tests/src/com/android/bluetooth/tests/pbap/BluetoothPbapVcardManagerTest.java
index 72168b5..ec000a0 100644
--- a/tests/src/com/android/bluetooth/tests/pbap/BluetoothPhabVcardManagerTest.java
+++ b/tests/src/com/android/bluetooth/tests/pbap/BluetoothPbapVcardManagerTest.java
@@ -16,26 +16,43 @@
package com.android.bluetooth.tests.pbap;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockContentResolver;
-
import com.android.bluetooth.pbap.BluetoothPbapObexServer;
import com.android.bluetooth.pbap.BluetoothPbapVcardManager;
import com.android.bluetooth.tests.mock.BluetoothMockContext;
import com.android.bluetooth.tests.mock.SimpleMockContentProvider;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.PhoneLookup;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.TextUtils;
+
+
import java.util.ArrayList;
-public class BluetoothPhabVcardManagerTest extends AndroidTestCase {
+public class BluetoothPbapVcardManagerTest extends AndroidTestCase {
- public void testGetContactNamesByNumber() {
- MatrixCursor mc = new MatrixCursor(
- new String[]{ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME});
+ public void testGetContactNamesByNumberWithEmptyPhoneNumber() {
+ getContactNamesByNumberInternal("");
+ }
+
+ public void testGetContactNamesByNumberWithPhoneNumber() {
+ getContactNamesByNumberInternal("111-111-111");
+ }
+
+ private void getContactNamesByNumberInternal(String phoneNumber) {
+ String[] columnNames;
+ if (TextUtils.isEmpty(phoneNumber)) {
+ columnNames = new String[]{Phone.CONTACT_ID, Phone.DISPLAY_NAME};
+ } else {
+ columnNames = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
+ }
+
+ MatrixCursor mc = new MatrixCursor(columnNames);
mc.addRow(new Object[]{1L, "A"});
mc.addRow(new Object[]{1L, "A (1)"});
mc.addRow(new Object[]{2L, "B"});
@@ -45,7 +62,7 @@
mc.addRow(new Object[]{3L, "C (2)"});
mc.addRow(new Object[]{4L, "D"});
BluetoothPbapVcardManager manager = createBluetoothPbapVcardManager(mc);
- ArrayList<String> nameList = manager.getContactNamesByNumber("111-111-111");
+ ArrayList<String> nameList = manager.getContactNamesByNumber(phoneNumber);
// If there are multiple display name per id, first one is picked.
assertEquals("A,1", nameList.get(0));