diff --git a/AndroidTestTemplate.xml b/AndroidTestTemplate.xml
index 4fb4bf9f76c1ee696673292530aa57f9e439146d..8332422b344895846238a5780915bfb73eb23fbf 100644
--- a/AndroidTestTemplate.xml
+++ b/AndroidTestTemplate.xml
@@ -24,7 +24,8 @@
-
+
+
@@ -38,6 +39,7 @@
diff --git a/TEST_MAPPING b/TEST_MAPPING
old mode 100755
new mode 100644
index 69a0709acb61c1dcbddbfd49052dbfbc469a1d49..65f2fefd19eeb4efa4c28a7848210d4673a427c9
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -62,6 +62,12 @@
{
"name" : "net_test_btif_hf_client_service"
},
+ {
+ "name" : "libaptx_enc_tests"
+ },
+ {
+ "name" : "libaptxhd_enc_tests"
+ },
{
"name" : "net_test_stack_btm"
}
diff --git a/android/BluetoothLegacyMigration/Android.bp b/android/BluetoothLegacyMigration/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..b755fa0895a43b0329b1aa30c099bf00e4417c43
--- /dev/null
+++ b/android/BluetoothLegacyMigration/Android.bp
@@ -0,0 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+ name: "BluetoothLegacyMigration",
+
+ srcs: [ "BluetoothLegacyMigration.kt" ],
+
+ // Must match Bluetooth.apk certificate because of sharedUserId
+ certificate: ":com.android.bluetooth.certificate",
+ platform_apis: true,
+}
diff --git a/android/BluetoothLegacyMigration/AndroidManifest.xml b/android/BluetoothLegacyMigration/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86989f9b0a19a9cb3eb3e1f7d82c84f1dfcccb72
--- /dev/null
+++ b/android/BluetoothLegacyMigration/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/android/BluetoothLegacyMigration/BluetoothLegacyMigration.kt b/android/BluetoothLegacyMigration/BluetoothLegacyMigration.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9c88a061385bf831329be043a13a2c31225c7d67
--- /dev/null
+++ b/android/BluetoothLegacyMigration/BluetoothLegacyMigration.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2022 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.bluetooth
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Context
+import android.content.UriMatcher
+import android.database.Cursor
+import android.database.sqlite.SQLiteDatabase
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+
+/**
+ * Define an implementation of ContentProvider for the Bluetooth migration
+ */
+class BluetoothLegacyMigration: ContentProvider() {
+ companion object {
+ private const val TAG = "BluetoothLegacyMigration"
+
+ private const val AUTHORITY = "bluetooth_legacy.provider"
+
+ private const val START_LEGACY_MIGRATION_CALL = "start_legacy_migration"
+ private const val FINISH_LEGACY_MIGRATION_CALL = "finish_legacy_migration"
+
+ private const val PHONEBOOK_ACCESS_PERMISSION = "phonebook_access_permission"
+ private const val MESSAGE_ACCESS_PERMISSION = "message_access_permission"
+ private const val SIM_ACCESS_PERMISSION = "sim_access_permission"
+
+ private const val VOLUME_MAP = "bluetooth_volume_map"
+
+ private const val OPP = "OPPMGR"
+ private const val BLUETOOTH_OPP_CHANNEL = "btopp_channels"
+ private const val BLUETOOTH_OPP_NAME = "btopp_names"
+
+ private const val BLUETOOTH_SIGNED_DEFAULT = "com.google.android.bluetooth_preferences"
+
+ private const val KEY_LIST = "key_list"
+
+ private enum class UriId(
+ val fileName: String,
+ val handler: (ctx: Context) -> DatabaseHandler
+ ) {
+ BLUETOOTH(BluetoothDatabase.DATABASE_NAME, ::BluetoothDatabase),
+ OPP(OppDatabase.DATABASE_NAME, ::OppDatabase),
+ }
+
+ private val URI_MATCHER = UriMatcher(UriMatcher.NO_MATCH).apply {
+ UriId.values().map { addURI(AUTHORITY, it.fileName, it.ordinal) }
+ }
+
+ private fun putObjectInBundle(bundle: Bundle, key: String, obj: Any?) {
+ when (obj) {
+ is Boolean -> bundle.putBoolean(key, obj)
+ is Int -> bundle.putInt(key, obj)
+ is Long -> bundle.putLong(key, obj)
+ is String -> bundle.putString(key, obj)
+ null -> throw UnsupportedOperationException("null type is not handled")
+ else -> throw UnsupportedOperationException("${obj.javaClass.simpleName}: type is not handled")
+ }
+ }
+ }
+
+ private lateinit var mContext: Context
+
+ /**
+ * Always return true, indicating that the
+ * provider loaded correctly.
+ */
+ override fun onCreate(): Boolean {
+ mContext = context!!.createDeviceProtectedStorageContext()
+ return true
+ }
+
+ /**
+ * Use a content URI to get database name associated
+ *
+ * @param uri Content uri
+ * @return A {@link Cursor} containing the results of the query.
+ */
+ override fun getType(uri: Uri): String {
+ val database = UriId.values().firstOrNull { it.ordinal == URI_MATCHER.match(uri) }
+ ?: throw UnsupportedOperationException("This Uri is not supported: $uri")
+ return database.fileName
+ }
+
+ /**
+ * Use a content URI to get information about a database
+ *
+ * @param uri Content uri
+ * @param projection unused
+ * @param selection unused
+ * @param selectionArgs unused
+ * @param sortOrder unused
+ * @return A {@link Cursor} containing the results of the query.
+ *
+ */
+ @Override
+ override fun query(
+ uri: Uri,
+ projection: Array?,
+ selection: String?,
+ selectionArgs: Array?,
+ sortOrder: String?
+ ): Cursor? {
+ val database = UriId.values().firstOrNull { it.ordinal == URI_MATCHER.match(uri) }
+ ?: throw UnsupportedOperationException("This Uri is not supported: $uri")
+ return database.handler(mContext).toCursor()
+ }
+
+ /**
+ * insert() is not supported
+ */
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ throw UnsupportedOperationException()
+ }
+
+ /**
+ * delete() is not supported
+ */
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
+ throw UnsupportedOperationException()
+ }
+
+ /**
+ * update() is not supported
+ */
+ override fun update(
+ uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?
+ ): Int {
+ throw UnsupportedOperationException()
+ }
+
+ abstract class MigrationHandler {
+ abstract fun toBundle(): Bundle?
+ abstract fun delete()
+ }
+
+ private class SharedPreferencesHandler(private val ctx: Context, private val key: String) :
+ MigrationHandler() {
+
+ override fun toBundle(): Bundle? {
+ val pref = ctx.getSharedPreferences(key, Context.MODE_PRIVATE)
+ if (pref.all.isEmpty()) {
+ Log.d(TAG, "No migration needed for shared preference: $key")
+ return null
+ }
+ val bundle = Bundle()
+ val keys = arrayListOf()
+ for (e in pref.all) {
+ keys += e.key
+ putObjectInBundle(bundle, e.key, e.value)
+ }
+ bundle.putStringArrayList(KEY_LIST, keys)
+ Log.d(TAG, "SharedPreferences migrating ${keys.size} key(s) from $key")
+ return bundle
+ }
+
+ override fun delete() {
+ ctx.deleteSharedPreferences(key)
+ Log.d(TAG, "$key: SharedPreferences deleted")
+ }
+ }
+
+ abstract class DatabaseHandler(private val ctx: Context, private val dbName: String) :
+ MigrationHandler() {
+
+ abstract val sql: String
+
+ fun toCursor(): Cursor? {
+ val databasePath = ctx.getDatabasePath(dbName)
+ if (!databasePath.exists()) {
+ Log.d(TAG, "No migration needed for database: $dbName")
+ return null
+ }
+ val db = SQLiteDatabase.openDatabase(
+ databasePath,
+ SQLiteDatabase.OpenParams.Builder().addOpenFlags(SQLiteDatabase.OPEN_READONLY)
+ .build()
+ )
+ return db.rawQuery(sql, null)
+ }
+
+ override fun toBundle(): Bundle? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun delete() {
+ val databasePath = ctx.getDatabasePath(dbName)
+ databasePath.delete()
+ Log.d(TAG, "$dbName: database deleted")
+ }
+ }
+
+ private class BluetoothDatabase(ctx: Context) : DatabaseHandler(ctx, DATABASE_NAME) {
+ companion object {
+ const val DATABASE_NAME = "bluetooth_db"
+ }
+ private val dbTable = "metadata"
+ override val sql = "select * from $dbTable"
+ }
+
+ private class OppDatabase(ctx: Context) : DatabaseHandler(ctx, DATABASE_NAME) {
+ companion object {
+ const val DATABASE_NAME = "btopp.db"
+ }
+ private val dbTable = "btopp"
+ override val sql = "select * from $dbTable"
+ }
+
+ /**
+ * Fetch legacy data describe by {@code arg} and perform {@code method} action on it
+ *
+ * @param method Action to perform. One of START_LEGACY_MIGRATION_CALL|FINISH_LEGACY_MIGRATION_CALL
+ * @param arg item on witch to perform the action specified by {@code method}
+ * @param extras unused
+ * @return A {@link Bundle} containing the results of the query.
+ */
+ override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
+ val migrationHandler = when (arg) {
+ OPP,
+ VOLUME_MAP,
+ BLUETOOTH_OPP_NAME,
+ BLUETOOTH_OPP_CHANNEL,
+ SIM_ACCESS_PERMISSION,
+ MESSAGE_ACCESS_PERMISSION,
+ PHONEBOOK_ACCESS_PERMISSION -> SharedPreferencesHandler(mContext, arg)
+ BLUETOOTH_SIGNED_DEFAULT -> {
+ val key = mContext.packageName + "_preferences"
+ SharedPreferencesHandler(mContext, key)
+ }
+ BluetoothDatabase.DATABASE_NAME -> BluetoothDatabase(mContext)
+ OppDatabase.DATABASE_NAME -> OppDatabase(mContext)
+ else -> throw UnsupportedOperationException()
+ }
+ return when (method) {
+ START_LEGACY_MIGRATION_CALL -> migrationHandler.toBundle()
+ FINISH_LEGACY_MIGRATION_CALL -> {
+ migrationHandler.delete()
+ return null
+ }
+ else -> throw UnsupportedOperationException()
+ }
+ }
+}
diff --git a/android/BluetoothLegacyMigration/OWNERS b/android/BluetoothLegacyMigration/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..8f5c903d7e8759d22a59ff162a43fd73a6309d16
--- /dev/null
+++ b/android/BluetoothLegacyMigration/OWNERS
@@ -0,0 +1,8 @@
+# Reviewers for /android/BluetoothLegacyMigration
+
+eruffieux@google.com
+rahulsabnis@google.com
+sattiraju@google.com
+siyuanh@google.com
+wescande@google.com
+zachoverflow@google.com
diff --git a/android/BluetoothLegacyMigration/res/mipmap-anydpi/bt_share.xml b/android/BluetoothLegacyMigration/res/mipmap-anydpi/bt_share.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ff00b0d77b333e0fe27bfcb8eceeba52219f80a3
--- /dev/null
+++ b/android/BluetoothLegacyMigration/res/mipmap-anydpi/bt_share.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/android/app/Android.bp b/android/app/Android.bp
index 25f35eafa32db7331c71c122917cdca5f78b93ed..7c65bf25e6d0902095665b997104a053cfd4b34e 100644
--- a/android/app/Android.bp
+++ b/android/app/Android.bp
@@ -69,12 +69,6 @@ cc_library_shared {
"libbluetooth",
"libc++fs",
],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- "-Wno-unused-parameter",
- ],
sanitize: {
scs: true,
},
@@ -114,7 +108,7 @@ android_app {
static_libs: [
"android.hardware.radio-V1.0-java",
"androidx.core_core",
- "androidx.legacy_legacy-support-v4",
+ "androidx.media_media",
"androidx.lifecycle_lifecycle-livedata",
"androidx.room_room-runtime",
"androidx.annotation_annotation",
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index ba69e740aca8c3abec353312ed8e43e2c13b974f..f25e208d977c2e8c79c101f5bd961f880a1fe41c 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -1810,6 +1810,35 @@ static jboolean allowLowLatencyAudioNative(JNIEnv* env, jobject obj,
return true;
}
+static void metadataChangedNative(JNIEnv* env, jobject obj, jbyteArray address,
+ jint key, jbyteArray value) {
+ ALOGV("%s", __func__);
+ if (!sBluetoothInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+ if (addr == nullptr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ RawAddress addr_obj = {};
+ addr_obj.FromOctets((uint8_t*)addr);
+
+ if (value == NULL) {
+ ALOGE("metadataChangedNative() ignoring NULL array");
+ return;
+ }
+
+ uint16_t len = (uint16_t)env->GetArrayLength(value);
+ jbyte* p_value = env->GetByteArrayElements(value, NULL);
+ if (p_value == NULL) return;
+
+ std::vector val_vec(reinterpret_cast(p_value),
+ reinterpret_cast(p_value + len));
+ env->ReleaseByteArrayElements(value, p_value, 0);
+
+ sBluetoothInterface->metadata_changed(addr_obj, key, std::move(val_vec));
+ return;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void*)classInitNative},
@@ -1852,6 +1881,7 @@ static JNINativeMethod sMethods[] = {
{"requestMaximumTxDataLengthNative", "([B)V",
(void*)requestMaximumTxDataLengthNative},
{"allowLowLatencyAudioNative", "(Z[B)Z", (void*)allowLowLatencyAudioNative},
+ {"metadataChangedNative", "([BI[B)V", (void*)metadataChangedNative},
};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp
index 82aa164959f020e0387ec0fc4f5958d6243ceebf..03517fc645eba3800b4585cdd92e95987c22a4da 100644
--- a/android/app/jni/com_android_bluetooth_gatt.cpp
+++ b/android/app/jni/com_android_bluetooth_gatt.cpp
@@ -199,8 +199,9 @@ static std::shared_mutex callbacks_mutex;
*/
void btgattc_register_app_cb(int status, int clientIf, const Uuid& app_uuid) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,
clientIf, UUID_PARAMS(app_uuid));
}
@@ -212,8 +213,9 @@ void btgattc_scan_result_cb(uint16_t event_type, uint8_t addr_type,
uint16_t periodic_adv_int,
std::vector adv_data,
RawAddress* original_bda) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), bda));
@@ -233,8 +235,9 @@ void btgattc_scan_result_cb(uint16_t event_type, uint8_t addr_type,
void btgattc_open_cb(int conn_id, int status, int clientIf,
const RawAddress& bda) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -244,8 +247,9 @@ void btgattc_open_cb(int conn_id, int status, int clientIf,
void btgattc_close_cb(int conn_id, int status, int clientIf,
const RawAddress& bda) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -254,8 +258,9 @@ void btgattc_close_cb(int conn_id, int status, int clientIf,
}
void btgattc_search_complete_cb(int conn_id, int status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSearchCompleted, conn_id,
status);
@@ -263,16 +268,18 @@ void btgattc_search_complete_cb(int conn_id, int status) {
void btgattc_register_for_notification_cb(int conn_id, int registered,
int status, uint16_t handle) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRegisterForNotifications,
conn_id, status, registered, handle);
}
void btgattc_notify_cb(int conn_id, const btgatt_notify_params_t& p_data) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(
sCallbackEnv.get(), bdaddr2newjstr(sCallbackEnv.get(), &p_data.bda));
@@ -288,8 +295,9 @@ void btgattc_notify_cb(int conn_id, const btgatt_notify_params_t& p_data) {
void btgattc_read_characteristic_cb(int conn_id, int status,
btgatt_read_params_t* p_data) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(), NULL);
if (status == 0) { // Success
@@ -308,8 +316,9 @@ void btgattc_read_characteristic_cb(int conn_id, int status,
void btgattc_write_characteristic_cb(int conn_id, int status, uint16_t handle,
uint16_t len, const uint8_t* value) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(), NULL);
jb.reset(sCallbackEnv->NewByteArray(len));
@@ -319,8 +328,9 @@ void btgattc_write_characteristic_cb(int conn_id, int status, uint16_t handle,
}
void btgattc_execute_write_cb(int conn_id, int status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExecuteCompleted,
conn_id, status);
@@ -328,8 +338,9 @@ void btgattc_execute_write_cb(int conn_id, int status) {
void btgattc_read_descriptor_cb(int conn_id, int status,
const btgatt_read_params_t& p_data) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(), NULL);
if (p_data.value.len != 0) {
@@ -346,8 +357,9 @@ void btgattc_read_descriptor_cb(int conn_id, int status,
void btgattc_write_descriptor_cb(int conn_id, int status, uint16_t handle,
uint16_t len, const uint8_t* value) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(), NULL);
jb.reset(sCallbackEnv->NewByteArray(len));
@@ -358,8 +370,9 @@ void btgattc_write_descriptor_cb(int conn_id, int status, uint16_t handle,
void btgattc_remote_rssi_cb(int client_if, const RawAddress& bda, int rssi,
int status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -369,23 +382,26 @@ void btgattc_remote_rssi_cb(int client_if, const RawAddress& bda, int rssi,
}
void btgattc_configure_mtu_cb(int conn_id, int status, int mtu) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConfigureMTU, conn_id,
status, mtu);
}
void btgattc_congestion_cb(int conn_id, bool congested) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientCongestion,
conn_id, congested);
}
void btgattc_batchscan_reports_cb(int client_if, int status, int report_format,
int num_records, std::vector data) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(),
sCallbackEnv->NewByteArray(data.size()));
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(),
@@ -396,15 +412,17 @@ void btgattc_batchscan_reports_cb(int client_if, int status, int report_format,
}
void btgattc_batchscan_threshold_cb(int client_if) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj,
method_onBatchScanThresholdCrossed, client_if);
}
void btgattc_track_adv_event_cb(btgatt_track_adv_info_t* p_adv_track_info) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(
sCallbackEnv.get(),
@@ -506,8 +524,9 @@ void fillGattDbElementArray(JNIEnv* env, jobject* array,
void btgattc_get_gatt_db_cb(int conn_id, const btgatt_db_element_t* db,
int count) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
jclass arrayListclazz = sCallbackEnv->FindClass("java/util/ArrayList");
ScopedLocalRef array(
@@ -525,8 +544,9 @@ void btgattc_get_gatt_db_cb(int conn_id, const btgatt_db_element_t* db,
void btgattc_phy_updated_cb(int conn_id, uint8_t tx_phy, uint8_t rx_phy,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientPhyUpdate, conn_id,
tx_phy, rx_phy, status);
@@ -534,16 +554,18 @@ void btgattc_phy_updated_cb(int conn_id, uint8_t tx_phy, uint8_t rx_phy,
void btgattc_conn_updated_cb(int conn_id, uint16_t interval, uint16_t latency,
uint16_t timeout, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientConnUpdate,
conn_id, interval, latency, timeout, status);
}
void btgattc_service_changed_cb(int conn_id) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceChanged, conn_id);
}
@@ -583,16 +605,18 @@ static const btgatt_client_callbacks_t sGattClientCallbacks = {
*/
void btgatts_register_app_cb(int status, int server_if, const Uuid& uuid) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerRegistered, status,
server_if, UUID_PARAMS(uuid));
}
void btgatts_connection_cb(int conn_id, int server_if, int connected,
const RawAddress& bda) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -603,8 +627,9 @@ void btgatts_connection_cb(int conn_id, int server_if, int connected,
void btgatts_service_added_cb(int status, int server_if,
const btgatt_db_element_t* service,
size_t service_count) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
jclass arrayListclazz = sCallbackEnv->FindClass("java/util/ArrayList");
ScopedLocalRef array(
@@ -620,15 +645,17 @@ void btgatts_service_added_cb(int status, int server_if,
}
void btgatts_service_stopped_cb(int status, int server_if, int srvc_handle) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceStopped, status,
server_if, srvc_handle);
}
void btgatts_service_deleted_cb(int status, int server_if, int srvc_handle) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceDeleted, status,
server_if, srvc_handle);
}
@@ -637,8 +664,9 @@ void btgatts_request_read_characteristic_cb(int conn_id, int trans_id,
const RawAddress& bda,
int attr_handle, int offset,
bool is_long) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -650,8 +678,9 @@ void btgatts_request_read_characteristic_cb(int conn_id, int trans_id,
void btgatts_request_read_descriptor_cb(int conn_id, int trans_id,
const RawAddress& bda, int attr_handle,
int offset, bool is_long) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -666,8 +695,9 @@ void btgatts_request_write_characteristic_cb(int conn_id, int trans_id,
bool need_rsp, bool is_prep,
const uint8_t* value,
size_t length) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -685,8 +715,9 @@ void btgatts_request_write_descriptor_cb(int conn_id, int trans_id,
int offset, bool need_rsp,
bool is_prep, const uint8_t* value,
size_t length) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -701,8 +732,9 @@ void btgatts_request_write_descriptor_cb(int conn_id, int trans_id,
void btgatts_request_exec_write_cb(int conn_id, int trans_id,
const RawAddress& bda, int exec_write) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -711,37 +743,42 @@ void btgatts_request_exec_write_cb(int conn_id, int trans_id,
}
void btgatts_response_confirmation_cb(int status, int handle) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onResponseSendCompleted,
status, handle);
}
void btgatts_indication_sent_cb(int conn_id, int status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNotificationSent,
conn_id, status);
}
void btgatts_congestion_cb(int conn_id, bool congested) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerCongestion,
conn_id, congested);
}
void btgatts_mtu_changed_cb(int conn_id, int mtu) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerMtuChanged,
conn_id, mtu);
}
void btgatts_phy_updated_cb(int conn_id, uint8_t tx_phy, uint8_t rx_phy,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerPhyUpdate, conn_id,
tx_phy, rx_phy, status);
@@ -749,8 +786,9 @@ void btgatts_phy_updated_cb(int conn_id, uint8_t tx_phy, uint8_t rx_phy,
void btgatts_conn_updated_cb(int conn_id, uint16_t interval, uint16_t latency,
uint16_t timeout, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerConnUpdate,
conn_id, interval, latency, timeout, status);
@@ -890,15 +928,17 @@ class JniScanningCallbacks : ScanningCallbacks {
void OnScannerRegistered(const Uuid app_uuid, uint8_t scannerId,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScannerRegistered,
status, scannerId, UUID_PARAMS(app_uuid));
}
void OnSetScannerParameterComplete(uint8_t scannerId, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(
mCallbacksObj, method_onScanParamSetupCompleted, status, scannerId);
}
@@ -907,8 +947,9 @@ class JniScanningCallbacks : ScanningCallbacks {
uint8_t primary_phy, uint8_t secondary_phy,
uint8_t advertising_sid, int8_t tx_power, int8_t rssi,
uint16_t periodic_adv_int, std::vector adv_data) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -932,8 +973,9 @@ class JniScanningCallbacks : ScanningCallbacks {
}
void OnTrackAdvFoundLost(AdvertisingTrackInfo track_info) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(
sCallbackEnv.get(),
@@ -973,8 +1015,9 @@ class JniScanningCallbacks : ScanningCallbacks {
void OnBatchScanReports(int client_if, int status, int report_format,
int num_records, std::vector data) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(),
sCallbackEnv->NewByteArray(data.size()));
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(),
@@ -986,8 +1029,9 @@ class JniScanningCallbacks : ScanningCallbacks {
}
void OnBatchScanThresholdCrossed(int client_if) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj,
method_onBatchScanThresholdCrossed, client_if);
}
@@ -996,6 +1040,7 @@ class JniScanningCallbacks : ScanningCallbacks {
uint8_t sid, uint8_t address_type,
RawAddress address, uint8_t phy,
uint16_t interval) override {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
if (!mPeriodicScanCallbacksObj) {
@@ -1013,8 +1058,9 @@ class JniScanningCallbacks : ScanningCallbacks {
void OnPeriodicSyncReport(uint16_t sync_handle, int8_t tx_power, int8_t rssi,
uint8_t data_status,
std::vector data) override {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mPeriodicScanCallbacksObj) return;
ScopedLocalRef jb(sCallbackEnv.get(),
sCallbackEnv->NewByteArray(data.size()));
@@ -1027,8 +1073,9 @@ class JniScanningCallbacks : ScanningCallbacks {
}
void OnPeriodicSyncLost(uint16_t sync_handle) override {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mPeriodicScanCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncLost,
sync_handle);
@@ -1036,6 +1083,7 @@ class JniScanningCallbacks : ScanningCallbacks {
void OnPeriodicSyncTransferred(int pa_source, uint8_t status,
RawAddress address) override {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
if (!mPeriodicScanCallbacksObj) {
@@ -1168,6 +1216,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
static const bt_interface_t* btIf;
static void initializeNative(JNIEnv* env, jobject object) {
+ std::unique_lock lock(callbacks_mutex);
if (btIf) return;
btIf = getBluetoothInterface();
@@ -1210,6 +1259,8 @@ static void initializeNative(JNIEnv* env, jobject object) {
}
static void cleanupNative(JNIEnv* env, jobject object) {
+ std::unique_lock lock(callbacks_mutex);
+
if (!btIf) return;
if (sGattIf != NULL) {
@@ -1250,8 +1301,9 @@ static void gattClientUnregisterAppNative(JNIEnv* env, jobject object,
void btgattc_register_scanner_cb(const Uuid& app_uuid, uint8_t scannerId,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScannerRegistered,
status, scannerId, UUID_PARAMS(app_uuid));
}
@@ -1305,8 +1357,9 @@ static void gattClientSetPreferredPhyNative(JNIEnv* env, jobject object,
static void readClientPhyCb(uint8_t clientIf, RawAddress bda, uint8_t tx_phy,
uint8_t rx_phy, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -1451,8 +1504,9 @@ static void gattClientReadRemoteRssiNative(JNIEnv* env, jobject object,
}
void set_scan_params_cmpl_cb(int client_if, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanParamSetupCompleted,
status, client_if);
}
@@ -1468,8 +1522,9 @@ static void gattSetScanParametersNative(JNIEnv* env, jobject object,
void scan_filter_param_cb(uint8_t client_if, uint8_t avbl_space, uint8_t action,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj,
method_onScanFilterParamsConfigured, action,
status, client_if, avbl_space);
@@ -1547,8 +1602,9 @@ static void gattClientScanFilterParamClearAllNative(JNIEnv* env, jobject object,
static void scan_filter_cfg_cb(uint8_t client_if, uint8_t filt_type,
uint8_t avbl_space, uint8_t action,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanFilterConfig, action,
status, client_if, filt_type, avbl_space);
}
@@ -1697,8 +1753,9 @@ static void gattClientScanFilterClearNative(JNIEnv* env, jobject object,
}
void scan_enable_cb(uint8_t client_if, uint8_t action, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanFilterEnableDisabled,
action, status, client_if);
}
@@ -1729,8 +1786,9 @@ static void gattConnectionParameterUpdateNative(JNIEnv* env, jobject object,
}
void batchscan_cfg_storage_cb(uint8_t client_if, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(
mCallbacksObj, method_onBatchScanStorageConfigured, status, client_if);
}
@@ -1746,8 +1804,9 @@ static void gattClientConfigBatchScanStorageNative(
}
void batchscan_enable_cb(uint8_t client_if, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBatchScanStartStopped,
0 /* unused */, status, client_if);
}
@@ -1820,8 +1879,9 @@ static void gattServerSetPreferredPhyNative(JNIEnv* env, jobject object,
static void readServerPhyCb(uint8_t serverIf, RawAddress bda, uint8_t tx_phy,
uint8_t rx_phy, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mCallbacksObj) return;
ScopedLocalRef address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bda));
@@ -1964,7 +2024,6 @@ static void gattServerSendResponseNative(JNIEnv* env, jobject object,
if (env->GetArrayLength(val) < BTGATT_MAX_ATTR_LEN) {
response.attr_value.len = (uint16_t)env->GetArrayLength(val);
} else {
- android_errorWriteLog(0x534e4554, "78787521");
response.attr_value.len = BTGATT_MAX_ATTR_LEN;
}
@@ -2000,7 +2059,7 @@ static void advertiseClassInitNative(JNIEnv* env, jclass clazz) {
}
static void advertiseInitializeNative(JNIEnv* env, jobject object) {
- std::shared_lock lock(callbacks_mutex);
+ std::unique_lock lock(callbacks_mutex);
if (mAdvertiseCallbacksObj != NULL) {
ALOGW("Cleaning up Advertise callback object");
env->DeleteGlobalRef(mAdvertiseCallbacksObj);
@@ -2011,7 +2070,7 @@ static void advertiseInitializeNative(JNIEnv* env, jobject object) {
}
static void advertiseCleanupNative(JNIEnv* env, jobject object) {
- std::shared_lock lock(callbacks_mutex);
+ std::unique_lock lock(callbacks_mutex);
if (mAdvertiseCallbacksObj != NULL) {
env->DeleteGlobalRef(mAdvertiseCallbacksObj);
mAdvertiseCallbacksObj = NULL;
@@ -2100,8 +2159,9 @@ static PeriodicAdvertisingParameters parsePeriodicParams(JNIEnv* env,
static void ble_advertising_set_started_cb(int reg_id, uint8_t advertiser_id,
int8_t tx_power, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
method_onAdvertisingSetStarted, reg_id,
advertiser_id, tx_power, status);
@@ -2109,8 +2169,9 @@ static void ble_advertising_set_started_cb(int reg_id, uint8_t advertiser_id,
static void ble_advertising_set_timeout_cb(uint8_t advertiser_id,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
method_onAdvertisingEnabled, advertiser_id,
false, status);
@@ -2160,8 +2221,9 @@ static void stopAdvertisingSetNative(JNIEnv* env, jobject object,
static void getOwnAddressCb(uint8_t advertiser_id, uint8_t address_type,
RawAddress address) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
ScopedLocalRef addr(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &address));
@@ -2178,15 +2240,17 @@ static void getOwnAddressNative(JNIEnv* env, jobject object,
static void callJniCallback(jmethodID method, uint8_t advertiser_id,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, method, advertiser_id,
status);
}
static void enableSetCb(uint8_t advertiser_id, bool enable, uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
method_onAdvertisingEnabled, advertiser_id,
enable, status);
@@ -2224,8 +2288,9 @@ static void setScanResponseDataNative(JNIEnv* env, jobject object,
static void setAdvertisingParametersNativeCb(uint8_t advertiser_id,
uint8_t status, int8_t tx_power) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
method_onAdvertisingParametersUpdated,
advertiser_id, tx_power, status);
@@ -2268,8 +2333,9 @@ static void setPeriodicAdvertisingDataNative(JNIEnv* env, jobject object,
static void enablePeriodicSetCb(uint8_t advertiser_id, bool enable,
uint8_t status) {
+ std::shared_lock lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid()) return;
+ if (!sCallbackEnv.valid() || !mAdvertiseCallbacksObj) return;
sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
method_onPeriodicAdvertisingEnabled,
advertiser_id, enable, status);
@@ -2295,6 +2361,7 @@ static void periodicScanClassInitNative(JNIEnv* env, jclass clazz) {
}
static void periodicScanInitializeNative(JNIEnv* env, jobject object) {
+ std::unique_lock lock(callbacks_mutex);
if (mPeriodicScanCallbacksObj != NULL) {
ALOGW("Cleaning up periodic scan callback object");
env->DeleteGlobalRef(mPeriodicScanCallbacksObj);
@@ -2305,6 +2372,7 @@ static void periodicScanInitializeNative(JNIEnv* env, jobject object) {
}
static void periodicScanCleanupNative(JNIEnv* env, jobject object) {
+ std::unique_lock lock(callbacks_mutex);
if (mPeriodicScanCallbacksObj != NULL) {
env->DeleteGlobalRef(mPeriodicScanCallbacksObj);
mPeriodicScanCallbacksObj = NULL;
diff --git a/android/app/jni/com_android_bluetooth_hfp.cpp b/android/app/jni/com_android_bluetooth_hfp.cpp
index fffaf8ccb48d014195a9f289c5959296ce2a9808..61be15f3d0243f1897aef6f2462c949b4c7f99c5 100644
--- a/android/app/jni/com_android_bluetooth_hfp.cpp
+++ b/android/app/jni/com_android_bluetooth_hfp.cpp
@@ -181,7 +181,6 @@ class JniHeadsetCallbacks : bluetooth::headset::Callbacks {
char null_str[] = "";
if (!sCallbackEnv.isValidUtf(number)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: number is not a valid UTF string.", __func__);
number = null_str;
}
@@ -325,7 +324,6 @@ class JniHeadsetCallbacks : bluetooth::headset::Callbacks {
char null_str[] = "";
if (!sCallbackEnv.isValidUtf(at_string)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: at_string is not a valid UTF string.", __func__);
at_string = null_str;
}
@@ -361,7 +359,6 @@ class JniHeadsetCallbacks : bluetooth::headset::Callbacks {
char null_str[] = "";
if (!sCallbackEnv.isValidUtf(at_string)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: at_string is not a valid UTF string.", __func__);
at_string = null_str;
}
diff --git a/android/app/jni/com_android_bluetooth_hfpclient.cpp b/android/app/jni/com_android_bluetooth_hfpclient.cpp
index c3e0c9842b5df59644f4c593d026285e628541fc..a36e19c75be3bb59e66aba8d0b75f95c638eac6b 100644
--- a/android/app/jni/com_android_bluetooth_hfpclient.cpp
+++ b/android/app/jni/com_android_bluetooth_hfpclient.cpp
@@ -170,7 +170,6 @@ static void current_operator_cb(const RawAddress* bd_addr, const char* name) {
const char null_str[] = "";
if (!sCallbackEnv.isValidUtf(name)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: name is not a valid UTF string.", __func__);
name = null_str;
}
@@ -246,7 +245,6 @@ static void clip_cb(const RawAddress* bd_addr, const char* number) {
const char null_str[] = "";
if (!sCallbackEnv.isValidUtf(number)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: number is not a valid UTF string.", __func__);
number = null_str;
}
@@ -267,7 +265,6 @@ static void call_waiting_cb(const RawAddress* bd_addr, const char* number) {
const char null_str[] = "";
if (!sCallbackEnv.isValidUtf(number)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: number is not a valid UTF string.", __func__);
number = null_str;
}
@@ -292,7 +289,6 @@ static void current_calls_cb(const RawAddress* bd_addr, int index,
const char null_str[] = "";
if (!sCallbackEnv.isValidUtf(number)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: number is not a valid UTF string.", __func__);
number = null_str;
}
@@ -338,7 +334,6 @@ static void subscriber_info_cb(const RawAddress* bd_addr, const char* name,
const char null_str[] = "";
if (!sCallbackEnv.isValidUtf(name)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: name is not a valid UTF string.", __func__);
name = null_str;
}
@@ -372,7 +367,6 @@ static void last_voice_tag_number_cb(const RawAddress* bd_addr,
const char null_str[] = "";
if (!sCallbackEnv.isValidUtf(number)) {
- android_errorWriteLog(0x534e4554, "109838537");
ALOGE("%s: number is not a valid UTF string.", __func__);
number = null_str;
}
diff --git a/android/app/res/values-af/strings.xml b/android/app/res/values-af/strings.xml
index 6cc8ff1d1fe1637d7fe67401eb39024363520953..91613cabd45dc6fdef9b596109f51f865daf7766 100644
--- a/android/app/res/values-af/strings.xml
+++ b/android/app/res/values-af/strings.xml
@@ -129,9 +129,9 @@
"Lêers groter as 4 GB kan nie oorgedra word nie""Koppel aan Bluetooth""Bluetooth is aan in vliegtuigmodus"
- "As jy Bluetooth aan hou, sal dit aan bly wanneer jy volgende keer in vliegtuigmodus is"
+ "As jy Bluetooth aangeskakel hou, sal jou foon onthou om dit aan te hou wanneer jy weer in vliegtuigmodus is""Bluetooth bly aan"
- "Jou foon onthou om Bluetooth aan te hou in vliegtuigmodus. Skakel Bluetooth af om dit te verander."
+ "Jou foon onthou om Bluetooth aangeskakel te hou in vliegtuigmodus. Skakel Bluetooth af as jy nie wil hê dit moet aan bly nie.""Wi-fi en Bluetooth bly aan"
- "Jou foon onthou om wi‑fi en Bluetooth aan te hou in vliegtuigmodus. Skakel hulle af om dit te verander."
+ "Jou foon onthou om wi‑fi en Bluetooth aan te hou in vliegtuigmodus. Skakel wi-fi en Bluetooth af as jy nie wil het hulle moet aan bly nie."
diff --git a/android/app/res/values-am/strings.xml b/android/app/res/values-am/strings.xml
index 6770381672f4f3e363aec0dd994710669921a58a..e75ee5d074c8bfc1e23f2987ed42d25850e58d51 100644
--- a/android/app/res/values-am/strings.xml
+++ b/android/app/res/values-am/strings.xml
@@ -129,9 +129,9 @@
"ከ4 ጊባ በላይ የሆኑ ፋይሎች ሊዛወሩ አይችሉም""ከብሉቱዝ ጋር ተገናኝ""ብሉቱዝ በአውሮፕላን ሁነታ ላይ በርቷል"
- "ብሉቱዝን አብርተው ካቆዩ ቀጣይ በአውሮፕላን ሁነታ ላይ ሲሆኑ በርቶ ይቆያል"
+ "ብሉቱዝን አብርተው ካቆዩ በቀጣይ ጊዜ በአውሮፕላን ሁነታ ውስጥ ሲሆኑ ስልክዎ እሱን አብርቶ ማቆየቱን ያስታውሳል""ብሉቱዝ በርቶ ይቆያል"
- "ስልክዎ ብሉቱዝን በአውሮፕላን ሁነታ ላይ አብርቶ ማቆየትን ያስታውሳል። ይህን ለመለወጥ ብሉቱዝን ያጥፉ።"
+ "ስልክዎ ብሉቱዝን በአውሮፕላን ሁነታ ውስጥ አብርቶ ማቆየትን ያስታውሳል። በርቶ እንዲቆይ ካልፈለጉ ብሉቱዝን ያጥፉት።""Wi-Fi እና ብሉቱዝ በርተው ይቆያሉ"
- "ስልክዎ Wi-Fiን እና ብሉቱዝን በአውሮፕላን ሁነታ ላይ አብርቶ ማቆየትን ያስታውሳል። ይህን ለመለወጥ ያጥፏቸው።"
+ "ስልክዎ Wi-Fiን እና ብሉቱዝን በአውሮፕላን ሁነታ ውስጥ አብርቶ ማቆየትን ያስታውሳል። በርተው እንዲቆዩ ካልፈለጉ Wi-Fi እና ብሉቱዝን ያጥፏቸው።"
diff --git a/android/app/res/values-ar/strings.xml b/android/app/res/values-ar/strings.xml
index 92ad4d75357697eaf5cba869735daa2ec6c0ac73..f101cfa776259c30e5b353da61af11f259606d77 100644
--- a/android/app/res/values-ar/strings.xml
+++ b/android/app/res/values-ar/strings.xml
@@ -115,7 +115,7 @@
"فتح""محو من القائمة""محو"
- "التعرّف التلقائي على الموسيقى"
+ "قيد التشغيل الآن""حفظ""إلغاء""حدد الحسابات التي تريد مشاركتها عبر البلوتوث. لا يزال يتعين عليك قبول أي دخول إلى الحسابات أثناء الاتصال."
@@ -129,9 +129,9 @@
"يتعذّر نقل الملفات التي يزيد حجمها عن 4 غيغابايت""الاتصال ببلوتوث""تقنية البلوتوث مفعّلة في \"وضع الطيران\""
- "إذا واصلت تفعيل تقنية البلوتوث، ستظل مفعَّلة في المرة القادمة التي تفعِّل فيها \"وضع الطيران\"."
+ "إذا واصلت تفعيل تقنية البلوتوث، سيتذكر هاتفك إبقاءها مفعَّلة في المرة القادمة التي تفعِّل فيها \"وضع الطيران\".""تظل تقنية البلوتوث مفعّلة"
- "يتذكر هاتفك الاحتفاظ بتقنية البلوتوث مفعَّلة في \"وضع الطيران\". لتغيير هذا الخيار، أوقِف البلوتوث."
+ "يتذكر هاتفك الاحتفاظ بتقنية البلوتوث مفعَّلة في \"وضع الطيران\". يمكنك إيقاف تقنية البلوتوث إذا لم تكن تريد مواصلة تفعيلها.""تظل شبكة Wi‑Fi وتقنية البلوتوث مفعَّلتَين."
- "يتذكر هاتفك الاحتفاظ بشبكة Wi‑Fi وتقنية البلوتوث مفعَّلتَين في \"وضع الطيران\". لتغيير هذا الخيار، عليك إيقافهما."
+ "يتذكر هاتفك الاحتفاظ بشبكة Wi‑Fi وتقنية البلوتوث مفعَّلتَين في \"وضع الطيران\". يمكنك إيقاف شبكة Wi‑Fi وتقنية البلوتوث إذا لم تكن تريد مواصلة تفعيلهما."
diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml
index d76fae61cf12c80607e6e1729643e09da829b984..526c3f00ec15ad345bef99b870b0c4a173f11e53 100644
--- a/android/app/res/values-as/strings.xml
+++ b/android/app/res/values-as/strings.xml
@@ -16,8 +16,8 @@
- "ডাউনল’ড মেনেজাৰ ব্যৱহাৰ কৰিব পাৰে।"
- "এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।"
+ "ডাউনল’ড মেনেজাৰ এক্সেছ কৰিব পাৰে।"
+ "এপ্টোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।""ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।""এপ্টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।""ব্লুটুথ"
@@ -86,7 +86,7 @@
"ফাইলটো ছেভ কৰিব পৰাকৈ ইউএছবি ষ্ট’ৰেজত পৰ্যাপ্ত খালী ঠাই নাই।""ফাইলটো ছেভ কৰিব পৰাকৈ এছডি কাৰ্ডখনত পৰ্যাপ্ত খালী ঠাই নাই।""ইমান খালী ঠাইৰ দৰকাৰ: %1$s"
- "বহুত বেছি অনুৰোধৰ ওপৰত প্ৰক্ৰিয়া চলি আছে৷ পিছত আকৌ চেষ্টা কৰক৷"
+ "বহুত বেছি অনুৰোধৰ ওপৰত প্ৰক্ৰিয়া চলি আছে৷ পাছত আকৌ চেষ্টা কৰক৷""ফাইলৰ স্থানান্তৰণ এতিয়ালৈকে আৰম্ভ হোৱা নাই।""ফাইলৰ স্থানান্তৰণ চলি আছে।""ফাইলৰ স্থানান্তৰণৰ কাৰ্য সফলতাৰে সম্পন্ন কৰা হ’ল।"
@@ -118,7 +118,7 @@
"এতিয়া প্লে’ হৈ আছে""ছেভ কৰক""বাতিল কৰক"
- "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ অনুমতি দিবই লাগিব।"
+ "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ এক্সেছ দিবই লাগিব।""বাকী থকা শ্লটবোৰ:""এপ্লিকেশ্বন আইকন""ব্লুটুথৰ জৰিয়তে বাৰ্তা শ্বেয়াৰ কৰাৰ ছেটিং"
@@ -129,9 +129,9 @@
"৪ জি. বি. তকৈ ডাঙৰ ফাইল স্থানান্তৰ কৰিব নোৱাৰি""ব্লুটুথৰ সৈতে সংযোগ কৰক""এয়াৰপ্লেন ম’ডত ব্লুটুথ অন হৈ থাকিব"
- "যদি আপুনি ব্লুটুথ অন কৰি ৰাখে, তেন্তে পৰৱৰ্তী সময়ত আপুনি এয়াৰপ্লেন ম’ডত থকাৰ সময়ত এইটো অন হৈ থাকিব"
+ "আপুনি যদি ব্লুটুথ অন কৰি ৰাখে, পৰৱৰ্তী সময়ত আপুনি এয়াৰপ্লেন ম’ড ব্যৱহাৰ কৰিলে আপোনাৰ ফ’নটোৱে এয়া অন কৰি ৰাখিবলৈ মনত ৰাখিব""ব্লুটুথ অন হৈ থাকে"
- "আপোনাৰ ফ’নটোৱে এয়াৰপ্লেন ম’ডত ব্লুটুথ অন ৰাখিবলৈ মনত ৰাখে। এইটো সলনি কৰিবলৈ, ব্লুটুথ অফ কৰক।"
+ "আপোনাৰ ফ’নটোৱে এয়াৰপ্লেন ম’ডত ব্লুটুথ অন ৰাখিবলৈ মনত ৰাখে। আপুনি যদি ব্লুটুথ অন হৈ থকাটো নিবিচাৰে, তেন্তে ইয়াক অফ কৰক।""ৱাই-ফাই আৰু ব্লুটুথ অন হৈ থাকে"
- "আপোনাৰ ফ’নটোৱে এয়াৰপ্লেন ম’ডত ৱাই-ফাই আৰু ব্লুটুথ অন ৰাখিবলৈ মনত ৰাখে। এইটো সলনি কৰিবলৈ, সেইসমূহ অফ কৰক।"
+ "আপোনাৰ ফ’নটোৱে এয়াৰপ্লেন ম’ডত ৱাই-ফাই আৰু ব্লুটুথ অন ৰাখিবলৈ মনত ৰাখে। আপুনি ৱাই-ফাই আৰু ব্লুটুথ অন হৈ থকাটো নিবিচাৰিলে সেইবোৰ অফ কৰক।"
diff --git a/android/app/res/values-as/strings_sap.xml b/android/app/res/values-as/strings_sap.xml
index f886e47fa17ce60bb1f36d6656f7700b89025392..f18138bb0b1ededdb6a24fe5e54495e8adb0051a 100644
--- a/android/app/res/values-as/strings_sap.xml
+++ b/android/app/res/values-as/strings_sap.xml
@@ -1,8 +1,8 @@
- "ব্লুটুথৰ ছিম ব্যৱহাৰ"
- "ব্লুটুথৰ ছিম ব্যৱহাৰ"
+ "ব্লুটুথৰ ছিম এক্সেছ"
+ "ব্লুটুথৰ ছিম এক্সেছ""সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টক অনুৰোধ কৰিবনে?""সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টৰ অপেক্ষা কৰি থকা হৈছে""বিচ্ছিন্ন কৰক"
diff --git a/android/app/res/values-az/strings.xml b/android/app/res/values-az/strings.xml
index c32d707dc5a9765119261da8064fec0b9aa0faf2..0791d10fa68b18ba42ffce8012b186a677632f01 100644
--- a/android/app/res/values-az/strings.xml
+++ b/android/app/res/values-az/strings.xml
@@ -129,9 +129,9 @@
"4GB-dən böyük olan faylları köçürmək mümkün deyil""Bluetooth\'a qoşulun""Bluetooth təyyarə rejimində aktivdir"
- "Bluetooth\'u aktiv saxlasanız, növbəti dəfə təyyarə rejimində olduqda aktiv qalacaq"
+ "Bluetooth\'u aktiv saxlasanız, növbəti dəfə təyyarə rejimində olduqda telefonunuz onu aktiv saxlayacaq""Bluetooth aktiv qalacaq"
- "Telefonunuz təyyarə rejimində Bluetooth\'u aktiv saxlayacaq. Bunu dəyişmək üçün Bluetooth\'u deaktiv edin."
+ "Telefonunuz təyyarə rejimində Bluetooth\'u aktiv saxlayacaq. Aktiv qalmasını istəmirsinizsə, Bluetooth\'u deaktiv edin.""Wi-Fi və Bluetooth aktiv qalır"
- "Telefonunuz təyyarə rejimində Wi‑Fi və Bluetooth\'u aktiv saxlayacaq. Bunu dəyişmək üçün onları deaktiv edin."
+ "Telefonunuz təyyarə rejimində Wi‑Fi və Bluetooth\'u aktiv saxlayacaq. Aktiv qalmasını istəmirsinizsə, Wi-Fi və Bluetooth\'u deaktiv edin."
diff --git a/android/app/res/values-b+sr+Latn/strings.xml b/android/app/res/values-b+sr+Latn/strings.xml
index f6ecdb3b2617ab21ac695fb8fdc1fd1c405c2996..2590f7d4f2e536e11ce2e3ffc553a1bb3b26d15e 100644
--- a/android/app/res/values-b+sr+Latn/strings.xml
+++ b/android/app/res/values-b+sr+Latn/strings.xml
@@ -129,9 +129,9 @@
"Ne mogu da se prenose datoteke veće od 4 GB""Poveži sa Bluetooth-om""Bluetooth je uključen u režimu rada u avionu"
- "Ako odlučite da ne isključujete Bluetooth, ostaće uključen sledeći put kada budete u režimu rada u avionu"
+ "Ako odlučite da ne isključujete Bluetooth, telefon će zapamtiti da ga ne isključuje sledeći put kada budete u režimu rada u avionu""Bluetooth se ne isključuje"
- "Telefon pamti da ne treba da isključuje Bluetooth u režimu rada u avionu. Da biste to promenili, isključite Bluetooth."
+ "Telefon pamti da ne treba da isključuje Bluetooth u režimu rada u avionu. Isključite Bluetooth ako ne želite da ostane uključen.""WiFi i Bluetooth ostaju uključeni"
- "Telefon pamti da ne treba da isključuje WiFi i Bluetooth u režimu rada u avionu. Da biste to promenili, isključite ih."
+ "Telefon pamti da ne treba da isključuje WiFi i Bluetooth u režimu rada u avionu. Isključite WiFi i Bluetooth ako ne želite da ostanu uključeni."
diff --git a/android/app/res/values-be/strings.xml b/android/app/res/values-be/strings.xml
index ba06cb557615b44b0c48467fdd4ecd42c9466de8..434757afedcb3490ea43caf95bf1191d99a5dbfe 100644
--- a/android/app/res/values-be/strings.xml
+++ b/android/app/res/values-be/strings.xml
@@ -129,9 +129,9 @@
"Немагчыма перадаць файлы, большыя за 4 ГБ""Падключыцца да Bluetooth""У рэжыме палёту Bluetooth уключаны"
- "Калі вы не выключыце Bluetooth, падчас наступнага пераходу ў рэжым палёту ён застанецца ўключаным"
+ "Калі вы не выключыце Bluetooth, падчас наступнага пераходу ў рэжым палёту тэлефон будзе захоўваць яго ўключаным""Bluetooth застаецца ўключаным"
- "На тэлефоне ў рэжыме палёту Bluetooth застанецца ўключаным, калі вы не выключыце яго."
+ "На тэлефоне ў рэжыме палёту Bluetooth застанецца ўключаным, але вы можаце выключыць яго.""Wi-Fi і Bluetooth застаюцца ўключанымі"
- "На тэлефоне ў рэжыме палёту сетка Wi‑Fi і Bluetooth будуць заставацца ўключанымі, калі вы не выключыце іх."
+ "На тэлефоне ў рэжыме палёту сетка Wi‑Fi і Bluetooth будуць заставацца ўключанымі, але вы можаце выключыць іх."
diff --git a/android/app/res/values-bg/strings.xml b/android/app/res/values-bg/strings.xml
index c5e5c8076b3fbe9dab914464531ec3234218bce6..882c273ce8d66062694468b8e70c99876d3abe7a 100644
--- a/android/app/res/values-bg/strings.xml
+++ b/android/app/res/values-bg/strings.xml
@@ -129,9 +129,9 @@
"Файловете с размер над 4 ГБ не могат да бъдат прехвърлени""Свързване с Bluetooth""Функцията за Bluetooth е включена в самолетния режим"
- "Ако не изключите Bluetooth, функцията ще остане включена следващия път, когато използвате самолетния режим"
+ "Ако не изключите функцията за Bluetooth, телефонът ви ще я остави активна следващия път, когато използвате самолетния режим""Функцията за Bluetooth няма да се изключи"
- "Телефонът ви помни, че не трябва да изключва Bluetooth в самолетния режим. За да промените това, изключете функцията."
+ "Функцията за Bluetooth ще бъде включена, докато телефонът ви е в самолетен режим. Ако не искате това, изключете я.""Функциите за Wi-Fi и Bluetooth няма да бъдат изключени"
- "Телефонът ви помни, че не трябва да изключва Wi‑Fi и Bluetooth в самолетния режим. За да промените това, изключете съответните функции."
+ "Функциите за Wi‑Fi и Bluetooth ще бъдат включени, докато телефонът ви е в самолетен режим. Ако не искате това, изключете ги."
diff --git a/android/app/res/values-bn/strings.xml b/android/app/res/values-bn/strings.xml
index b1e9a25da03c20ab4a5a98e7028f88fa30620ae3..7e35d1e02d929723c8f8f5252397f92ff847c039 100644
--- a/android/app/res/values-bn/strings.xml
+++ b/android/app/res/values-bn/strings.xml
@@ -129,9 +129,9 @@
"৪GB থেকে বড় ফটো ট্রান্সফার করা যাবে না""ব্লুটুথের সাথে কানেক্ট করুন""\'বিমান মোড\'-এ থাকাকালীন ব্লুটুথ চালু থাকে"
- "আপনি ব্লুটুথ চালু করে রাখলে, পরের বার \'বিমান মোড\'-এ থাকাকালীন এটি চালু থাকবে"
+ "আপনি ওয়াই-ফাই চালু রাখলে, আপনি এরপর \'বিমান মোডে\' থাকলে আপনার ফোন এটি চালু রাখবে""ব্লুটুথ চালু থাকে"
- "\'বিমান মোড\'-এ থাকাকালীন আপনার ফোন ব্লুটুথ চালু রাখে। এটি পরিবর্তন করতে, ব্লুটুথ বন্ধ করুন।"
+ "\'বিমান মোড\'-এ থাকাকালীন আপনার ফোন ব্লুটুথ চালু রাখে। আপনি ব্লুটুথ চালু না রাখতে চাইলে এটি বন্ধ করুন।""ওয়াই-ফাই ও ব্লুটুথ চালু থাকে"
- "\'বিমান মোড\'-এ থাকাকালীন আপনার ফোন ওয়াই-ফাই ও ব্লুটুথ চালু রাখে। এটি পরিবর্তন করতে, এগুলি বন্ধ করুন।"
+ "\'বিমান মোড\'-এ থাকাকালীন আপনার ফোন ওয়াই-ফাই ও ব্লুটুথ চালু রাখে। আপনি যদি ওয়াই-ফাই এবং ব্লুটুথ চালু রাখতে না চান, সেগুলি বন্ধ করে দিন।"
diff --git a/android/app/res/values-bs/strings.xml b/android/app/res/values-bs/strings.xml
index 982d9ed506049d78d5b1fd9c6db11929db678a45..8c57a628a6b8951d1680c2f2224dcd64bd7a11a6 100644
--- a/android/app/res/values-bs/strings.xml
+++ b/android/app/res/values-bs/strings.xml
@@ -129,9 +129,9 @@
"Nije moguće prenijeti fajlove veće od 4 GB""Poveži se na Bluetooth""Bluetooth je uključen u načinu rada u avionu"
- "Ako Bluetooth ostavite uključenim, ostat će uključen sljedeći put kada budete u načinu rada u avionu"
+ "Ako ostavite Bluetooth uključenim, telefon će zapamtiti da ga ostavi uključenog sljedeći put kada budete u načinu rada u avionu""Bluetooth ostaje uključen"
- "Telefon pamti da Bluetooth treba biti uključen u načinu rada u avionu. Da to primijenite, isključite Bluetooth."
+ "Telefon pamti da Bluetooth treba biti uključen u načinu rada u avionu. Isključite Bluetooth ako ne želite da ostane uključen.""WiFi i Bluetooth ostaju uključeni"
- "Telefon pamti da WiFi i Bluetooth trebaju biti uključeni u načinu rada u avionu. Da to primijenite, isključite ih."
+ "Telefon pamti da WiFi i Bluetooth trebaju biti uključeni u načinu rada u avionu. Isključite WiFi i Bluetooth ako ne želite da ostanu uključeni."
diff --git a/android/app/res/values-ca/strings.xml b/android/app/res/values-ca/strings.xml
index bf63b99d5b161dfe3385c4dda2907995cc77c327..46e1afa41c285824e23035556928a9b91fb7ce8d 100644
--- a/android/app/res/values-ca/strings.xml
+++ b/android/app/res/values-ca/strings.xml
@@ -129,9 +129,9 @@
"No es poden transferir fitxers més grans de 4 GB""Connecta el Bluetooth""El Bluetooth està activat en mode d\'avió"
- "Si tens activat el Bluetooth, es mantindrà així la pròxima vegada que utilitzis el mode d\'avió"
+ "Si tens activat el Bluetooth, el telèfon recordarà mantenir-lo així la pròxima vegada que utilitzis el mode d\'avió""El Bluetooth es mantindrà activat"
- "El telèfon recorda mantenir el Bluetooth activat en mode d\'avió. Per canviar-ho, desactiva\'l."
+ "El telèfon recorda mantenir el Bluetooth activat en mode d\'avió. Desactiva el Bluetooth si no vols que es quedi activat.""La Wi‑Fi i el Bluetooth es mantenen activats"
- "El telèfon recorda mantenir la Wi‑Fi i el Bluetooth activats en mode d\'avió. Per canviar-ho, desactiva\'ls."
+ "El telèfon recorda mantenir la Wi‑Fi i el Bluetooth activats en mode d\'avió. Desactiva la Wi‑Fi i el Bluetooth si no vols que es quedin activats."
diff --git a/android/app/res/values-cs/strings.xml b/android/app/res/values-cs/strings.xml
index b0172121e9cbc2cff0fc8fffe2c0249e49d9acd9..33f3bfc6a256492aee3eb34313561825932304b8 100644
--- a/android/app/res/values-cs/strings.xml
+++ b/android/app/res/values-cs/strings.xml
@@ -129,9 +129,9 @@
"Soubory větší než 4 GB nelze přenést""Připojit k Bluetooth""Zapnutý Bluetooth v režimu Letadlo"
- "Pokud Bluetooth necháte zapnutý, příště v režimu Letadlo zůstane zapnutý"
+ "Pokud Bluetooth necháte zapnutý, telefon si zapamatuje, že ho má příště v režimu Letadlo ponechat zapnutý""Bluetooth zůstane zapnutý"
- "Telefon si pamatuje, že má v režimu Letadlo ponechat zapnutý Bluetooth. Pokud to chcete změnit, vypněte ho."
+ "Telefon si pamatuje, že má v režimu Letadlo ponechat zapnutý Bluetooth. Pokud nechcete, aby Bluetooth zůstal zapnutý, vypněte ho.""Wi-Fi a Bluetooth zůstávají zapnuté"
- "Telefon si pamatuje, že má v režimu Letadlo ponechat zapnutou Wi-Fi a Bluetooth. Pokud to chcete změnit, vypněte je."
+ "Telefon si pamatuje, že má v režimu Letadlo ponechat zapnutou Wi-Fi a Bluetooth. Pokud nechcete, aby Wi-Fi a Bluetooth zůstaly zapnuté, vypněte je."
diff --git a/android/app/res/values-da/strings.xml b/android/app/res/values-da/strings.xml
index 32d2fa1f652881ecbd0c503810c0780229786728..b3ac49d8cef2beacdfacbd6159cd7a5254fb0735 100644
--- a/android/app/res/values-da/strings.xml
+++ b/android/app/res/values-da/strings.xml
@@ -129,9 +129,9 @@
"File, der er større end 4 GB, kan ikke overføres""Opret forbindelse til Bluetooth""Bluetooth er aktiveret i flytilstand"
- "Hvis du beholder Bluetooth aktiveret, forbliver det aktiveret, næste gang enheden er i flytilstand"
+ "Hvis du holder Bluetooth aktiveret, sørger din telefon for, at Bluetooth forbliver aktiveret, næste gang du sætter den til flytilstand""Bluetooth forbliver aktiveret"
- "Din telefon beholder Bluetooth aktiveret i flytilstand. Du kan ændre dette ved at deaktivere Bluetooth."
+ "Din telefon beholder Bluetooth aktiveret i flytilstand. Deaktiver Bluetooth, hvis du ikke vil have, at det forbliver aktiveret.""Wi-Fi og Bluetooth forbliver aktiveret"
- "Din telefon beholder Wi-Fi og Bluetooth aktiveret i flytilstand. Du kan ændre dette ved at slå dem fra."
+ "Din telefon husker at holde Wi-Fi og Bluetooth aktiveret i flytilstand. Deaktiver Wi-Fi og Bluetooth, hvis du ikke vil have, at de forbliver aktiveret."
diff --git a/android/app/res/values-de/strings.xml b/android/app/res/values-de/strings.xml
index 6f2e2dbf26a019009e465cdc8aedb36a8af165b2..82283c36de83657bd8103b74af6f8bee9a41caf4 100644
--- a/android/app/res/values-de/strings.xml
+++ b/android/app/res/values-de/strings.xml
@@ -80,9 +80,9 @@
"Die Datei wird empfangen. Überprüfe den Fortschritt in der Benachrichtigungskonsole.""Die Datei kann nicht empfangen werden.""Der Empfang der Datei von \"%1$s\" wurde angehalten."
- "Datei wird an \"%1$s\" gesendet..."
+ "Datei wird an „%1$s“ gesendet...""%1$s Dateien werden an \"%2$s\" gesendet."
- "Die Übertragung der Datei an \"%1$s\" wurde abgebrochen"
+ "Die Übertragung der Datei an „%1$s“ wurde abgebrochen""Auf dem USB-Speicher ist nicht genügend Platz, um die Datei zu speichern.""Auf der SD-Karte ist nicht genügend Platz, um die Datei zu speichern.""Erforderlicher Speicherplatz: %1$s"
@@ -129,9 +129,9 @@
"Dateien mit mehr als 4 GB können nicht übertragen werden""Mit Bluetooth verbinden""Bluetooth im Flugmodus eingeschaltet"
- "Wenn du Bluetooth nicht deaktivierst, bleibt es eingeschaltet, wenn du das nächste Mal in den Flugmodus wechselst"
+ "Wenn du Bluetooth nicht ausschaltest, bleibt es eingeschaltet, wenn du das nächste Mal in den Flugmodus wechselst""Bluetooth bleibt aktiviert"
- "Dein Smartphone lässt Bluetooth im Flugmodus eingeschaltet. Wenn du das ändern möchtest, deaktiviere Bluetooth."
+ "Auf deinem Smartphone bleibt Bluetooth im Flugmodus eingeschaltet. Schalte Bluetooth aus, wenn du das nicht möchtest.""WLAN und Bluetooth bleiben eingeschaltet"
- "Dein Smartphone lässt das WLAN und Bluetooth im Flugmodus eingeschaltet. Wenn du das ändern möchtest, deaktiviere das WLAN und Bluetooth."
+ "Auf deinem Smartphone bleiben WLAN und Bluetooth im Flugmodus eingeschaltet. Schalte sie aus, wenn du das nicht möchtest."
diff --git a/android/app/res/values-el/strings.xml b/android/app/res/values-el/strings.xml
index f58e150e56b05f06e1ee10d2e70e572e5691c54b..6afca1f394ed0c2fceb053119b95b394e7e59661 100644
--- a/android/app/res/values-el/strings.xml
+++ b/android/app/res/values-el/strings.xml
@@ -129,9 +129,9 @@
"Δεν είναι δυνατή η μεταφορά αρχείων που ξεπερνούν τα 4 GB""Σύνδεση σε Bluetooth""Bluetooth ενεργοποιημένο σε λειτουργία πτήσης"
- "Εάν διατηρήσετε ενεργοποιημένο το Bluetooth, θα παραμείνει ενεργοποιημένο την επόμενη φορά που θα χρησιμοποιήσετε τη λειτουργία πτήσης"
+ "Αν διατηρήσετε το Bluetooth ενεργοποιημένο, το τηλέφωνό σας θα θυμάται να το διατηρήσει ενεργοποιημένο την επόμενη φορά που θα βρεθεί σε λειτουργία πτήσης""Το Bluetooth παραμένει ενεργό"
- "Το τηλέφωνο θυμάται να διατηρεί ενεργοποιημένο το Bluetooth σε λειτουργία πτήσης. Για να το αλλάξετε αυτό, απενεργοποιήστε το Bluetooth."
+ "Το τηλέφωνο θυμάται να διατηρεί ενεργοποιημένο το Bluetooth σε λειτουργία πτήσης. Απενεργοποιήστε το Bluetooth αν δεν θέλετε να παραμένει ενεργοποιημένο.""Το Wi-Fi και το Bluetooth παραμένουν ενεργοποιημένα"
- "Το τηλέφωνο θυμάται να διατηρεί ενεργοποιημένο το Wi‑Fi και το Bluetooth σε λειτουργία πτήσης. Για να το αλλάξετε αυτό, απενεργοποιήστε το Wi‑Fi και το Bluetooth."
+ "Το τηλέφωνο θυμάται να διατηρεί ενεργοποιημένο το Wi‑Fi και το Bluetooth σε λειτουργία πτήσης. Απενεργοποιήστε το Wi-Fi και το Bluetooth αν δεν θέλετε να παραμένουν ενεργοποιημένα."
diff --git a/android/app/res/values-en-rAU/strings.xml b/android/app/res/values-en-rAU/strings.xml
index 4b47a7cf210efc0e4cb58f2a930f25fb12c8269c..724d1e6959912a0e7b7b6e05849589b8a65a581a 100644
--- a/android/app/res/values-en-rAU/strings.xml
+++ b/android/app/res/values-en-rAU/strings.xml
@@ -129,9 +129,9 @@
"Files bigger than 4 GB cannot be transferred""Connect to Bluetooth""Bluetooth on in aeroplane mode"
- "If you keep Bluetooth on, it will stay on the next time that you\'re in aeroplane mode"
+ "If you keep Bluetooth on, your phone will remember to keep it on the next time that you\'re in aeroplane mode""Bluetooth stays on"
- "Your phone remembers to keep Bluetooth on in aeroplane mode. To change this, turn off Bluetooth."
+ "Your phone remembers to keep Bluetooth on in aeroplane mode. Turn off Bluetooth if you don\'t want it to stay on.""Wi-Fi and Bluetooth stay on"
- "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. To change this, turn them off."
+ "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. Turn off Wi-Fi and Bluetooth if you don\'t want them to stay on."
diff --git a/android/app/res/values-en-rCA/strings.xml b/android/app/res/values-en-rCA/strings.xml
index ad70291c964c8c88a1064c7b1e408783183ba26e..c812ceed90e1f9a3e74c7da0c49227fb81de3bae 100644
--- a/android/app/res/values-en-rCA/strings.xml
+++ b/android/app/res/values-en-rCA/strings.xml
@@ -17,8 +17,8 @@
"Access download manager."
- "Allows the application to access the Bluetooth Share manager and to use it to transfer files."
- "Acceptlist Bluetooth device access."
+ "Allows the app to access the BluetoothShare manager and use it to transfer files."
+ "Acceptlist bluetooth device access.""Allows the app to temporarily acceptlist a Bluetooth device, allowing that device to send files to this device without user confirmation.""Bluetooth""Unknown device"
@@ -87,13 +87,13 @@
"There isn\'t enough space on the SD card to save the file.""Space needed: %1$s""Too many requests are being processed. Try again later."
- "File transfer not started yet"
+ "File transfer not started yet.""File transfer is ongoing.""File transfer completed successfully.""Content isn\'t supported.""Transfer forbidden by target device."
- "Transfer cancelled by user."
- "Storage issue"
+ "Transfer canceled by user."
+ "Storage issue.""No USB storage.""No SD card. Insert an SD card to save transferred files.""Connection unsuccessful."
@@ -115,23 +115,23 @@
"Open""Clear from list""Clear"
- "Now playing"
+ "Now Playing""Save""Cancel"
- "Select the accounts that you want to share through Bluetooth. You still have to accept any access to the accounts when connecting."
+ "Select the accounts you want to share through Bluetooth. You still have to accept any access to the accounts when connecting.""Slots left:"
- "Application icon"
- "Bluetooth message sharing settings"
+ "Application Icon"
+ "Bluetooth Message Sharing Settings""Cannot select account. 0 slots left""Bluetooth audio connected""Bluetooth audio disconnected"
- "Bluetooth audio"
- "Files bigger than 4 GB cannot be transferred"
+ "Bluetooth Audio"
+ "Files bigger than 4GB cannot be transferred""Connect to Bluetooth"
- "Bluetooth on in aeroplane mode"
- "If you keep Bluetooth on, it will stay on the next time that you\'re in aeroplane mode"
+ "Bluetooth on in Airplane mode"
+ "If you keep Bluetooth on, your phone will remember to keep it on the next time you\'re in Airplane mode""Bluetooth stays on"
- "Your phone remembers to keep Bluetooth on in aeroplane mode. To change this, turn off Bluetooth."
+ "Your phone remembers to keep Bluetooth on in Airplane mode. Turn off Bluetooth if you don\'t want it to stay on.""Wi-Fi and Bluetooth stay on"
- "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. To change this, turn them off."
+ "Your phone remembers to keep Wi-Fi and Bluetooth on in Airplane mode. Turn off Wi-Fi and Bluetooth if you don\'t want them to stay on."
diff --git a/android/app/res/values-en-rCA/strings_pbap.xml b/android/app/res/values-en-rCA/strings_pbap.xml
index c7b8dc853ba7038f1f0ee413a45f1804befa203b..eb205ce24874d1a54c4efc8c4268f236972d4ae6 100644
--- a/android/app/res/values-en-rCA/strings_pbap.xml
+++ b/android/app/res/values-en-rCA/strings_pbap.xml
@@ -4,11 +4,11 @@
"Type session key for %1$s""Bluetooth session key required""There was time out to accept connection with %1$s"
- "There was a timeout to input session key with %1$s"
+ "There was time out to input session key with %1$s""Obex authentication request"
- "Session key"
+ "Session Key""Type session key for %1$s"
- "Car Kit"
+ "Carkit""Unknown name""My name""000000"
diff --git a/android/app/res/values-en-rCA/strings_sap.xml b/android/app/res/values-en-rCA/strings_sap.xml
index 29288d1eff1a0df87e15c5d701a24e173baa08a7..0656641288869273e67927b0dce66a75e62ceef9 100644
--- a/android/app/res/values-en-rCA/strings_sap.xml
+++ b/android/app/res/values-en-rCA/strings_sap.xml
@@ -2,7 +2,7 @@
"Bluetooth SIM access"
- "Bluetooth SIM access"
+ "Bluetooth SIM Access""Request client to disconnect?""Waiting for client to disconnect""Disconnect"
diff --git a/android/app/res/values-en-rGB/strings.xml b/android/app/res/values-en-rGB/strings.xml
index 4b47a7cf210efc0e4cb58f2a930f25fb12c8269c..724d1e6959912a0e7b7b6e05849589b8a65a581a 100644
--- a/android/app/res/values-en-rGB/strings.xml
+++ b/android/app/res/values-en-rGB/strings.xml
@@ -129,9 +129,9 @@
"Files bigger than 4 GB cannot be transferred""Connect to Bluetooth""Bluetooth on in aeroplane mode"
- "If you keep Bluetooth on, it will stay on the next time that you\'re in aeroplane mode"
+ "If you keep Bluetooth on, your phone will remember to keep it on the next time that you\'re in aeroplane mode""Bluetooth stays on"
- "Your phone remembers to keep Bluetooth on in aeroplane mode. To change this, turn off Bluetooth."
+ "Your phone remembers to keep Bluetooth on in aeroplane mode. Turn off Bluetooth if you don\'t want it to stay on.""Wi-Fi and Bluetooth stay on"
- "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. To change this, turn them off."
+ "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. Turn off Wi-Fi and Bluetooth if you don\'t want them to stay on."
diff --git a/android/app/res/values-en-rIN/strings.xml b/android/app/res/values-en-rIN/strings.xml
index 4b47a7cf210efc0e4cb58f2a930f25fb12c8269c..724d1e6959912a0e7b7b6e05849589b8a65a581a 100644
--- a/android/app/res/values-en-rIN/strings.xml
+++ b/android/app/res/values-en-rIN/strings.xml
@@ -129,9 +129,9 @@
"Files bigger than 4 GB cannot be transferred""Connect to Bluetooth""Bluetooth on in aeroplane mode"
- "If you keep Bluetooth on, it will stay on the next time that you\'re in aeroplane mode"
+ "If you keep Bluetooth on, your phone will remember to keep it on the next time that you\'re in aeroplane mode""Bluetooth stays on"
- "Your phone remembers to keep Bluetooth on in aeroplane mode. To change this, turn off Bluetooth."
+ "Your phone remembers to keep Bluetooth on in aeroplane mode. Turn off Bluetooth if you don\'t want it to stay on.""Wi-Fi and Bluetooth stay on"
- "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. To change this, turn them off."
+ "Your phone remembers to keep Wi-Fi and Bluetooth on in aeroplane mode. Turn off Wi-Fi and Bluetooth if you don\'t want them to stay on."
diff --git a/android/app/res/values-en-rXC/strings.xml b/android/app/res/values-en-rXC/strings.xml
index 8fef4c9b2fdd99c01fbc65833781d68650dd4a98..d925306cbadd7a632a677ae21e6a294a2f079340 100644
--- a/android/app/res/values-en-rXC/strings.xml
+++ b/android/app/res/values-en-rXC/strings.xml
@@ -129,9 +129,9 @@
"Files bigger than 4GB cannot be transferred""Connect to Bluetooth""Bluetooth on in airplane mode"
- "If you keep Bluetooth on, it will stay on the next time you\'re in airplane mode"
+ "If you keep Bluetooth on, your phone will remember to keep it on the next time you\'re in airplane mode""Bluetooth stays on"
- "Your phone remembers to keep Bluetooth on in airplane mode. To change this, turn off Bluetooth."
+ "Your phone remembers to keep Bluetooth on in airplane mode. Turn off Bluetooth if you don\'t want it to stay on.""Wi-Fi and Bluetooth stay on"
- "Your phone remembers to keep Wi-Fi and Bluetooth on in airplane mode. To change this, turn them off."
+ "Your phone remembers to keep Wi-Fi and Bluetooth on in airplane mode. Turn off Wi-Fi and Bluetooth if you don\'t want them to stay on."
diff --git a/android/app/res/values-es-rUS/strings.xml b/android/app/res/values-es-rUS/strings.xml
index 235442c9e42924bf83061a573e3c633d16831984..89d6b57d7c306f44aa59e3aca10b360d5425302e 100644
--- a/android/app/res/values-es-rUS/strings.xml
+++ b/android/app/res/values-es-rUS/strings.xml
@@ -129,9 +129,9 @@
"No se pueden transferir los archivos de más de 4 GB""Conectarse a Bluetooth""Bluetooth activado en modo de avión"
- "Si dejas el Bluetooth activado, seguirá activado la próxima vez que actives el modo de avión."
+ "Si mantienes el Bluetooth activado, el teléfono lo dejará activado la próxima vez que actives el modo de avión""El Bluetooth permanece activado"
- "El teléfono dejará activado el Bluetooth en el modo de avión. Si quieres cambiar esta opción, desactiva el Bluetooth."
+ "El teléfono dejará activado el Bluetooth en el modo de avión. Desactívalo si no quieres que permanezca activado.""El Wi-Fi y el Bluetooth permanecen activados"
- "Tu teléfono dejará activados el Wi-Fi y el Bluetooth en modo de avión. Para cambiarlo, desactívalos."
+ "El teléfono dejará activado el Wi-Fi y el Bluetooth en el modo de avión. Si no quieres que permanezcan activados, desactívalos."
diff --git a/android/app/res/values-es/strings.xml b/android/app/res/values-es/strings.xml
index 6e76688314d408939ed5702a7de4e555c06c4c0f..be8fb70750689b08a0e1c264c9ed06c19cf46c2a 100644
--- a/android/app/res/values-es/strings.xml
+++ b/android/app/res/values-es/strings.xml
@@ -129,9 +129,9 @@
"No se pueden transferir archivos de más de 4 GB""Conectarse a un dispositivo Bluetooth""Bluetooth activado en modo Avión"
- "Si dejas el Bluetooth activado, seguirá estándolo la próxima vez que actives el modo Avión"
+ "Si dejas el Bluetooth activado, tu teléfono se acordará de mantenerlo así la próxima vez que uses el modo Avión""El Bluetooth permanece activado"
- "Tu teléfono dejará activado el Bluetooth en modo Avión. Para cambiarlo, desactiva el Bluetooth."
+ "Tu teléfono se acordará de mantener activado el Bluetooth en modo Avión. Desactiva el Bluetooth si no quieres que permanezca activado.""El Wi-Fi y el Bluetooth permanecen activados"
- "Tu teléfono dejará activados el Wi-Fi y el Bluetooth en modo Avión. Para cambiarlo, desactívalos."
+ "Tu teléfono se acordará de mantener activados el Wi-Fi y el Bluetooth en modo Avión. Desactívalos si no quieres que permanezcan activados."
diff --git a/android/app/res/values-et/strings.xml b/android/app/res/values-et/strings.xml
index bd57882868edc18c6ec0a56a8cd2b97795681dd9..12b4e558da7f9e858a24f7e65e04cdadfde8f2c1 100644
--- a/android/app/res/values-et/strings.xml
+++ b/android/app/res/values-et/strings.xml
@@ -129,9 +129,9 @@
"Faile, mis on üle 4 GB, ei saa üle kanda""Ühenda Bluetoothiga""Bluetooth on lennukirežiimis sisse lülitatud"
- "Kui hoiate Bluetoothi sisselülitatuna, jääb see järgmisel korral lennukirežiimis sisselülitatuks."
+ "Kui hoiate Bluetoothi sisselülitatuna, jätab telefon teie valiku meelde ja kasutab seda järgmisel korral lennukirežiimi aktiveerimisel.""Bluetooth jääb sisselülitatuks"
- "Teie telefon hoiab Bluetoothi lennukirežiimis sisselülitatuna. Selle muutmiseks lülitage Bluetooth välja."
+ "Teie telefon hoiab Bluetoothi lennukirežiimis sisselülitatuna. Lülitage Bluetooth välja, kui te ei soovi, et see oleks sisse lülitatud.""WiFi ja Bluetoothi jäävad sisselülitatuks"
- "Teie telefon hoiab WiFi ja Bluetoothi lennukirežiimis sisselülitatuna. Selle muutmiseks lülitage need välja."
+ "Teie telefon hoiab WiFi ja Bluetoothi lennukirežiimis sisselülitatuna. Lülitage WiFi ja Bluetooth välja, kui te ei soovi, et need oleksid sisse lülitatud."
diff --git a/android/app/res/values-eu/strings.xml b/android/app/res/values-eu/strings.xml
index 6ea17fc0a8ce8c590ec9a7b886044ede91dd4607..872bbacdde78ea9f4f4575e08d41abc958f62f0d 100644
--- a/android/app/res/values-eu/strings.xml
+++ b/android/app/res/values-eu/strings.xml
@@ -20,14 +20,14 @@
"Bluetooth bidezko partekatzeen kudeatzailea atzitzea eta fitxategiak transferitzeko erabiltzeko baimena ematen die aplikazioei.""Ezarri Bluetooth bidezko gailuak onartutakoen zerrendan.""Bluetooth bidezko gailu bat aldi baterako onartutakoen zerrendan ezartzeko baimena ematen die aplikazioei, gailu honetara fitxategiak bidaltzeko baimena izan dezan, baina gailu honen erabiltzaileari berrespena eskatu beharrik gabe."
- "Bluetooth-a"
+ "Bluetootha""Identifikatu ezin den gailua""Ezezaguna""Hegaldi modua"
- "Ezin duzu erabili Bluetooth-a Hegaldi moduan."
+ "Ezin duzu erabili Bluetootha Hegaldi moduan."
- "Bluetooth-zerbitzuak erabiltzeko, Bluetooth-a aktibatu behar duzu."
- "Bluetooth-a aktibatu nahi duzu?"
+ "Bluetooth-zerbitzuak erabiltzeko, Bluetootha aktibatu behar duzu."
+ "Bluetootha aktibatu nahi duzu?""Utzi""Aktibatu""Fitxategi-transferentzia"
@@ -76,7 +76,7 @@
"Ez dago fitxategirik""Ez dago horrelako fitxategirik. \n""Itxaron…"
- "Bluetooth-a aktibatzen…"
+ "Bluetootha aktibatzen…""Fitxategia jasoko da. Egoera kontrolatzeko, joan Jakinarazpenen panelera.""Ezin da fitxategia jaso.""\"%1$s\" igorlearen fitxategia jasotzeari utzi zaio"
@@ -127,11 +127,11 @@
"Deskonektatu da Bluetooth bidezko audioa""Bluetooth bidezko audioa""Ezin dira transferitu 4 GB baino gehiagoko fitxategiak"
- "Konektatu Bluetooth-era"
- "Bluetooth-a aktibatuta mantentzen da hegaldi moduan"
- "Bluetooth-a aktibatuta utziz gero, aktibatuta mantenduko da hegaldi modua erabiltzen duzun hurrengoan"
- "Bluetooth-a aktibatuta mantenduko da"
- "Hegaldi moduan, Bluetooth-a aktibatuta mantentzeaz gogoratzen da telefonoa. Hori aldatzeko, desaktibatu Bluetooth-a."
- "Wifia eta Bluetooth-a aktibatuta mantentzen dira"
- "Hegaldi moduan, wifia eta Bluetooth-a aktibatuta mantentzeaz gogoratzen da telefonoa. Hori aldatzeko, desaktiba itzazu."
+ "Konektatu Bluetoothera"
+ "Bluetootha aktibatuta mantentzen da hegaldi moduan"
+ "Bluetootha aktibatuta utziz gero, hura aktibatuta mantentzeaz gogoratuko da telefonoa hegaldi modua erabiltzen duzun hurrengoan"
+ "Bluetootha aktibatuta mantenduko da"
+ "Hegaldi moduan, Bluetootha aktibatuta mantentzeaz gogoratzen da telefonoa. Halakorik nahi ez baduzu, desaktiba ezazu zuk zeuk."
+ "Wifia eta Bluetootha aktibatuta mantentzen dira"
+ "Hegaldi moduan, wifia eta Bluetootha aktibatuta mantentzeaz gogoratzen da telefonoa. Halakorik nahi ez baduzu, desaktiba itzazu zuk zeuk."
diff --git a/android/app/res/values-eu/test_strings.xml b/android/app/res/values-eu/test_strings.xml
index e6016491941356221b070e29ccc92f7b4df2b76f..4c501ab19e1bb6c4ce2e378cbca368589ab0d067 100644
--- a/android/app/res/values-eu/test_strings.xml
+++ b/android/app/res/values-eu/test_strings.xml
@@ -1,7 +1,7 @@
- "Bluetooth-a"
+ "Bluetootha""Sartu erregistroa""Berretsi erregistroa""ACK erregistroa"
diff --git a/android/app/res/values-fa/strings.xml b/android/app/res/values-fa/strings.xml
index c1b6b301c8e5907f689a380633bbb0e64a31121f..d2507b1647ae9892ce9e43f64714f39181a9d2ba 100644
--- a/android/app/res/values-fa/strings.xml
+++ b/android/app/res/values-fa/strings.xml
@@ -129,9 +129,9 @@
"فایلهای بزرگتر از ۴ گیگابایت نمیتوانند منتقل شوند""اتصال به بلوتوث""بلوتوث در «حالت هواپیما» روشن باشد"
- "اگر بلوتوث را روشن نگه دارید، دفعه بعد که در «حالت هواپیما» باشید روشن میماند"
+ "اگر بلوتوث را روشن نگه دارید، تلفنتان بهیاد خواهد داشت تا دفعه بعدی که در «حالت هواپیما» هستید آن را روشن نگه دارد""بلوتوث روشن بماند"
- "تلفنتان بهیاد میآورد که بلوتوث را در «حالت هواپیما» روشن نگه دارد. برای تغییر این، بلوتوث را خاموش کنید."
+ "تلفنتان بهیاد میآورد که بلوتوث را در «حالت هواپیما» روشن نگه دارد. اگر نمیخواهید بلوتوث روشن بماند، آن را خاموش کنید.""Wi-Fi و بلوتوث روشن بماند"
- "تلفنتان بهیاد میآورد که Wi-Fi و بلوتوث را در «حالت هواپیما» روشن نگه دارد. برای تغییر این، آنها را خاموش کنید."
+ "تلفنتان بهیاد میآورد که Wi-Fi و بلوتوث را در «حالت هواپیما» روشن نگه دارد. اگر نمیخواهید Wi-Fi و بلوتوث روشن بمانند، آنها را خاموش کنید."
diff --git a/android/app/res/values-fi/strings.xml b/android/app/res/values-fi/strings.xml
index 31a0be287ebb2fe4fcbffe505ede79b6789f49fe..bc3550fc50e9688c4a4ecbb0d8cf93c037d4cd24 100644
--- a/android/app/res/values-fi/strings.xml
+++ b/android/app/res/values-fi/strings.xml
@@ -129,9 +129,9 @@
"Yli 4 Gt:n kokoisia tiedostoja ei voi siirtää.""Muodosta Bluetooth-yhteys""Bluetooth päällä lentokonetilassa"
- "Jos pidät Bluetoothin päällä, se pysyy päällä, kun seuraavan kerran olet lentokonetilassa"
+ "Jos pidät Bluetooth-yhteyden päällä, puhelin pitää sen päällä, kun seuraavan kerran olet lentokonetilassa""Bluetooth pysyy päällä"
- "Puhelimen Bluetooth pysyy päällä lentokonetilassa. Muuta tätä laittamalla Bluetooth pois päältä."
+ "Puhelimen Bluetooth pysyy päällä lentokonetilassa. Voit halutessasi laittaa Bluetooth-yhteyden pois päältä.""Wi-Fi ja Bluetooth pysyvät päällä"
- "Puhelimen Wi-Fi-yhteys ja Bluetooth pysyvät päällä lentokonetilassa. Voit muuttaa tätä laittamalla ne pois päältä."
+ "Puhelimen Wi-Fi-yhteys ja Bluetooth pysyvät päällä lentokonetilassa. Voit halutessasi laittaa ne pois päältä."
diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml
index 4d4abd514ac1b5ce9ee4ee4e058147d9592dff34..1977da0f9eae1ee415352e7adcae3ed7ce4de19a 100644
--- a/android/app/res/values-fr-rCA/strings.xml
+++ b/android/app/res/values-fr-rCA/strings.xml
@@ -80,9 +80,9 @@
"La réception du fichier va commencer. La progression va s\'afficher dans le panneau de notification.""Impossible de recevoir le fichier.""Réception du fichier de \"%1$s\" interrompue"
- "Envoi du fichier à \"%1$s\""
+ "Envoi du fichier à « %1$s »""Envoi de %1$s fichiers à \"%2$s\""
- "Envoi du fichier à \"%1$s\" interrompu"
+ "Envoi du fichier à « %1$s » interrompu""Espace insuffisant sur la mémoire de stockage USB pour l\'enregistrement du fichier.""Espace insuffisant sur la carte SD pour l\'enregistrement du fichier.""Espace requis : %1$s"
@@ -129,9 +129,9 @@
"Les fichiers dépassant 4 Go ne peuvent pas être transférés""Connexion au Bluetooth""Bluetooth activé en mode Avion"
- "Si vous laissez le Bluetooth activé, il restera en fonction la prochaine fois que vous serez en mode Avion"
+ "Si vous laissez le Bluetooth activé, votre téléphone se souviendra qu\'il doit le laisser activé la prochaine fois que vous serez en mode Avion""Le Bluetooth reste activé"
- "Votre téléphone se souvient de garder le Bluetooth activé en mode Avion. Pour modifier cela, désactivez le Bluetooth."
- "LE Wi-Fi et le Bluetooth restent activés"
- "Votre téléphone se souvient de garder le Wi-Fi et le Bluetooth activés en mode Avion. Pour modifier cela, désactivez-les."
+ "Votre téléphone se souvient de garder le Bluetooth activé en mode Avion. Désactivez le Bluetooth si vous ne souhaitez pas qu\'il reste activé."
+ "Le Wi-Fi et le Bluetooth restent activés"
+ "Votre téléphone se souvient de garder le Wi-Fi et le Bluetooth activés en mode Avion. Désactivez le Wi-Fi et le Bluetooth si vous ne souhaitez pas qu\'ils restent activés."
diff --git a/android/app/res/values-fr/strings.xml b/android/app/res/values-fr/strings.xml
index 383dcfb47cfaaf1ed092dce279c3d86b1d1a8203..48a84dc0025edc6f36c54ec15353db04f1aa4ef4 100644
--- a/android/app/res/values-fr/strings.xml
+++ b/android/app/res/values-fr/strings.xml
@@ -129,9 +129,9 @@
"Impossible de transférer les fichiers supérieurs à 4 Go""Se connecter au Bluetooth""Bluetooth activé en mode Avion"
- "Si vous laissez le Bluetooth activé, il le restera la prochaine fois que vous serez en mode Avion"
+ "Si vous laissez le Bluetooth activé, votre téléphone s\'en souviendra et le Bluetooth restera activé la prochaine fois que vous serez en mode Avion""Le Bluetooth reste activé"
- "Le Bluetooth de votre téléphone restera activé en mode Avion, mais vous pourrez le désactiver."
+ "Le Bluetooth restera activé en mode Avion. Vous pouvez le désactiver si vous le souhaitez.""Le Wi-Fi et le Bluetooth restent activés"
- "Le Wi‑Fi et le Bluetooth de votre téléphone resteront activés en mode Avion, mais vous pourrez les désactiver."
+ "Le Wi‑Fi et le Bluetooth de votre téléphone resteront activés en mode Avion. Vous pouvez les désactivez si vous le souhaitez."
diff --git a/android/app/res/values-gl/strings.xml b/android/app/res/values-gl/strings.xml
index 0678d5bcb18cc8c3e782ad76125a2cc63c4bfee2..d46049d8e9d1ba8cb8a3c208225dc16d244cefa7 100644
--- a/android/app/res/values-gl/strings.xml
+++ b/android/app/res/values-gl/strings.xml
@@ -129,9 +129,9 @@
"Non se poden transferir ficheiros de máis de 4 GB""Conectar ao Bluetooth""Bluetooth activado no modo avión"
- "Se mantés o Bluetooth activado, permanecerá nese estado a próxima vez que utilices o modo avión"
+ "Se mantés o Bluetooth activado, o teléfono lembrará que ten que deixalo nese estado a próxima vez que esteas no modo avión""O Bluetooth permanece activado"
- "O teu teléfono lembrará manter o Bluetooth activado no modo avión. Para cambiar esta opción, desactiva o Bluetooth."
+ "O teu teléfono lembrará manter o Bluetooth activado no modo avión. Se non queres que permaneza nese estado, desactívao.""A wifi e o Bluetooth permanecen activados"
- "O teu teléfono lembrará manter a wifi e o Bluetooth activados no modo avión. Para cambiar esta opción, desactiva ambas as funcións."
+ "O teu teléfono lembrará manter a wifi e o Bluetooth activados no modo avión. Se non queres que permanezan nese estado, desactívaos."
diff --git a/android/app/res/values-gu/strings.xml b/android/app/res/values-gu/strings.xml
index 2cba3ba4ea6e4604080037f1116e843dce2dec3f..1832689dd363b74fc0fc467cbb5f82637dd4d53b 100644
--- a/android/app/res/values-gu/strings.xml
+++ b/android/app/res/values-gu/strings.xml
@@ -129,9 +129,9 @@
"4GB કરતા મોટી ફાઇલ ટ્રાન્સફર કરી શકાતી નથી""બ્લૂટૂથ સાથે કનેક્ટ કરો""એરપ્લેન મોડમાં બ્લૂટૂથ ચાલુ છે"
- "જો તમે બ્લૂટૂથ ચાલુ રાખો છો, તો તમે જ્યારે આગલી વખતે એરપ્લેન મોડ પર જશો ત્યારે તે ચાલુ રહેશે"
+ "જો તમે બ્લૂટૂથ ચાલુ રાખો, તો તમે જ્યારે આગલી વખતે એરપ્લેન મોડ પર જશો, ત્યારે તમારો ફોન તેને ચાલુ રાખવાનું યાદ રાખશે""બ્લૂટૂથ ચાલુ રહેશે"
- "તમારો ફોન બ્લૂટૂથને એરપ્લેન મોડમાં ચાલુ રાખવાનું યાદ રાખે છે. આમાં ફેરફાર કરવા માટે, બ્લૂટૂથ બંધ કરો."
+ "તમારો ફોન બ્લૂટૂથને એરપ્લેન મોડમાં ચાલુ રાખવાનું યાદ રાખે છે. જો તમે બ્લૂટૂથ ચાલુ રાખવા માગતા ન હો, તો તેને બંધ કરો.""વાઇ-ફાઇ અને બ્લૂટૂથ ચાલુ રહે છે"
- "તમારો ફોન વાઇ-ફાઇ અને બ્લૂટૂથને એરપ્લેન મોડમાં ચાલુ રાખવાનું યાદ રાખે છે. આમાં ફેરફાર કરવા માટે, તેમને બંધ કરો."
+ "તમારો ફોન વાઇ-ફાઇ અને બ્લૂટૂથને એરપ્લેન મોડમાં ચાલુ રાખવાનું યાદ રાખે છે. જો તમે વાઇ-ફાઇ અને બ્લૂટૂથ ચાલુ રાખવા માગતા ન હો, તો તેને બંધ કરો."
diff --git a/android/app/res/values-hi/strings.xml b/android/app/res/values-hi/strings.xml
index 90f8931914a5badfe3ddc23c941f2cf71264a644..df6c33daf311e60bd6c01003f943a8a5eafe54af 100644
--- a/android/app/res/values-hi/strings.xml
+++ b/android/app/res/values-hi/strings.xml
@@ -129,9 +129,9 @@
"4 जीबी से बड़ी फ़ाइलें ट्रांसफ़र नहीं की जा सकतीं""ब्लूटूथ से कनेक्ट करें""हवाई जहाज़ मोड में ब्लूटूथ चालू है"
- "अगर ब्लूटूथ चालू है, तो अगली बार हवाई जहाज़ मोड में भी यह चालू रहेगा"
+ "ब्लूटूथ चालू रखने पर आपका फ़ोन, अगली बार हवाई जहाज़ मोड चालू होने पर भी ब्लूटूथ चालू रखेगा""ब्लूटूथ चालू रहता है"
- "हवाई जहाज़ मोड में भी, आपके फ़ोन पर ब्लूटूथ चालू रहेगा. इस सेटिंग को बदलने के लिए, ब्लूटूथ बंद करें."
+ "हवाई जहाज़ मोड में भी, आपका फ़ोन ब्लूटूथ चालू रखता है. अगर ब्लूटूथ चालू नहीं रखना है, तो उसे बंद कर दें.""वाई-फ़ाई और ब्लूटूथ चालू रहते हैं"
- "हवाई जहाज़ मोड में भी, आपके फ़ोन पर वाई-फ़ाई और ब्लूटूथ चालू रहेगा. इस सेटिंग को बदलने के लिए, इन्हें बंद करें."
+ "हवाई जहाज़ मोड में भी, आपका फ़ोन वाई-फ़ाई और ब्लूटूथ को चालू रखता है. अगर आपको वाई-फ़ाई और ब्लूटूथ चालू नहीं रखना है, तो उन्हें बंद कर दें."
diff --git a/android/app/res/values-hr/strings.xml b/android/app/res/values-hr/strings.xml
index 72658509d2f2f9fb20645d6abcab0d796c239bd6..69df52142e0206f55090e2888b3c05294ebd65eb 100644
--- a/android/app/res/values-hr/strings.xml
+++ b/android/app/res/values-hr/strings.xml
@@ -129,9 +129,9 @@
"Datoteke veće od 4 GB ne mogu se prenijeti""Povezivanje s Bluetoothom""Bluetooth je uključen u načinu rada u zrakoplovu"
- "Ako Bluetooth ostane uključen, ostat će uključen i sljedeći put kad prijeđete u način rada u zrakoplovu"
+ "Ako Bluetooth ostane uključen, telefon će zapamtiti da treba ostati uključen u načinu rada u zrakoplovu""Bluetooth ostaje uključen"
- "Telefon će zapamtiti da Bluetooth treba ostati uključen u načinu rada u zrakoplovu. Da biste to promijenili, isključite Bluetooth."
+ "Telefon će zapamtiti da Bluetooth treba ostati uključen u načinu rada u zrakoplovu. Isključite Bluetooth ako ne želite da ostane uključen.""Wi-Fi i Bluetooth ostat će uključeni"
- "Telefon će zapamtiti da Wi‑Fi i Bluetooth trebaju ostati uključeni u načinu rada u zrakoplovu. Isključite ih da biste to promijenili."
+ "Telefon će zapamtiti da Wi‑Fi i Bluetooth trebaju ostati uključeni u načinu rada u zrakoplovu. Uključite Wi-Fi i Bluetooth ako ne želite da ostanu uključeni."
diff --git a/android/app/res/values-hu/strings.xml b/android/app/res/values-hu/strings.xml
index 774f85bb9a4cd0bdc60b6f57c699bb589d9bc9e6..4b1c45193356d7fcf23e79e53d26bea9eacfa0cf 100644
--- a/android/app/res/values-hu/strings.xml
+++ b/android/app/res/values-hu/strings.xml
@@ -129,9 +129,9 @@
"A 4 GB-nál nagyobb fájlokat nem lehet átvinni""Csatlakozás Bluetooth-eszközhöz""Bluetooth bekapcsolva Repülős üzemmódban"
- "Ha bekapcsolva tartja a Bluetootht, bekapcsolva marad, amikor legközelebb Repülős üzemmódba kapcsol."
+ "Ha bekapcsolva tartja a Bluetootht, a telefon emlékezni fog arra, hogy a következő alkalommal, amikor Repülős üzemmódban van, bekapcsolva tartsa a funkciót.""A Bluetooth bekapcsolva marad"
- "A telefon bekapcsolva tartja a Bluetootht Repülős üzemmódban. Ennek módosításához kapcsolja ki a Bluetootht."
+ "A telefon bekapcsolva tartja a Bluetootht Repülős üzemmódban. Kapcsolja ki a Bluetootht, ha nem szeretné, hogy bekapcsolva maradjon.""A Wi-Fi és a Bluetooth bekapcsolva marad"
- "A telefon bekapcsolva tartja a Wi‑Fi-t és a Bluetootht Repülős üzemmódban. Ennek módosításához kapcsolja ki őket."
+ "A telefon bekapcsolva tartja a Wi‑Fi-t és a Bluetootht Repülős üzemmódban. Ha nem szeretné, hogy bekapcsolva maradjon a Wi-Fi és a Bluetooth, kapcsolja ki őket."
diff --git a/android/app/res/values-hy/strings.xml b/android/app/res/values-hy/strings.xml
index c3bd9279eafa42515f1c53e7bf0de7dc0d47638c..28d1cabcab397ea9798732485f7425af59c01bc7 100644
--- a/android/app/res/values-hy/strings.xml
+++ b/android/app/res/values-hy/strings.xml
@@ -129,9 +129,9 @@
"4 ԳԲ-ից մեծ ֆայլերը հնարավոր չէ փոխանցել""Միանալ Bluetooth-ին""Bluetooth-ը միացված է ավիառեժիմում"
- "Եթե Bluetooth-ը միացված թողնեք, հաջորդ անգամ այն ավտոմատ չի անջատվի ավիառեժիմում"
+ "Եթե Bluetooth-ը միացված թողնեք, հաջորդ անգամ այն ավտոմատ միացված կմնա ավիառեժիմում""Bluetooth-ը կմնա միացված"
- "Ավիառեժիմում Bluetooth-ը միացված կմնա։ Սա փոխելու համար անջատեք Bluetooth-ը։"
+ "Ավիառեժիմում Bluetooth-ը միացված կմնա։ Ցանկության դեպքում կարող եք անջատել Bluetooth-ը։""Wi-Fi-ը և Bluetooth-ը մնում են միացված"
- "Ավիառեժիմում Wi-Fi-ը և Bluetooth-ը միացված կմնան։ Սա փոխելու համար անջատեք այդ գործառույթները։"
+ "Ավիառեժիմում Wi-Fi-ը և Bluetooth-ը միացված կմնան։ Ցանկության դեպքում կարող եք անջատել Wi-Fi-ը և Bluetooth-ը։"
diff --git a/android/app/res/values-in/strings.xml b/android/app/res/values-in/strings.xml
index b47d8b5a2665926524e989b5cc97ce8c69e93cd0..f9a8987ac95893ea088cfccff9471cb2444814a9 100644
--- a/android/app/res/values-in/strings.xml
+++ b/android/app/res/values-in/strings.xml
@@ -129,9 +129,9 @@
"File yang berukuran lebih dari 4GB tidak dapat ditransfer""Hubungkan ke Bluetooth""Bluetooth aktif dalam mode pesawat"
- "Jika tetap diaktifkan, Bluetooth akan tetap aktif saat berikutnya ponsel Anda disetel ke mode pesawat"
+ "Jika Bluetooth tetap diaktifkan, ponsel akan ingat untuk tetap mengaktifkannya saat berikutnya ponsel Anda disetel ke mode pesawat""Bluetooth tetap aktif"
- "Ponsel akan mengingat untuk tetap mengaktifkan Bluetooth dalam mode pesawat. Untuk mengubahnya, nonaktifkan Bluetooth."
+ "Ponsel akan mengingat untuk tetap mengaktifkan Bluetooth dalam mode pesawat. Nonaktifkan jika Anda tidak ingin Bluetooth terus aktif.""Wi-Fi dan Bluetooth tetap aktif"
- "Ponsel akan mengingat untuk tetap mengaktifkan Wi-Fi dan Bluetooth dalam mode pesawat. Untuk mengubahnya, nonaktifkan Wi-Fi dan Bluetooth."
+ "Ponsel akan mengingat untuk tetap mengaktifkan Wi-Fi dan Bluetooth dalam mode pesawat. Nonaktifkan jika Anda tidak ingin Wi-Fi dan Bluetooth terus aktif."
diff --git a/android/app/res/values-is/strings.xml b/android/app/res/values-is/strings.xml
index 2c1781f4ac92dee5ffcc4ed155f2f22126a9feaf..36f077f4a93ef83c6e1521e3890b1ae7cbc5eaaa 100644
--- a/android/app/res/values-is/strings.xml
+++ b/android/app/res/values-is/strings.xml
@@ -129,9 +129,9 @@
"Ekki er hægt að flytja skrár sem eru stærri en 4 GB""Tengjast við Bluetooth""Kveikt á Bluetooth í flugstillingu"
- "Ef þú hefur kveikt á Bluetooth verður það áfram í gangi næst þegar þú setur á flugstillingu."
+ "Ef þú hefur kveikt á Bluetooth mun síminn muna að hafa kveikt á því næst þegar þú stillir á flugstillingu""Áfram kveikt á Bluetooth"
- "Síminn man að hafa kveikt á Bluetooth í flugstillingu. Slökktu á Bluetooth til að breyta þessu."
+ "Síminn man að hafa kveikt á Bluetooth í flugstillingu. Slökktu á Bluetooth ef þú vilt ekki hafa kveikt á því.""Áfram verður kveikt á Wi-Fi og Bluetooth"
- "Síminn man að hafa kveikt á Wi-Fi og Bluetooth í flugstillingu. Til að breyta þessu skaltu slökkva á hvoru tveggja."
+ "Síminn man að hafa kveikt á Wi-Fi og Bluetooth í flugstillingu. Slökktu á Wi-Fi og Bluetooth ef þú vilt ekki hafa kveikt á þessu."
diff --git a/android/app/res/values-it/strings.xml b/android/app/res/values-it/strings.xml
index 91b2b896b0cb62657b857326cf9ad39bb6475984..b9b8a6512139b94dd441b3b82e14cb769de79e8d 100644
--- a/android/app/res/values-it/strings.xml
+++ b/android/app/res/values-it/strings.xml
@@ -129,9 +129,9 @@
"Impossibile trasferire file con dimensioni superiori a 4 GB""Connettiti a Bluetooth""Bluetooth attivo in modalità aereo"
- "Se lo tieni attivo, il Bluetooth rimarrà attivo la prossima volta che sarai in modalità aereo"
+ "Se tieni attivo il Bluetooth, il telefono ricorderà di tenerlo attivo la prossima volta che sarai in modalità aereo""Il Bluetooth rimane attivo"
- "Il telefono memorizza che deve tenere attivo il Bluetooth in modalità aereo. Per cambiare questa opzione, disattiva il Bluetooth."
+ "Il telefono memorizza che deve tenere attivo il Bluetooth in modalità aereo. Disattiva il Bluetooth se non vuoi tenerlo attivo.""Wi-Fi e Bluetooth rimangono attivi"
- "Il telefono memorizza che deve tenere attivi il Wi‑Fi e il Bluetooth in modalità aereo. Per cambiare questa opzione, disattivali."
+ "Il telefono memorizza che deve tenere attivi il Wi‑Fi e il Bluetooth in modalità aereo. Disattiva il Wi-Fi e il Bluetooth se non vuoi tenerli attivi."
diff --git a/android/app/res/values-iw/strings.xml b/android/app/res/values-iw/strings.xml
index 9a9d490f6918cb4df245fdaf6e2889c03eb03ed3..51def833d8a26f33b092b746b126d28af00ef966 100644
--- a/android/app/res/values-iw/strings.xml
+++ b/android/app/res/values-iw/strings.xml
@@ -109,8 +109,8 @@
"כל הפריטים ינוקו מהרשימה.""שיתוף Bluetooth: נשלחו קבצים""שיתוף Bluetooth: התקבלו קבצים"
- "{count,plural, =1{# נכשל}two{# נכשלו}many{# נכשלו}other{# נכשלו}}"
- "{count,plural, =1{# הצליח, %1$s}two{# הצליחו, %1$s}many{# הצליחו, %1$s}other{# הצליחו, %1$s}}"
+ "{count,plural, =1{# נכשל}one{# נכשלו}two{# נכשלו}other{# נכשלו}}"
+ "{count,plural, =1{# הצליח, %1$s}one{# הצליחו, %1$s}two{# הצליחו, %1$s}other{# הצליחו, %1$s}}""ניקוי רשימה""פתיחה""ניקוי מהרשימה"
@@ -129,9 +129,9 @@
"לא ניתן להעביר קבצים שגדולים מ-4GB""התחברות באמצעות Bluetooth""חיבור ה-Bluetooth מופעל במצב טיסה"
- "אם חיבור ה-Bluetooth נשאר מופעל, הוא יישאר מופעל בפעם הבאה שהטלפון יועבר למצב טיסה"
+ "אם חיבור ה-Bluetooth נשאר מופעל, הטלפון יזכור להשאיר אותו מופעל בפעם הבאה שהוא יועבר למצב טיסה""Bluetooth יישאר מופעל"
- "חיבור ה-Bluetooth בטלפון יישאר מופעל במצב טיסה. כדי לשנות זאת יש להשבית את ה-Bluetooth."
+ "חיבור ה-Bluetooth בטלפון יישאר מופעל במצב טיסה. אפשר להשבית את ה-Bluetooth אם לא רוצים שהוא יפעל.""חיבורי ה-Wi‑Fi וה-Bluetooth יישארו מופעלים"
- "חיבורי ה-Wi‑Fi וה-Bluetooth בטלפון יישארו מופעלים במצב טיסה. כדי לשנות זאת יש להשבית אותם."
+ "חיבורי ה-Wi‑Fi וה-Bluetooth בטלפון יישארו מופעלים במצב טיסה. אפשר להשבית את ה-Wi-Fi וה-Bluetooth אם לא רוצים שהם יפעלו."
diff --git a/android/app/res/values-ja/strings.xml b/android/app/res/values-ja/strings.xml
index 5eb4fbbcc5f42c9873a8ba6ef12867bf458e11c2..fc4fe4ae2bbf3ff77ba28aea43df5f6659534900 100644
--- a/android/app/res/values-ja/strings.xml
+++ b/android/app/res/values-ja/strings.xml
@@ -129,9 +129,9 @@
"4 GB を超えるファイルは転送できません""Bluetooth に接続する""機内モードで Bluetooth を ON にする"
- "Bluetooth を ON にしておくと、次に機内モードになったときも ON のままになります"
+ "Bluetooth を ON にしておくと、次に機内モードになったときも ON のままになります""Bluetooth を ON にしておく"
- "機内モードでも、スマートフォンの Bluetooth は ON のままになります。変更するには Bluetooth を OFF にしてください。"
+ "機内モードでも、スマートフォンの Bluetooth は ON のままになります。Bluetooth を ON にしたくない場合は OFF にしてください。""Wi-Fi と Bluetooth を ON のままにする"
- "機内モードでも、スマートフォンの Wi-Fi と Bluetooth は ON のままになります。変更するには、両方を OFF にしてください。"
+ "機内モードでも、スマートフォンの Wi-Fi と Bluetooth は ON のままになります。Wi-Fi と Bluetooth を ON にしたくない場合は OFF にしてください。"
diff --git a/android/app/res/values-ka/strings.xml b/android/app/res/values-ka/strings.xml
index 62b4e6cb799597428acd967f1f25f0299571bddc..42eb4e494e75b81adc7d8fb066b480a213e4036d 100644
--- a/android/app/res/values-ka/strings.xml
+++ b/android/app/res/values-ka/strings.xml
@@ -129,9 +129,9 @@
"4 გბაიტზე დიდი მოცულობის ფაილების გადატანა ვერ მოხერხდება""Bluetooth-თან დაკავშირება""Bluetooth ჩართულია თვითმფრინავის რეჟიმში"
- "თუ Bluetooth ჩართული გექნებათ, ის ჩართული დარჩება, როდესაც შემდეგ თვითმფრინავის რეჟიმში იქნებით"
+ "თუ Bluetooth-ს ჩართულს დატოვებთ, თქვენი ტელეფონი დაიმახსოვრებს და ჩართულს დატოვებს მას, როდესაც შემდეგ ჯერზე თვითმფრინავის რეჟიმში იქნებით""Bluetooth რᲩება Ჩართული"
- "თქვენს ტელეფონს ემახსოვრება, რომ Bluetooth ჩართული უნდა იყოს თვითმფრინავის რეჟიმში. გამორთეთ Bluetooth ამის შესაცვლელად."
+ "თქვენს ტელეფონს ემახსოვრება, რომ Bluetooth ჩართული უნდა იყოს თვითმფრინავის რეჟიმში. გამორთეთ Bluetooth, თუ არ გსურთ, რომ ის ჩართული იყოს.""Wi-Fi და Bluetooth ჩართული დარჩება"
- "თქვენს ტელეფონს ემახსოვრება, რომ Wi‑Fi და Bluetooth ჩართული უნდა იყოს თვითმფრინავის რეჟიმში. გამორთეთ ისინი ამის შესაცვლელად."
+ "თქვენს ტელეფონს ემახსოვრება, რომ Wi‑Fi და Bluetooth ჩართული უნდა იყოს თვითმფრინავის რეჟიმში. გამორთეთ Wi-Fi და Bluetooth, თუ არ გსურთ, რომ ისინი ჩართული იყოს."
diff --git a/android/app/res/values-kk/strings.xml b/android/app/res/values-kk/strings.xml
index 2857d660227aa8ac64876d2599dc7adf1c480aa0..d807e04e7e010e006684c7d0fd0f8f69e2c38dc4 100644
--- a/android/app/res/values-kk/strings.xml
+++ b/android/app/res/values-kk/strings.xml
@@ -90,7 +90,7 @@
"Файлды аудару әлі басталған жоқ.""Файлды аудару орындалуда.""Файлды аудару сәтті орындалды."
- "Мазмұн қолдауы жоқ."
+ "Контент қолдауы жоқ.""Аударуға қабылдайтын құрылғы тыйым салды.""Тасымалды пайдаланушы тоқтатты.""Жад ақаулығы."
@@ -129,9 +129,9 @@
"Көлемі 4 ГБ-тан асатын файлдар тасымалданбайды""Bluetooth-қа қосылу""Bluetooth ұшақ режимінде қосулы"
- "Bluetooth қосулы тұра беретін болса, ол келесіде ұшақ режиміне ауысқаныңызда да қосулы тұра беретін болады."
+ "Bluetooth-ты қосулы қалдырсаңыз, келесі жолы ұшақ режиміне ауысқанда да ол қосылып тұрады.""Bluetooth қосулы болады"
- "Телефондағы Bluetooth ұшақ режимінде қосулы болады. Өзгерткіңіз келсе, Bluetooth функциясын өшіріп қойыңыз."
+ "Bluetooth ұшақ режимінде қосылып тұрады. Қаласаңыз, оны өшіріп қоюыңызға болады.""Wi-Fi мен Bluetooth қосулы тұрады"
- "Телефондағы Wi‑Fi мен Bluetooth ұшақ режимінде қосулы болады. Өзгерткіңіз келсе, оларды өшіріп қойыңыз."
+ "Wi‑Fi мен Bluetooth ұшақ режимінде қосылып тұрады. Қаласаңыз, оларды өшіріп қоюыңызға болады."
diff --git a/android/app/res/values-km/strings.xml b/android/app/res/values-km/strings.xml
index 69ba92ac31d16cb1fa461a05e2d42f7bad86d94b..822765be43b1bf0b9771db87a27e140ccacbbf8e 100644
--- a/android/app/res/values-km/strings.xml
+++ b/android/app/res/values-km/strings.xml
@@ -129,9 +129,9 @@
"ឯកសារដែលមានទំហំធំជាង 4 GB មិនអាចផ្ទេរបានទេ""ភ្ជាប់ប៊្លូធូស""បើកប៊្លូធូសនៅក្នុងមុខងារពេលជិះយន្តហោះ"
- "ប្រសិនបើអ្នកបើកប៊្លូធូសចោល នោះវានឹងបន្តបើកនៅពេលបន្ទាប់ ដែលអ្នកកំពុងស្ថិតនៅក្នុងមុខងារពេលជិះយន្តហោះ"
+ "ប្រសិនបើអ្នកបើកប៊្លូធូស នោះទូរសព្ទរបស់អ្នកនឹងចាំថាត្រូវបើកវា នៅលើកក្រោយដែលអ្នកស្ថិតក្នុងមុខងារពេលជិះយន្តហោះ""ប៊្លូធូសបន្តបើក"
- "ទូរសព្ទរបស់អ្នកចាំថាត្រូវបើកប៊្លូធូសនៅក្នុងមុខងារពេលជិះយន្តហោះ។ ដើម្បីប្ដូរវា សូមបិទប៊្លូធូស។"
+ "ទូរសព្ទរបស់អ្នកចាំថាត្រូវបើកប៊្លូធូសនៅក្នុងមុខងារពេលជិះយន្តហោះ។ បិទប៊្លូធូស ប្រសិនបើអ្នកមិនចង់បើកទេ។""Wi-Fi និងប៊្លូធូសបន្តបើក"
- "ទូរសព្ទរបស់អ្នកចាំថាត្រូវបើក Wi-Fi និងប៊្លូធូសនៅក្នុងមុខងារពេលជិះយន្តហោះ។ ដើម្បីប្ដូរវា សូមបិទ Wi-Fi និងប៊្លូធូស។"
+ "ទូរសព្ទរបស់អ្នកចាំថាត្រូវបើក Wi-Fi និងប៊្លូធូសនៅក្នុងមុខងារពេលជិះយន្តហោះ។ បិទ Wi-Fi និងប៊្លូធូស ប្រសិនបើអ្នកមិនចង់បើកទេ។"
diff --git a/android/app/res/values-kn/strings.xml b/android/app/res/values-kn/strings.xml
index d57a3c395d8bb78f3b6e199e865575c565b55150..e9d376dcd6b6bc917c06f2fe11963e81fffb50b4 100644
--- a/android/app/res/values-kn/strings.xml
+++ b/android/app/res/values-kn/strings.xml
@@ -129,9 +129,9 @@
"4GB ಗಿಂತ ದೊಡ್ಡದಾದ ಫೈಲ್ಗಳನ್ನು ವರ್ಗಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ""ಬ್ಲೂಟೂತ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ""ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿ ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿದೆ"
- "ನೀವು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಆನ್ ಮಾಡಿದರೆ, ಮುಂದಿನ ಬಾರಿ ನೀವು ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿರುವಾಗ ಅದು ಆನ್ ಆಗಿರುತ್ತದೆ"
+ "ನೀವು ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರಿಸಿದರೆ, ಮುಂದಿನ ಬಾರಿ ನೀವು ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿರುವಾಗ ಅದನ್ನು ಆನ್ ಆಗಿರಿಸಿಕೊಳ್ಳುವುದನ್ನು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿಸಿಕೊಳ್ಳುತ್ತದೆ""ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರುತ್ತದೆ"
- "ಬ್ಲೂಟೂತ್ ಅನ್ನು ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿ ಇರಿಸಿಕೊಳ್ಳಲು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿನಲ್ಲಿರಿಸಿಕೊಳ್ಳುತ್ತದೆ. ಇದನ್ನು ಬದಲಾಯಿಸಲು, ಬ್ಲೂಟೂತ್ ಅನ್ನು ಆಫ್ ಮಾಡಿ."
+ "ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿ ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರಿಸಿಕೊಳ್ಳುವುದನ್ನು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿಸಿಕೊಳ್ಳುತ್ತದೆ. ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರಿಸಲು ನೀವು ಬಯಸದಿದ್ದರೆ ಅದನ್ನು ಆಫ್ ಮಾಡಿ.""ವೈ-ಫೈ ಮತ್ತು ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರುತ್ತದೆ"
- "ವೈ-ಫೈ ಮತ್ತು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿ ಇರಿಸಿಕೊಳ್ಳಲು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿಸಿಕೊಳ್ಳುತ್ತದೆ. ಇದನ್ನು ಬದಲಾಯಿಸಲು, ಅವುಗಳನ್ನು ಆಫ್ ಮಾಡಿ."
+ "ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿ ವೈಫೈ ಮತ್ತು ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರಿಸಿಕೊಳ್ಳುವುದನ್ನು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿಸಿಕೊಳ್ಳುತ್ತದೆ. ವೈಫೈ ಮತ್ತು ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರಿಸಲು ನೀವು ಬಯಸದಿದ್ದರೆ ಅವುಗಳನ್ನು ಆಫ್ ಮಾಡಿ."
diff --git a/android/app/res/values-ko/strings.xml b/android/app/res/values-ko/strings.xml
index cb994e5884362902426808c0dc9eb525b03fc95f..4a9c63fb9949b4984cdc04ccbd4e80fa32545e0c 100644
--- a/android/app/res/values-ko/strings.xml
+++ b/android/app/res/values-ko/strings.xml
@@ -129,9 +129,9 @@
"4GB보다 큰 파일은 전송할 수 없습니다""블루투스에 연결""비행기 모드에서 블루투스 사용 설정"
- "블루투스를 켜진 상태로 유지하면 다음에 비행기 모드를 사용할 때도 블루투스 연결이 유지됩니다."
+ "블루투스를 켜진 상태로 유지하면 다음에 비행기 모드를 사용할 때도 블루투스 연결이 유지됩니다.""블루투스가 켜진 상태로 유지됨"
- "휴대전화가 비행기 모드에서 블루투스를 켜진 상태로 유지합니다. 변경하려면 블루투스를 사용 중지하세요."
+ "휴대전화가 비행기 모드에서 블루투스를 켜진 상태로 유지합니다. 유지하지 않으려면 블루투스를 사용 중지하세요.""Wi-Fi 및 블루투스 계속 사용"
- "휴대전화가 비행기 모드에서 Wi-Fi 및 블루투스를 켜진 상태로 유지합니다. 변경하려면 Wi-Fi 및 블루투스를 사용 중지하세요."
+ "휴대전화가 비행기 모드에서 Wi-Fi 및 블루투스를 켜진 상태로 유지합니다. 유지하지 않으려면 Wi-Fi와 블루투스를 사용 중지하세요."
diff --git a/android/app/res/values-ky/strings.xml b/android/app/res/values-ky/strings.xml
index 1b52bb25e43b42ddd78e4a1d1a7e029f5d53df37..954556f3a4aa4ee8141128f3a53c63ada6e4bfe7 100644
--- a/android/app/res/values-ky/strings.xml
+++ b/android/app/res/values-ky/strings.xml
@@ -129,9 +129,9 @@
"4Гб чоң файлдарды өткөрүү мүмкүн эмес""Bluetooth\'га туташуу""Учак режиминде Bluetooth күйүк"
- "Эгер Bluetooth күйүк бойдон калса, кийинки жолу учак режимине өткөнүңүздө автоматтык түрдө өчпөйт"
+ "Эгер Bluetooth күйүк бойдон калса, кийинки жолу учак режимине өткөнүңүздө телефонуңуз аны эстеп калат""Bluetooth күйүк бойдон калат"
- "Телефонуңуз учак режиминде Bluetooth\'га туташкан бойдон калат. Муну өзгөртүү үчүн Bluetooth\'ду өчүрүңүз."
+ "Телефонуңуз учак режиминде Bluetooth\'га туташкан бойдон калат. Кааласаңыз, Bluetooth\'ду өчүрүп койсоңуз болот.""Wi-Fi менен Bluetooth күйүк бойдон калат"
- "Учак режиминде Wi‑Fi менен Bluetooth күйүп тура берет. Бул функцияларды каалаган учурда өчүрө аласыз."
+ "Телефонуңуз учак режиминде Wi‑Fi\'га жана Bluetooth\'га туташкан бойдон калат. Кааласаңыз, Wi-Fi менен Bluetooth\'ду өчүрүп койсоңуз болот."
diff --git a/android/app/res/values-lo/strings.xml b/android/app/res/values-lo/strings.xml
index 125e400c9223427f1b40b12cf9f65dc7ad9356ae..0dcbeb77e7df9d2b8b0a5e406dbd3f05db703cfa 100644
--- a/android/app/res/values-lo/strings.xml
+++ b/android/app/res/values-lo/strings.xml
@@ -129,9 +129,9 @@
"ບໍ່ສາມາດໂອນຍ້າຍໄຟລ໌ທີ່ໃຫຍກວ່າ 4GB ໄດ້""ເຊື່ອມຕໍ່ກັບ Bluetooth""ເປີດ Bluetooth ໃນໂໝດຢູ່ໃນຍົນ"
- "ຫາກທ່ານສືບຕໍ່ເປີດ Bluetooth ໄວ້, ມັນຈະເປີດປະໄວ້ໃນເທື່ອຕໍ່ໄປທີ່ທ່ານຢູ່ໃນໂໝດຢູ່ໃນຍົນ"
+ "ຫາກທ່ານເປີດ Bluetooth ປະໄວ້, ໂທລະສັບຂອງທ່ານຈະຈື່ວ່າຕ້ອງເປີດ Wi‑Fi ໃນເທື່ອຕໍ່ໄປທີ່ທ່ານຢູ່ໃນໂໝດຢູ່ໃນຍົນ""Bluetooth ເປີດຢູ່"
- "ໂທລະສັບຂອງທ່ານຈື່ວ່າຈະຕ້ອງເປີດ Bluetooth ໄວ້ໃນໂໝດຢູ່ໃນຍົນ. ເພື່ອປ່ຽນການຕັ້ງຄ່ານີ້, ໃຫ້ປິດ Bluetooth ໄວ້."
+ "ໂທລະສັບຂອງທ່ານຈື່ວ່າຈະຕ້ອງເປີດ Bluetooth ປະໄວ້ໃນໂໝດຢູ່ໃນຍົນ. ປິດ Bluetooth ຫາກທ່ານບໍ່ຕ້ອງການໃຫ້ເປີດປະໄວ້.""Wi-Fi ແລະ Bluetooth ຈະເປີດປະໄວ້"
- "ໂທລະສັບຂອງທ່ານຈື່ວ່າຈະຕ້ອງເປີດ Wi-Fi ແລະ Bluetooth ໄວ້ໃນໂໝດຢູ່ໃນຍົນ. ເພື່ອປ່ຽນການຕັ້ງຄ່ານີ້, ໃຫ້ປິດພວກມັນໄວ້."
+ "ໂທລະສັບຂອງທ່ານຈື່ວ່າຈະຕ້ອງເປີດ Wi-Fi ແລະ Bluetooth ປະໄວ້ໃນໂໝດຢູ່ໃນຍົນ. ປິດ Wi-Fi ແລະ Bluetooth ຫາກທ່ານບໍ່ຕ້ອງການໃຫ້ເປີດປະໄວ້."
diff --git a/android/app/res/values-lt/strings.xml b/android/app/res/values-lt/strings.xml
index a195e6abc164df23e8101b6941c9366b58d12116..f7972c0d559146c741d392697e3e425e5c68059d 100644
--- a/android/app/res/values-lt/strings.xml
+++ b/android/app/res/values-lt/strings.xml
@@ -129,9 +129,9 @@
"Negalima perkelti didesnių nei 4 GB failų""Prisijungti prie „Bluetooth“""„Bluetooth“ ryšys įjungtas lėktuvo režimu"
- "Jei paliksite „Bluetooth“ ryšį įjungtą, jis liks įjungtas, kai kitą kartą naudosite lėktuvo režimą"
+ "Jei paliksite „Bluetooth“ ryšį įjungtą, telefonas, prisimins palikti jį įjungtą, kai kitą kartą įjungsite lėktuvo režimą""„Bluetooth“ liks įjungtas"
- "Telefonas prisimena, kad naudojant lėktuvo režimą reikia palikti įjungtą „Bluetooth“ ryšį. Norėdami pakeisti šią nuostatą, išjunkite „Bluetooth“ ryšį."
+ "Telefonas prisimena, kad naudojant lėktuvo režimą reikia palikti įjungtą „Bluetooth“ ryšį. Išjunkite „Bluetooth“, jei nenorite, kad jis liktų įjungtas.""„Wi‑Fi“ ir „Bluetooth“ ryšys lieka įjungtas"
- "Telefonas prisimena, kad lėktuvo režimu reikia palikti įjungtą „Wi‑Fi“ ir „Bluetooth“ ryšį. Norėdami pakeisti šią nuostatą, išjunkite ryšį."
+ "Telefonas prisimena, kad lėktuvo režimu reikia palikti įjungtą „Wi‑Fi“ ir „Bluetooth“ ryšį. Išjunkite „Wi-Fi“ ir „Bluetooth“, jei nenorite, kad jie liktų įjungti."
diff --git a/android/app/res/values-lv/strings.xml b/android/app/res/values-lv/strings.xml
index 359dda2fbd4ff834604ef549623a0d8287ba6037..9f8282336a42df5fc2944f965ffaf0691be2df6f 100644
--- a/android/app/res/values-lv/strings.xml
+++ b/android/app/res/values-lv/strings.xml
@@ -129,9 +129,9 @@
"Nevar pārsūtīt failus, kas lielāki par 4 GB.""Izveidot savienojumu ar Bluetooth""Tehnoloģija Bluetooth lidojuma režīmā paliek ieslēgta"
- "Ja atstāsiet tehnoloģiju Bluetooth ieslēgtu, tā netiks izslēgta arī nākamreiz, kad ieslēgsiet lidojuma režīmu"
+ "Ja Bluetooth savienojums paliks ieslēgts, tālrunī tas paliks ieslēgts arī nākamreiz, kad ieslēgsiet lidojuma režīmu.""Bluetooth savienojums joprojām ir ieslēgts"
- "Lidojuma režīmā tālrunī joprojām būs ieslēgta tehnoloģija Bluetooth. Lai mainītu šo iestatījumu, izslēdziet Bluetooth."
+ "Lidojuma režīmā tālrunī joprojām būs ieslēgts Bluetooth savienojums. Izslēdziet Bluetooth savienojumu, ja nevēlaties, lai tas paliktu ieslēgts.""Wi-Fi savienojums un tehnoloģija Bluetooth paliek ieslēgta"
- "Lidojuma režīmā tālrunī joprojām būs ieslēgts Wi-Fi savienojums un tehnoloģija Bluetooth. Lai mainītu šo iestatījumu, izslēdziet tos."
+ "Lidojuma režīmā tālrunī joprojām būs ieslēgti Wi-Fi un Bluetooth savienojumi. Izslēdziet Wi-Fi un Bluetooth savienojumus, ja nevēlaties, lai tie paliktu ieslēgti."
diff --git a/android/app/res/values-mk/strings.xml b/android/app/res/values-mk/strings.xml
index 0e3eca68bc933ef72962aee6e9cf7132a56c953d..b580d37787d3c755c882d58fea75a7b6fe50fffd 100644
--- a/android/app/res/values-mk/strings.xml
+++ b/android/app/res/values-mk/strings.xml
@@ -129,9 +129,9 @@
"Не може да се пренесуваат датотеки поголеми од 4 GB""Поврзи се со Bluetooth""Вклучен Bluetooth во авионски режим"
- "Ако го оставите Bluetooth вклучен, ќе остане вклучен до следниот пат кога ќе бидете во авионски режим"
+ "Ако го оставите Bluetooth вклучен, телефонот ќе запомни да го остави вклучен до следниот пат кога ќе бидете во авионски режим""Bluetooth останува вклучен"
- "Телефонот помни да го задржи Bluetooth вклучен во авионски режим. За да го промените ова, исклучете го Bluetooth."
+ "Телефонот помни да го задржи Bluetooth вклучен во авионски режим. Исклучете го Bluetooth ако не сакате да остане вклучен.""Wi-Fi и Bluetooth остануваат вклучени"
- "Телефонот помни да ги задржи Wi‑Fi и Bluetooth вклучени во авионски режим. За да го промените ова, исклучете ги."
+ "Телефонот помни да ги задржи Wi‑Fi и Bluetooth вклучени во авионски режим. Исклучете ги Wi-Fi и Bluetooth ако не сакате да бидат вклучени."
diff --git a/android/app/res/values-ml/strings.xml b/android/app/res/values-ml/strings.xml
index 4fbf4b28095c3733c0777e6b8b22ec29deb2d7e1..e511d2c25935efd3617376fbf3dffefeab927853 100644
--- a/android/app/res/values-ml/strings.xml
+++ b/android/app/res/values-ml/strings.xml
@@ -129,9 +129,9 @@
"4GB-യിൽ കൂടുതലുള്ള ഫയലുകൾ കൈമാറാനാവില്ല""Bluetooth-ലേക്ക് കണക്റ്റ് ചെയ്യുക""ഫ്ലൈറ്റ് മോഡിൽ Bluetooth ഓണാണ്"
- "നിങ്ങൾ Bluetooth ഓണാക്കി വയ്ക്കുകയാണെങ്കിൽ, അടുത്ത തവണ ഫൈറ്റ് മോഡിൽ ആകുമ്പോൾ അത് ഓണായ നിലയിൽ തന്നെ തുടരും"
+ "Bluetooth ഓണാക്കി വച്ചാൽ, അടുത്ത തവണ നിങ്ങൾ ഫ്ലൈറ്റ് മോഡിൽ ആയിരിക്കുമ്പോൾ നിങ്ങളുടെ ഫോൺ അത് ഓണാക്കി വയ്ക്കാൻ ഓർക്കും""Bluetooth ഓണാക്കിയ നിലയിൽ തുടരും"
- "ഫ്ലൈറ്റ് മോഡിലായിരിക്കുമ്പോൾ Bluetooth ഓണാക്കി വയ്ക്കാൻ നിങ്ങളുടെ ഫോൺ ഓർമ്മിക്കുന്നു. ഇത് മാറ്റാൻ, Bluetooth ഓഫാക്കുക."
+ "ഫ്ലൈറ്റ് മോഡിലായിരിക്കുമ്പോൾ Bluetooth ഓണാക്കി വയ്ക്കാൻ നിങ്ങളുടെ ഫോൺ ഓർമ്മിക്കുന്നു. Bluetooth ഓണാക്കി വയ്ക്കാൻ താൽപ്പര്യമില്ലെങ്കിൽ അത് ഓഫാക്കുക.""വൈഫൈ, Bluetooth എന്നിവ ഓണായ നിലയിൽ തുടരും"
- "ഫ്ലൈറ്റ് മോഡിലായിരിക്കുമ്പോൾ വൈഫൈ, Bluetooth എന്നിവ ഓണാക്കി വയ്ക്കാൻ നിങ്ങളുടെ ഫോൺ ഓർമ്മിക്കുന്നു. ഇത് മാറ്റാൻ, അവ ഓഫാക്കുക."
+ "ഫ്ലൈറ്റ് മോഡിലായിരിക്കുമ്പോൾ വൈഫൈ, Bluetooth എന്നിവ ഓണാക്കി വയ്ക്കാൻ നിങ്ങളുടെ ഫോൺ ഓർമ്മിക്കുന്നു. വൈഫൈ, Bluetooth എന്നിവ ഓണാക്കി വയ്ക്കാൻ താൽപ്പര്യമില്ലെങ്കിൽ അവ ഓഫാക്കുക."
diff --git a/android/app/res/values-mn/strings.xml b/android/app/res/values-mn/strings.xml
index 8e4ab5d64bbe3361ec113875b378b4bc74d66d78..0eb7701e87752638f1b7fbdc750ff29f926dd279 100644
--- a/android/app/res/values-mn/strings.xml
+++ b/android/app/res/values-mn/strings.xml
@@ -129,9 +129,9 @@
"4ГБ-с дээш хэмжээтэй файлыг шилжүүлэх боломжгүй""Bluetooth-тэй холбогдох""Нислэгийн горимд Bluetooth асаалттай"
- "Хэрэв та Bluetooth-г асаалттай байлгавал энэ нь таныг дараагийн удаа нислэгийн горимд байхад асаалттай хэвээр байна"
+ "Хэрэв та Bluetooth-г асаалттай байлгавал таныг дараагийн удаа нислэгийн горимд байх үед утас тань үүнийг асаалттай байлгахыг санана""Bluetooth асаалттай хэвээр байна"
- "Таны утас Bluetooth-г нислэгийн горимд асаалттай байлгахыг санана. Үүнийг өөрчлөхийн тулд Bluetooth-г унтраана уу."
+ "Таны утас Bluetooth-г нислэгийн горимд асаалттай байлгахыг санана. Хэрэв та асаалттай байлгахыг хүсэхгүй байгаа бол Bluetooth-г унтрааж болно.""Wi-Fi болон Bluetooth асаалттай хэвээр байна"
- "Таны утас Wi-Fi болон Bluetooth-г нислэгийн горимд асаалттай байлгахыг санана. Үүнийг өөрчлөхийн тулд тэдгээрийг унтраана уу."
+ "Таны утас Wi-Fi болон Bluetooth-г нислэгийн горимд асаалттай байлгахыг санана. Хэрэв та асаалттай байлгахыг хүсэхгүй байгаа бол Wi-Fi болон Bluetooth-г унтрааж болно."
diff --git a/android/app/res/values-mr/strings.xml b/android/app/res/values-mr/strings.xml
index c274d557b1a38361632c61801cb2c1fd6cac26ea..ade07591772bfd106fda8486faeb1feaa2742b54 100644
--- a/android/app/res/values-mr/strings.xml
+++ b/android/app/res/values-mr/strings.xml
@@ -129,9 +129,9 @@
"4 GB हून मोठ्या फाइल ट्रान्सफर करता येणार नाहीत""ब्लूटूथशी कनेक्ट करा""विमान मोडमध्ये ब्लूटूथ सुरू आहे"
- "तुम्ही ब्लूटूथ सुरू ठेवल्यास, पुढील वेळी तुम्ही विमान मोडमध्ये असाल, तेव्हा ते सुरू राहील"
+ "तुम्ही ब्लूटूथ सुरू ठेवल्यास, पुढील वेळी विमान मोडमध्ये असाल, तेव्हा तुमचा फोन ते सुरू ठेवण्याचे लक्षात ठेवेल""ब्लूटूथ सुरू राहते"
- "तुमचा फोन विमान मोडमध्ये ब्लूटूथ सुरू ठेवण्याचे लक्षात ठेवतो. हे बदलण्यासाठी, ब्लूटूथ बंद करा."
+ "तुमचा फोन विमान मोडमध्ये ब्लूटूथ सुरू ठेवण्याचे लक्षात ठेवतो. तुम्हाला ब्लूटूथ सुरू ठेवायचे नसल्यास ते बंद करा.""वाय-फाय आणि ब्लूटूथ सुरू राहते"
- "तुमचा फोन विमान मोडमध्ये वाय-फाय आणि ब्लूटूथ सुरू ठेवण्याचे लक्षात ठेवतो. हे बदलण्यासाठी, ते बंद करा."
+ "तुमचा फोन विमान मोडमध्ये वाय-फाय आणि ब्लूटूथ सुरू ठेवण्याचे लक्षात ठेवतो. तुम्हाला वाय-फाय आणि ब्लूटूथ सुरू ठेवायचे नसल्यास ते बंद करा."
diff --git a/android/app/res/values-ms/strings.xml b/android/app/res/values-ms/strings.xml
index ec74831c7f23bf8ef79e892f23fd887a1aa85cdf..07d1f3acef7b0cd7af9ecdbcfdfb5862b3a1b07c 100644
--- a/android/app/res/values-ms/strings.xml
+++ b/android/app/res/values-ms/strings.xml
@@ -129,9 +129,9 @@
"Fail lebih besar daripada 4GB tidak boleh dipindahkan""Sambung ke Bluetooth""Bluetooth dihidupkan dalam mod pesawat"
- "Jika anda terus menghidupkan Bluetooth, pada kali seterusnya anda dalam mod pesawat, Bluetooth akan kekal dihidupkan"
+ "Jika anda terus menghidupkan Bluetooth, telefon anda akan ingat untuk membiarkan Bluetooth hidup pada kali seterusnya telefon anda berada dalam mod pesawat""Bluetooth kekal dihidupkan"
- "Telefon anda diingatkan untuk terus menghidupkan Bluetooth dalam mod pesawat. Untuk menukar tetapan ini, matikan Bluetooth."
+ "Telefon anda diingatkan untuk terus menghidupkan Bluetooth dalam mod pesawat. Matikan Bluetooth jika anda tidak mahu Bluetooth sentiasa hidup.""Wi-Fi dan Bluetooth kekal dihidupkan"
- "Telefon anda diingatkan untuk terus menghidupkan Wi-Fi dan Bluetooth dalam mod pesawat. Untuk menukar tetapan ini, matikan Wi-Fi dan Bluetooth."
+ "Telefon anda diingatkan untuk terus menghidupkan Wi-Fi dan Bluetooth dalam mod pesawat. Matikan Wi-Fi dan Bluetooth jika anda tidak mahu Wi-Fi dan Bluetooth sentiasa hidup."
diff --git a/android/app/res/values-my/strings.xml b/android/app/res/values-my/strings.xml
index 8e5803798f8e5e2bd9ce7dde9d21642feedc0134..b7c523df8729a62ff8deaa65d12d72ad2c39028f 100644
--- a/android/app/res/values-my/strings.xml
+++ b/android/app/res/values-my/strings.xml
@@ -129,9 +129,9 @@
"4GB ထက်ပိုကြီးသည့် ဖိုင်များကို လွှဲပြောင်းမရနိုင်ပါ""ဘလူးတုသ်သို့ ချိတ်ဆက်ရန်""လေယာဉ်ပျံမုဒ်တွင် ဘလူးတုသ် ပွင့်နေသည်"
- "ဘလူးတုသ် ဆက်ဖွင့်ထားပါက နောက်တစ်ကြိမ်လေယာဉ်ပျံမုဒ် သုံးချိန်တွင် ၎င်းဆက်ပွင့်နေမည်"
+ "ဘလူးတုသ် ဆက်ဖွင့်ထားပါက နောက်တစ်ကြိမ်လေယာဉ်ပျံမုဒ် သုံးချိန်တွင် ၎င်းဆက်ဖွင့်ရန် သင့်ဖုန်းက မှတ်ထားမည်။""ဘလူးတုသ် ဆက်ပွင့်နေသည်"
- "လေယာဉ်ပျံမုဒ်သုံးစဉ် ဘလူးတုသ် ဆက်ဖွင့်ထားရန် သင့်ဖုန်းက မှတ်မိသည်။ ပြောင်းရန် ဘလူးတုသ်ကို ပိတ်ပါ။"
+ "လေယာဉ်ပျံမုဒ်သုံးစဉ် ဘလူးတုသ် ဆက်ဖွင့်ထားရန် သင့်ဖုန်းက မှတ်မိသည်။ ဘလူးတုသ် ဆက်ဖွင့်မထားလိုပါက ပိတ်နိုင်သည်။""Wi-Fi နှင့် ဘလူးတုသ် ဆက်ဖွင့်ထားသည်"
- "လေယာဉ်ပျံမုဒ်သုံးစဉ် Wi-Fi နှင့် ဘလူးတုသ် ဆက်ဖွင့်ထားရန် သင့်ဖုန်းက မှတ်မိသည်။ ပြောင်းရန် ၎င်းတို့ကို ပိတ်ပါ။"
+ "လေယာဉ်ပျံမုဒ်သုံးစဉ် Wi-Fi နှင့် ဘလူးတုသ် ဆက်ဖွင့်ထားရန် သင့်ဖုန်းက မှတ်မိသည်။ Wi-Fi နှင့် ဘလူးတုသ် ဆက်ဖွင့်မထားလိုပါက ပိတ်နိုင်သည်။"
diff --git a/android/app/res/values-nb/strings.xml b/android/app/res/values-nb/strings.xml
index eeb0aa114e2c4f1b83ea550953545a4f26b650bf..8c975818bb588350cf16337737235524f6eb438c 100644
--- a/android/app/res/values-nb/strings.xml
+++ b/android/app/res/values-nb/strings.xml
@@ -129,9 +129,9 @@
"Filer som er større enn 4 GB, kan ikke overføres""Koble til Bluetooth""Bluetooth er på i flymodus"
- "Hvis du lar Bluetooth være på, gjelder dette også for den neste gangen du bruker flymodus"
+ "Hvis du lar Bluetooth være på, husker telefonen dette til den neste gangen du bruker flymodus""Bluetooth blir værende på"
- "Telefonen husker at Bluetooth skal være på i flymodus. Du kan endre dette ved å slå av Bluetooth."
+ "Telefonen husker at Bluetooth skal være på i flymodus. Slå av Bluetooth hvis du ikke vil at det skal være på.""Wifi og Bluetooth holdes påslått"
- "Telefonen husker at wifi og Bluetooth skal være på i flymodus. Du kan endre dette ved å slå dem av."
+ "Telefonen husker at wifi og Bluetooth skal være på i flymodus. Slå av wifi og Bluetooth hvis du ikke vil at de skal være på."
diff --git a/android/app/res/values-ne/strings.xml b/android/app/res/values-ne/strings.xml
index 0821d6cf388df476eabba5df3a66e6e10b1667f1..67e0bf8c828d01f5fdb6f29923e6add477f42879 100644
--- a/android/app/res/values-ne/strings.xml
+++ b/android/app/res/values-ne/strings.xml
@@ -129,9 +129,9 @@
"४ जि.बि. भन्दा ठूला फाइलहरूलाई स्थानान्तरण गर्न सकिँदैन""ब्लुटुथमा कनेक्ट गर्नुहोस्""हवाइजहाज मोडमा ब्लुटुथ अन राखियोस्"
- "तपाईंले ब्लुटुथ अन राखिराख्नुभयो भने तपाईंको फोन अर्को पटक हवाइजहाज मोडमा लैजाँदा ब्लुटुथ अन रहिरहने छ"
+ "तपाईंले ब्लुटुथ अन राखिराख्नुभयो भने तपाईंले आफ्नो फोन अर्को पटक हवाइजहाज मोडमा लैजाँदा तपाईंको फोनले ब्लुटुथ अन राख्नु पर्ने कुरा याद गर्छ""ब्लुटुथ अन रहन्छ"
- "हवाइजहाज मोडमा पनि तपाईंको फोनको ब्लुटुथ अन नै रहिरहने छ। यो सेटिङ बदल्न चाहनुहुन्छ भने ब्लुटुथ अफ गर्नुहोस्।"
+ "तपाईंको फोन अर्को पटक हवाइजहाज मोडमा लैजाँदा तपाईंको फोनले ब्लुटुथ अन राख्नु पर्ने कुरा याद गर्छ तपाईं ब्लुटुथ अन भइनरहोस् भन्ने चाहनुहुन्छ भने ब्लुटुथ अफ गर्नुहोस्।""Wi-Fi र ब्लुटुथ अन रहिरहने छन्"
- "हवाइजहाज मोडमा पनि तपाईंको फोनको Wi-Fi र ब्लुटुथ अन नै रहिरहने छन्। यो सेटिङ बदल्न चाहनुहुन्छ भने यी दुवै अफ गर्नुहोस्।"
+ "हवाइजहाज मोडमा पनि तपाईंको फोनको Wi-Fi र ब्लुटुथ अन नै रहिरहने छन्। तपाईं Wi-Fi र ब्लुटुथ अन भइनरहोस् भन्ने चाहनुहुन्छ भने तिनलाई अफ गर्नुहोस्।"
diff --git a/android/app/res/values-nl/strings.xml b/android/app/res/values-nl/strings.xml
index 5696f568ecb52ba80497f8cb9c69ad62cfef9244..83493b168c35c64c8513001c001d3754e3eea719 100644
--- a/android/app/res/values-nl/strings.xml
+++ b/android/app/res/values-nl/strings.xml
@@ -129,9 +129,9 @@
"Bestanden groter dan 4 GB kunnen niet worden overgedragen""Verbinding maken met bluetooth""Bluetooth aan in de vliegtuigmodus"
- "Als je bluetooth laat aanstaan, blijft het aanstaan als je de vliegtuigmodus weer aanzet"
+ "Als je bluetooth laat aanstaan, onthoudt je telefoon dit en blijft bluetooth aanstaan als je de vliegtuigmodus weer aanzet""Bluetooth blijft aan"
- "Bluetooth op je telefoon blijft aan in de vliegtuigmodus. Zet bluetooth uit om dit te wijzigen."
+ "Bluetooth op je telefoon blijft aan in de vliegtuigmodus. Zet bluetooth uit als je niet wilt dat dit aan blijft.""Wifi en bluetooth blijven aan"
- "De wifi en bluetooth op je telefoon blijven aan in de vliegtuigmodus. Zet ze uit om dit te wijzigen."
+ "Wifi en bluetooth op je telefoon blijven aan in de vliegtuigmodus. Zet wifi en bluetooth uit als je niet wilt dat ze aan blijven."
diff --git a/android/app/res/values-or/strings.xml b/android/app/res/values-or/strings.xml
index 01665b61e725a82c42f577628293bc3c96c8c6f1..66ecc7d59e81af2959e17b02df852d75b96683fc 100644
--- a/android/app/res/values-or/strings.xml
+++ b/android/app/res/values-or/strings.xml
@@ -19,7 +19,7 @@
"ଡାଉନଲୋଡ ମ୍ୟାନେଜର୍କୁ ଆକ୍ସେସ୍ କରନ୍ତୁ।""BluetoothShare ମ୍ୟାନେଜର୍ ଆକ୍ସେସ୍ କରି ଫାଇଲ୍ଗୁଡ଼ିକ ଟ୍ରାନ୍ସଫର୍ କରିବାକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।""ବ୍ଲୁଟୁଥ୍ ଡିଭାଇସର ଆକ୍ସେସକୁ ଗ୍ରହଣ-ସୂଚୀରେ ରଖନ୍ତୁ।"
- "ଏକ ବ୍ଲୁଟୁଥ୍ ଡିଭାଇସକୁ ଅସ୍ଥାୟୀ ଭାବେ ଗ୍ରହଣ-ସୂଚୀରେ ରଖିବାକୁ ଆପଟିକୁ ଅନୁମତି ଦେଇଥାଏ, ଯାହା ଦ୍ୱାରା ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ସୁନିଶ୍ଚିତକରଣ ବିନା ଏହି ଡିଭାଇସକୁ ଫାଇଲ୍ ପଠାଇବା ପାଇଁ ସେହି ଡିଭାଇସକୁ ଅନୁମତି ମିଳିଥାଏ।"
+ "ଏକ ବ୍ଲୁଟୁଥ ଡିଭାଇସକୁ ଅସ୍ଥାୟୀ ଭାବେ ଗ୍ରହଣ-ସୂଚୀରେ ରଖିବାକୁ ଆପଟିକୁ ଅନୁମତି ଦେଇଥାଏ, ଯାହା ଦ୍ୱାରା ୟୁଜରଙ୍କ ସୁନିଶ୍ଚିତକରଣ ବିନା ଏହି ଡିଭାଇସକୁ ଫାଇଲ ପଠାଇବା ପାଇଁ ସେହି ଡିଭାଇସକୁ ଅନୁମତି ମିଳିଥାଏ।""ବ୍ଲୁଟୁଥ""ଅଜଣା ଡିଭାଇସ୍""ଅଜଣା"
@@ -92,7 +92,7 @@
"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍ ସଫଳତାପୂର୍ବକ ସମ୍ପୂର୍ଣ୍ଣ ହେଲା।""କଣ୍ଟେଣ୍ଟ ସପୋର୍ଟ କରୁନାହିଁ।""ଟାର୍ଗେଟ୍ ଡିଭାଇସ୍ ଦ୍ୱାରା ଟ୍ରାନ୍ସଫର୍ ପ୍ରତିବନ୍ଧିତ କରାଯାଇଛି।"
- "ୟୁଜର୍ଙ୍କ ଦ୍ୱାରା ଟ୍ରାନ୍ସଫର୍ କ୍ୟାନ୍ସଲ୍ କରାଗଲା।"
+ "ୟୁଜରଙ୍କ ଦ୍ୱାରା ଟ୍ରାନ୍ସଫର କେନ୍ସଲ କରାଯାଇଛି।""ଷ୍ଟୋରେଜ୍ ସମସ୍ୟା।""କୌଣସି USB ଷ୍ଟୋରେଜ୍ ନାହିଁ।""କୌଣସି SD କାର୍ଡ ନାହିଁ। ଟ୍ରାନ୍ସଫର୍ କରାଯାଇଥିବା ଫାଇଲ୍ଗୁଡ଼ିକୁ ସେଭ୍ କରିବା ପାଇଁ ଗୋଟିଏ SD କାର୍ଡ ଭର୍ତ୍ତି କରନ୍ତୁ।"
@@ -129,9 +129,9 @@
"4GBରୁ ବଡ଼ ଫାଇଲ୍ଗୁଡ଼ିକୁ ଟ୍ରାନ୍ସଫର୍ କରାଯାଇପାରିବ ନାହିଁ""ବ୍ଲୁଟୁଥ୍ ସହ ସଂଯୋଗ କରନ୍ତୁ""ଏୟାରପ୍ଲେନ ମୋଡରେ ବ୍ଲୁଟୁଥ ଚାଲୁ ଅଛି"
- "ଯଦି ଆପଣ ବ୍ଲୁଟୁଥ ଚାଲୁ ରଖନ୍ତି, ତେବେ ଆପଣ ପରବର୍ତ୍ତୀ ଥର ଏୟାରପ୍ଲେନ ମୋଡରେ ଥିବା ସମୟରେ ଏହା ଚାଲୁ ରହିବ"
+ "ଯଦି ଆପଣ ବ୍ଲୁଟୁଥକୁ ଚାଲୁ ରଖନ୍ତି, ତେବେ ଆପଣ ପରବର୍ତ୍ତୀ ଥର ଏୟାରପ୍ଲେନ ମୋଡରେ ଥିବା ସମୟରେ ଆପଣଙ୍କ ଫୋନ ଏହାକୁ ଚାଲୁ ରଖିବା ପାଇଁ ମନେ ରଖିବ""ବ୍ଲୁଟୁଥ ଚାଲୁ ରହେ"
- "ଆପଣଙ୍କ ଫୋନ ଏୟାରପ୍ଲେନ ମୋଡରେ ବ୍ଲୁଟୁଥ ଚାଲୁ ରଖିବାକୁ ମନେ ରଖେ। ଏହାକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, ବ୍ଲୁଟୁଥ ବନ୍ଦ କରନ୍ତୁ।"
+ "ଆପଣଙ୍କ ଫୋନ ଏୟାରପ୍ଲେନ ମୋଡରେ ବ୍ଲୁଟୁଥକୁ ଚାଲୁ ରଖିବା ପାଇଁ ମନେ ରଖେ। ଯଦି ଆପଣ ବ୍ଲୁଟୁଥ ଚାଲୁ ରଖିବାକୁ ଚାହାଁନ୍ତି ନାହିଁ ତେବେ ଏହାକୁ ବନ୍ଦ କରନ୍ତୁ।""ୱାଇ-ଫାଇ ଏବଂ ବ୍ଲୁଟୁଥ ଚାଲୁ ରହେ"
- "ଆପଣଙ୍କ ଫୋନ ଏୟାରପ୍ଲେନ ମୋଡରେ ୱାଇ-ଫାଇ ଏବଂ ବ୍ଲୁଟୁଥ ଚାଲୁ ରଖିବାକୁ ମନେ ରଖେ। ଏହାକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, ସେଗୁଡ଼ିକୁ ବନ୍ଦ କରନ୍ତୁ।"
+ "ଆପଣଙ୍କ ଫୋନ ଏୟାରପ୍ଲେନ ମୋଡରେ ୱାଇ-ଫାଇ ଏବଂ ବ୍ଲୁଟୁଥକୁ ଚାଲୁ ରଖିବା ପାଇଁ ମନେ ରଖେ। ଯଦି ଆପଣ ୱାଇ-ଫାଇ ଏବଂ ବ୍ଲୁଟୁଥ ଚାଲୁ ରଖିବାକୁ ଚାହାଁନ୍ତି ନାହିଁ ତେବେ ସେଗୁଡ଼ିକୁ ବନ୍ଦ କରନ୍ତୁ।"
diff --git a/android/app/res/values-pa/strings.xml b/android/app/res/values-pa/strings.xml
index 5196622f2e67440e9561422e031ad5ba9f3dd470..13570e16b289b30b30bdbbf777223f29d652f151 100644
--- a/android/app/res/values-pa/strings.xml
+++ b/android/app/res/values-pa/strings.xml
@@ -129,9 +129,9 @@
"4GB ਤੋਂ ਜ਼ਿਆਦਾ ਵੱਡੀਆਂ ਫ਼ਾਈਲਾਂ ਨੂੰ ਟ੍ਰਾਂਸਫ਼ਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ""ਬਲੂਟੁੱਥ ਨਾਲ ਕਨੈਕਟ ਕਰੋ""ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਬਲੂਟੁੱਥ ਚਾਲੂ ਹੈ"
- "ਜੇ ਤੁਸੀਂ ਬਲੂਟੁੱਥ ਨੂੰ ਚਾਲੂ ਰੱਖਦੇ ਹੋ, ਤਾਂ ਅਗਲੀ ਵਾਰ ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਇਹ ਚਾਲੂ ਰਹੇਗਾ"
+ "ਜੇ ਤੁਸੀਂ ਬਲੂਟੁੱਥ ਨੂੰ ਚਾਲੂ ਰੱਖਦੇ ਹੋ, ਤਾਂ ਅਗਲੀ ਵਾਰ ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਤੁਹਾਡਾ ਫ਼ੋਨ ਇਸਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖੇਗਾ""ਬਲੂਟੁੱਥ ਚਾਲੂ ਰਹਿੰਦਾ ਹੈ"
- "ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਬਲੂਟੁੱਥ ਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖਦਾ ਹੈ। ਇਸਨੂੰ ਬਦਲਣ ਲਈ, ਬਲੂਟੁੱਥ ਬੰਦ ਕਰੋ।"
+ "ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਬਲੂਟੁੱਥ ਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖਦਾ ਹੈ। ਜੇ ਤੁਸੀਂ ਇਸਨੂੰ ਚਾਲੂ ਨਹੀਂ ਰੱਖਣਾ ਚਾਹੁੰਦੇ, ਤਾਂ ਬਲੂਟੁੱਥ ਨੂੰ ਬੰਦ ਕਰੋ।""ਵਾਈ-ਫਾਈ ਅਤੇ ਬਲੂਟੁੱਥ ਚਾਲੂ ਰਹਿੰਦੇ ਹਨ"
- "ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਵਾਈ-ਫਾਈ ਅਤੇ ਬਲੂਟੁੱਥ ਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖਦਾ ਹੈ। ਇਸਨੂੰ ਬਦਲਣ ਲਈ, ਉਨ੍ਹਾਂ ਨੂੰ ਬੰਦ ਕਰੋ।"
+ "ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਵਾਈ-ਫਾਈ ਅਤੇ ਬਲੂਟੁੱਥ ਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖਦਾ ਹੈ। ਜੇ ਤੁਸੀਂ ਇਨ੍ਹਾਂ ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਰੱਖਣਾ ਚਾਹੁੰਦੇ, ਤਾਂ ਵਾਈ-ਫਾਈ ਅਤੇ ਬਲੂਟੁੱਥ ਨੂੰ ਬੰਦ ਕਰੋ।"
diff --git a/android/app/res/values-pl/strings.xml b/android/app/res/values-pl/strings.xml
index 8319c9d4083cc6143edbaeda775b601e7964f01a..eca2def4a8838b1a6c2f99955b3fcb37d8224c6b 100644
--- a/android/app/res/values-pl/strings.xml
+++ b/android/app/res/values-pl/strings.xml
@@ -129,9 +129,9 @@
"Nie można przenieść plików przekraczających 4 GB""Nawiązywanie połączeń przez Bluetooth""Bluetooth włączony w trybie samolotowym"
- "Jeśli pozostawisz Bluetooth włączony, będzie aktywny, gdy następnym razem uruchomisz tryb samolotowy"
+ "Jeśli pozostawisz włączony Bluetooth, telefon zachowa się podobnie przy kolejnym przejściu w tryb samolotowy""Bluetooth pozostanie włączony"
- "Bluetooth na telefonie pozostaje włączony w trybie samolotowym. Aby to zmienić, wyłącz tę funkcję."
+ "Bluetooth na telefonie pozostaje włączony w trybie samolotowym. Wyłącz Bluetooth, jeśli nie chcesz, żeby pozostawał włączony.""Wi-Fi i Bluetooth pozostają włączone"
- "Wi-Fi i Bluetooth na telefonie pozostają włączone w trybie samolotowym. Aby to zmienić, wyłącz te funkcje."
+ "Wi-Fi i Bluetooth na telefonie pozostają włączone w trybie samolotowym. Wyłącz Wi-Fi i Bluetooth, jeśli nie chcesz, żeby funkcje te pozostawały włączone."
diff --git a/android/app/res/values-pt-rPT/strings.xml b/android/app/res/values-pt-rPT/strings.xml
index 204acda30e07d97bb6a91bd73277053ab5d627c0..4469a011daa985b6ecc2b95dea553acf6130b09d 100644
--- a/android/app/res/values-pt-rPT/strings.xml
+++ b/android/app/res/values-pt-rPT/strings.xml
@@ -86,7 +86,7 @@
"Não existe espaço suficiente na memória USB para guardar o ficheiro.""Não existe espaço suficiente no cartão SD para guardar o ficheiro.""Espaço necessário: %1$s"
- "Existem demasiados pedidos em processamento. Tente novamente mais tarde."
+ "Existem demasiados pedidos em processamento. Tente mais tarde.""Ainda não foi iniciada a transferência do ficheiro.""Transferência do ficheiro em curso.""Transferência de ficheiros concluída com êxito."
@@ -129,9 +129,9 @@
"Não é possível transferir os ficheiros com mais de 4 GB.""Ligar ao Bluetooth""Bluetooth ativado no modo de avião"
- "Se mantiver o Bluetooth ativado, vai permanecer assim da próxima vez que estiver no modo de avião"
+ "Se mantiver o Bluetooth ativado, o telemóvel vai lembrar-se de o manter ativado da próxima vez que estiver no modo de avião""O Bluetooth mantém-se ativado"
- "O seu telemóvel mantém o Bluetooth ativado no modo de avião. Para alterar isto, desative o Bluetooth."
+ "O seu telemóvel mantém o Bluetooth ativado no modo de avião. Desative o Bluetooth se não quiser que fique ativado.""O Wi-Fi e o Bluetooth mantêm-se ativados"
- "O seu telemóvel mantém o Wi-Fi e o Bluetooth ativados no modo de avião. Para alterar isto, desative-os."
+ "O seu telemóvel mantém o Wi-Fi e o Bluetooth ativados no modo de avião. Desative o Wi-Fi e o Bluetooth se não quiser que fiquem ativados."
diff --git a/android/app/res/values-pt/strings.xml b/android/app/res/values-pt/strings.xml
index 37eb1b121b60c7c7be9535d5e6fd613196a85d73..612ef7ccbee97d04de16212d2c6e0304b57a41c0 100644
--- a/android/app/res/values-pt/strings.xml
+++ b/android/app/res/values-pt/strings.xml
@@ -129,9 +129,9 @@
"Não é possível transferir arquivos maiores que 4 GB""Conectar ao Bluetooth""Bluetooth ativado no modo avião"
- "Se você deixar o Bluetooth ativado, ele vai continuar assim na próxima vez em que o dispositivo estiver no modo avião"
+ "Se você escolher manter o Bluetooth ativado, essa configuração vai ser aplicada na próxima vez que usar o modo avião""O Bluetooth fica ativado"
- "O smartphone vai manter o Bluetooth ativado no modo avião. Para mudar essa configuração, desative o Bluetooth."
+ "O smartphone vai manter o Bluetooth ativado no modo avião. Ele pode ser desativado manualmente se você preferir.""O Wi-Fi e o Bluetooth ficam ativados"
- "O smartphone vai manter o Wi-Fi e o Bluetooth ativados no modo avião. Para mudar essa configuração, desative os dois."
+ "O smartphone vai manter o Wi-Fi e o Bluetooth ativados no modo avião. Eles podem ser desativados manualmente se você preferir."
diff --git a/android/app/res/values-ro/strings.xml b/android/app/res/values-ro/strings.xml
index 739b508361d7082cdd18d923ffa8bfbcd4d3f049..61bfb10cdac81528a37acd80f7fc9cc14fb4f4aa 100644
--- a/android/app/res/values-ro/strings.xml
+++ b/android/app/res/values-ro/strings.xml
@@ -129,9 +129,9 @@
"Fișierele mai mari de 4 GB nu pot fi transferate""Conectează-te la Bluetooth""Bluetooth activat în modul Avion"
- "Dacă păstrezi Bluetooth activat, va rămâne activat data viitoare când ești în modul Avion"
+ "Dacă păstrezi Bluetooth activat, telefonul tău va reține să-l păstreze activat data viitoare când ești în modul Avion""Bluetooth rămâne activat"
- "Telefonul reține să păstreze Bluetooth activat în modul avion. Ca să schimbi asta, dezactivează Bluetooth."
+ "Telefonul reține să păstreze Bluetooth activat în modul Avion. Dezactivează Bluetooth dacă nu vrei să rămână activat.""Wi-Fi și Bluetooth rămân activate"
- "Telefonul reține să păstreze funcțiile Wi-Fi și Bluetooth activate în modul Avion. Ca să schimbi asta, dezactivează-le."
+ "Telefonul reține să păstreze funcțiile Wi-Fi și Bluetooth activate în modul Avion. Dezactivează Wi-Fi și Bluetooth dacă nu vrei să rămână activate."
diff --git a/android/app/res/values-ru/strings.xml b/android/app/res/values-ru/strings.xml
index 23b6cf0e0e065787993cae3e92a0c269e78062a3..d1e42c858283420046a55194b37efc33656b8500 100644
--- a/android/app/res/values-ru/strings.xml
+++ b/android/app/res/values-ru/strings.xml
@@ -129,9 +129,9 @@
"Можно перенести только файлы размером до 4 ГБ.""Подключиться по Bluetooth""Функция Bluetooth будет включена в режиме полета"
- "Если не отключать Bluetooth, в следующий раз эта функция останется включенной в режиме полета."
+ "Если не отключить функцию Bluetooth, в следующий раз она останется включенной в режиме полета.""Функция Bluetooth остается включенной"
- "Bluetooth останется включенной в режиме полета. Отключить эту функцию можно в любой момент."
+ "Функция Bluetooth останется включенной в режиме полета. Вы можете отключить ее, если хотите.""Функции Wi‑Fi и Bluetooth остаются включенными"
- "Wi‑Fi и Bluetooth останутся включенными в режиме полета. Отключить эти функции можно в любой момент."
+ "Wi‑Fi и Bluetooth останутся включенными в режиме полета. Вы можете отключить их, если хотите."
diff --git a/android/app/res/values-si/strings.xml b/android/app/res/values-si/strings.xml
index 28ce3f7ba49eee1e13594283a5f2cd488ba1019b..4da66455fbb0ee2ac3d3b1adbc2dc9b38002b506 100644
--- a/android/app/res/values-si/strings.xml
+++ b/android/app/res/values-si/strings.xml
@@ -129,9 +129,9 @@
"4GBට වඩා විශාල ගොනු මාරු කළ නොහැකිය""බ්ලූටූත් වෙත සබඳින්න""අහස්යානා ආකාරයේ බ්ලූටූත් ක්රියාත්මකයි"
- "ඔබ බ්ලූටූත් ක්රියාත්මක කර තබා ගන්නේ නම්, ඔබ මීළඟ වතාවේ අහස්යානා ආකාරයේ සිටින විට එය ක්රියාත්මකව පවතිනු ඇත"
+ "ඔබ බ්ලූටූත් ක්රියාත්මක කර තබා ගන්නේ නම්, ඔබ අහස්යානා ආකාරයේ සිටින මීළඟ වතාවේ එය ක්රියාත්මක කිරීමට ඔබේ දුරකථනයට මතක තිබෙනු ඇත.""බ්ලූටූත් ක්රියාත්මකව පවතී"
- "ඔබේ දුරකථනයට අහස්යානා ආකාරයේ බ්ලූටූත් ක්රියාත්මකව තබා ගැනීමට මතකයි. මෙය වෙනස් කිරීමට, බ්ලූටූත් ක්රියාවිරහිත කරන්න."
+ "ඔබේ දුරකථනයට අහස්යානා ආකාරයේ බ්ලූටූත් ක්රියාත්මකව තබා ගැනීමට මතකයි. ඔබට බ්ලූටූත් ක්රියාත්මක වීමට අවශ්ය නොවේ නම් එය ක්රියාවිරහිත කරන්න.""Wi-Fi සහ බ්ලූටූත් ක්රියාත්මකව පවතී"
- "ඔබේ දුරකථනයට අහස්යානා ආකාරයේ Wi-Fi සහ බ්ලූටූත් ක්රියාත්මකව තබා ගැනීමට මතකයි. මෙය වෙනස් කිරීමට, ඒවා ක්රියා විරහිත කරන්න."
+ "ඔබේ දුරකථනයට අහස්යානා ආකාරයේ Wi-Fi සහ බ්ලූටූත් ක්රියාත්මකව තබා ගැනීමට මතකයි. Wi-Fi සහ බ්ලූටූත් ඒවා ක්රියාත්මක වීමට ඔබට අවශ්ය නැතිනම් ක්රියා විරහිත කරන්න."
diff --git a/android/app/res/values-sk/strings.xml b/android/app/res/values-sk/strings.xml
index 57b3f543db8cede8b6ba801e3feb570d0200dfb5..fa4a7d450fa383e955951322ff42db85c48c9a85 100644
--- a/android/app/res/values-sk/strings.xml
+++ b/android/app/res/values-sk/strings.xml
@@ -129,9 +129,9 @@
"Súbory väčšie ako 4 GB sa nedajú preniesť""Pripojiť k zariadeniu Bluetooth""Rozhranie Bluetooth bude v režime v lietadle zapnuté"
- "Ak Bluetooth nevypnete, nevypne sa ani pri ďalšom aktivovaní režimu v lietadle"
+ "Ak ponecháte rozhranie Bluetooth zapnuté, váš telefón si zapamätá, že ho má ponechať zapnuté pri ďalšom aktivovaní režimu v lietadle""Rozhranie Bluetooth zostane zapnuté"
- "Telefón si pamätá, aby v režime v lietadle nevypínal Bluetooth. Ak to chcete zmeniť, vypnite Bluetooth."
+ "Telefón si pamätá, aby v režime v lietadle nevypínal rozhranie Bluetooth. Ak ho nechcete ponechať zapnuté, vypnite ho.""Wi‑Fi a Bluetooth zostanú zapnuté"
- "Telefón si pamätá, aby v režime v lietadle nevypínal Wi‑Fi ani Bluetooth. Ak to chcete zmeniť, vypnite ich."
+ "Telefón si pamätá, aby v režime v lietadle nevypínal Wi‑Fi ani Bluetooth. Ak ich nechcete ponechať zapnuté, vypnite ich."
diff --git a/android/app/res/values-sl/strings.xml b/android/app/res/values-sl/strings.xml
index bd31d725c5e07feb0109d0d2c29879a25ba1e28c..ac1de2bd28d9d33aae4cc7cac4b4c3c208b78836 100644
--- a/android/app/res/values-sl/strings.xml
+++ b/android/app/res/values-sl/strings.xml
@@ -129,9 +129,9 @@
"Datotek, večjih od 4 GB, ni mogoče prenesti""Povezovanje z Bluetoothom""Bluetooth je vklopljen v načinu za letalo"
- "Če pustite Bluetooth vklopljen, bo ob naslednjem preklopu na način za letalo ostal vklopljen."
+ "Če pustite Bluetooth vklopljen, bo telefon ob naslednjem preklopu na način za letalo pustil Bluetooth vklopljen.""Bluetooth ostane vklopljen"
- "Telefon v načinu za letalo pusti Bluetooth vklopljen. Če želite to spremeniti, izklopite Bluetooth."
+ "Telefon v načinu za letalo pusti Bluetooth vklopljen. Če ne želite, da ostane vklopljen, izklopite vmesnik Bluetooth.""Wi-Fi in Bluetooth ostaneta vklopljena"
- "Telefon v načinu za letalo pusti Wi-Fi in Bluetooth vklopljena. Če želite to spremeniti, ju izklopite."
+ "Telefon v načinu za letalo pusti Wi-Fi in Bluetooth vklopljena. Če ne želite, da Wi-Fi in Bluetooth ostaneta vklopljena, ju izklopite."
diff --git a/android/app/res/values-sq/strings.xml b/android/app/res/values-sq/strings.xml
index 7f45275ff1ce035306e6390716dcb5fbb35bae5e..e084c926d56fd828bf7fa8ca8424c51deff51b96 100644
--- a/android/app/res/values-sq/strings.xml
+++ b/android/app/res/values-sq/strings.xml
@@ -129,9 +129,9 @@
"Skedarët më të mëdhenj se 4 GB nuk mund të transferohen""Lidhu me Bluetooth""Bluetooth-i aktiv në modalitetin e aeroplanit"
- "Nëse e mban Bluetooth-in të aktivizuar, ai do të qëndrojë aktiv herën tjetër kur të jesh në modalitetin e aeroplanit"
+ "Nëse e mban Bluetooth-in të aktivizuar, telefoni yt do të kujtohet ta mbajë atë të aktivizuar herën tjetër kur të jesh në modalitetin e aeroplanit""Bluetooth qëndron i aktivizuar"
- "Telefoni yt kujtohet që ta mbajë Bluetooth-in të aktivizuar në modalitetin e aeroplanit. Për ta ndryshuar këtë, çaktivizoje Bluetooth-in."
+ "Telefoni yt kujtohet që ta mbajë Bluetooth-in të aktivizuar në modalitetin e aeroplanit. Çaktivizo Bluetooth-in nëse nuk dëshiron që të qëndrojë i aktivizuar.""Wi-Fi dhe Bluetooth-i qëndrojnë aktivë"
- "Telefoni yt kujtohet që ta mbajë Wi-Fi dhe Bluetooth-in të aktivizuar në modalitetin e aeroplanit. Për ta ndryshuar këtë, çaktivizoji."
+ "Telefoni yt kujtohet që ta mbajë Wi-Fi dhe Bluetooth-in të aktivizuar në modalitetin e aeroplanit. Çaktivizo Wi-Fi dhe Bluetooth-in nëse nuk dëshiron që të qëndrojnë aktivë."
diff --git a/android/app/res/values-sr/strings.xml b/android/app/res/values-sr/strings.xml
index 3393376bf6dc6f1df62b11f08468ac640bdfd1c3..b7092082851bc76716177ef1cd4b29dde1dfff6f 100644
--- a/android/app/res/values-sr/strings.xml
+++ b/android/app/res/values-sr/strings.xml
@@ -129,9 +129,9 @@
"Не могу да се преносе датотеке веће од 4 GB""Повежи са Bluetooth-ом""Bluetooth је укључен у режиму рада у авиону"
- "Ако одлучите да не искључујете Bluetooth, остаће укључен следећи пут када будете у режиму рада у авиону"
+ "Ако одлучите да не искључујете Bluetooth, телефон ће запамтити да га не искључује следећи пут када будете у режиму рада у авиону""Bluetooth се не искључује"
- "Телефон памти да не треба да искључује Bluetooth у режиму рада у авиону. Да бисте то променили, искључите Bluetooth."
+ "Телефон памти да не треба да искључује Bluetooth у режиму рада у авиону. Искључите Bluetooth ако не желите да остане укључен.""WiFi и Bluetooth остају укључени"
- "Телефон памти да не треба да искључује WiFi и Bluetooth у режиму рада у авиону. Да бисте то променили, искључите их."
+ "Телефон памти да не треба да искључује WiFi и Bluetooth у режиму рада у авиону. Искључите WiFi и Bluetooth ако не желите да остану укључени."
diff --git a/android/app/res/values-sv/strings.xml b/android/app/res/values-sv/strings.xml
index 9cd1dce95c5b4d049d850e45df573273e7c29938..e8f951e8211d7419d395f9227c761bf69ff6794f 100644
--- a/android/app/res/values-sv/strings.xml
+++ b/android/app/res/values-sv/strings.xml
@@ -129,9 +129,9 @@
"Det går inte att överföra filer som är större än 4 GB""Anslut till Bluetooth""Håll Bluetooth aktiverat i flygplansläge"
- "Om du har Bluetooth aktiverat så fortsätter det vara aktiverat nästa gång du sätter på flygplansläget"
+ "Om du håller wifi aktiverat kommer telefonen ihåg att hålla det aktiverat nästa gång du använder flygplansläge.""Bluetooth förblir aktiverat"
- "Telefonen kommer ihåg att hålla Bluetooth aktiverat i flygplansläge. Om du vill ändra det stänger du av Bluetooth."
+ "Telefonen kommer ihåg att hålla Bluetooth aktiverat i flygplansläge. Inaktivera Bluetooth om du inte vill att det ska hållas aktiverat.""Wifi och Bluetooth ska vara aktiverade"
- "Telefonen kommer ihåg att hålla wifi och Bluetooth aktiverade i flygplansläge. Om du vill ändra det stänger du av dem."
+ "Telefonen kommer ihåg att hålla wifi och Bluetooth aktiverade i flygplansläge. Du kan inaktivera wifi och Bluetooth om du inte vill hålla dem aktiverade."
diff --git a/android/app/res/values-sw/strings.xml b/android/app/res/values-sw/strings.xml
index 8a1530871a430f9bb3d2b457c472d5ae86dab91d..d3f5875b67c8f438a32d7d9daa8c53816eccfb4b 100644
--- a/android/app/res/values-sw/strings.xml
+++ b/android/app/res/values-sw/strings.xml
@@ -129,9 +129,9 @@
"Haiwezi kutuma faili zinazozidi GB 4""Unganisha kwenye Bluetooth""Bluetooth itawashwa katika hali ya ndegeni"
- "Usipozima Bluetooth, itaendelea kuwaka utakapowasha hali ya ndegeni"
+ "Usipozima Bluetooth, simu yako itakumbuka kuiwasha wakati mwingine unapokuwa katika hali ya ndegeni""Bluetooth itaendelea kuwaka"
- "Simu yako itaendelea kuwasha Bluetooth ukiwa katika hali ya ndegeni. Zima Bluetooth ili ubadili hali hii."
+ "Simu yako itaendelea kuwasha Bluetooth katika hali ya ndegeni. Zima Bluetooth iwapo hutaki iendelee kuwaka.""Wi-Fi na Bluetooth zitaendelea kuwaka"
- "Simu yako itaendelea kuwasha Wi-Fi na Bluetooth ukiwa katika hali ya ndegeni. Zima Wi-Fi na Bluetooth ili ubadilishe hali hii."
+ "Simu yako itaendelea kuwasha Wi-Fi na Bluetooth ukiwa katika hali ya ndegeni. Zima Wi-Fi na Bluetooth ikiwa hutaki ziendelee kuwaka."
diff --git a/android/app/res/values-ta/strings.xml b/android/app/res/values-ta/strings.xml
index 7460d182ad8da97c6f92db8aa8f4c9bb2f010e5b..0cd15afd99934787a5b00323f6ae0cc81ee441fc 100644
--- a/android/app/res/values-ta/strings.xml
+++ b/android/app/res/values-ta/strings.xml
@@ -129,9 +129,9 @@
"4ஜி.பை.க்கு மேலிருக்கும் ஃபைல்களை இடமாற்ற முடியாது""புளூடூத் உடன் இணை""விமானப் பயன்முறையில் புளூடூத்தை இயக்குதல்"
- "நீங்கள் புளூடூத்தை இயக்கத்தில் வைத்திருந்தால், அடுத்த முறை நீங்கள் விமானப் பயன்முறையில் இருக்கும்போது அது இயக்கத்திலேயே இருக்கும்"
+ "புளூடூத்தை இயக்கத்தில் வைத்திருந்தால், அடுத்த முறை நீங்கள் விமானப் பயன்முறையைப் பயன்படுத்தும்போது உங்கள் மொபைல் புளூடூத்தை இயக்கத்தில் வைக்கும்""புளூடூத் இயக்கத்திலேயே இருக்கும்"
- "விமானப் பயன்முறையில் புளூடூத்தை உங்கள் மொபைல் இயக்கத்திலேயே வைத்திருக்கும். இதை மாற்ற புளூடூத்தை முடக்கவும்."
+ "விமானப் பயன்முறையில் புளூடூத்தை உங்கள் மொபைல் இயக்கத்திலேயே வைத்திருக்கும். புளூடூத்தை இயக்க விரும்பவில்லை என்றால் அதை முடக்கவும்.""வைஃபையும் புளூடூத்தும் இயக்கத்திலேயே இருத்தல்"
- "விமானப் பயன்முறையில் வைஃபையையும் புளூடூத்தையும் உங்கள் மொபைல் இயக்கத்திலேயே வைத்திருக்கும். இதை மாற்ற அவற்றை முடக்குங்கள்."
+ "விமானப் பயன்முறையில் வைஃபையையும் புளூடூத்தையும் உங்கள் மொபைல் இயக்கத்திலேயே வைத்திருக்கும். வைஃபை மற்றும் புளூடூத்தை இயக்க விரும்பவில்லை என்றால் அவற்றை முடக்கவும்."
diff --git a/android/app/res/values-te/strings.xml b/android/app/res/values-te/strings.xml
index b2a52128b510cb3037e75261d044314048b3e62c..207ee3efa7dae9235e060fb05d0f6cac917d4113 100644
--- a/android/app/res/values-te/strings.xml
+++ b/android/app/res/values-te/strings.xml
@@ -28,8 +28,8 @@
"బ్లూటూత్ సేవలను ఉపయోగించడానికి, మీరు తప్పనిసరిగా ముందుగా బ్లూటూత్ను ప్రారంభించాలి.""ఇప్పుడే బ్లూటూత్ను ప్రారంభించాలా?"
- "రద్దు చేయి"
- "ప్రారంభించు"
+ "రద్దు చేయండి"
+ "ప్రారంభించండి""ఫైల్ బదిలీ""ఇన్కమింగ్ ఫైల్ను ఆమోదించాలా?""తిరస్కరిస్తున్నాను"
@@ -48,14 +48,14 @@
"ఫైల్ బదిలీ""వీరి నుండి: \"%1$s\"""ఫైల్: %1$s"
- "ఫైల్ పరిమాణం: %1$s"
+ "ఫైల్ సైజ్: %1$s""ఫైల్ను స్వీకరిస్తోంది…""ఆపివేయి""దాచు""దీని నుండి""ఫైల్ పేరు"
- "పరిమాణం"
+ "సైజ్""ఫైల్ స్వీకరించబడలేదు""ఫైల్: %1$s""కారణం: %1$s"
@@ -72,7 +72,7 @@
"మూసివేయి""సరే""తెలియని ఫైల్"
- "ఈ రకమైన ఫైల్ను నిర్వహించడానికి యాప్ ఏదీ లేదు. \n"
+ "ఈ రకమైన ఫైల్ను మేనేజ్ చేయడానికి యాప్ ఏదీ లేదు. \n""ఫైల్ లేదు""ఫైల్ ఉనికిలో లేదు. \n""దయచేసి వేచి ఉండండి..."
@@ -116,8 +116,8 @@
"లిస్ట్ నుండి క్లియర్ చేయండి""క్లియర్ చేయండి""ప్రస్తుతం ప్లే అవుతున్నవి"
- "సేవ్ చేయి"
- "రద్దు చేయి"
+ "సేవ్ చేయండి"
+ "రద్దు చేయండి""మీరు బ్లూటూత్ ద్వారా షేర్ చేయాలనుకునే ఖాతాలను ఎంచుకోండి. మీరు ఇప్పటికీ కనెక్ట్ చేస్తున్నప్పుడు ఖాతాలకు అందించే ఏ యాక్సెస్నైనా ఆమోదించాల్సి ఉంటుంది.""మిగిలిన స్లాట్లు:""యాప్ చిహ్నం"
@@ -129,9 +129,9 @@
"4GB కన్నా పెద్ద ఫైళ్లు బదిలీ చేయబడవు""బ్లూటూత్కు కనెక్ట్ చేయి""విమానం మోడ్లో బ్లూటూత్ ఆన్ చేయబడింది"
- "మీరు బ్లూటూత్ని ఆన్లో ఉంచినట్లయితే, తదుపరిసారి మీరు విమానం మోడ్లో ఉన్నప్పుడు అది ఆన్లో ఉంటుంది"
+ "మీరు బ్లూటూత్ను ఆన్లో ఉంచినట్లయితే, మీరు తదుపరిసారి విమానం మోడ్లో ఉన్నప్పుడు దాన్ని ఆన్లో ఉంచాలని మీ ఫోన్ గుర్తుంచుకుంటుంది""బ్లూటూత్ ఆన్లో ఉంటుంది"
- "విమానం మోడ్లో బ్లూటూత్ ఆన్లో ఉంచాలని మీ ఫోన్ గుర్తుంచుకుంటుంది. దీన్ని మార్చడానికి, బ్లూటూత్ని ఆఫ్ చేయండి."
+ "విమానం మోడ్లో బ్లూటూత్ ఆన్లో ఉంచాలని మీ ఫోన్ గుర్తుంచుకుంటుంది. బ్లూటూత్ ఆన్లో ఉండకూడదనుకుంటే దాన్ని ఆఫ్ చేయండి.""Wi-Fi, బ్లూటూత్ ఆన్లో ఉంటాయి"
- "మీ ఫోన్ విమానం మోడ్లో Wi‑Fiని, బ్లూటూత్ని ఆన్లో ఉంచాలని గుర్తుంచుకుంటుంది. దీన్ని మార్చడానికి, వాటిని ఆఫ్ చేయండి."
+ "మీ ఫోన్ విమానం మోడ్లో Wi‑Fiని, బ్లూటూత్ని ఆన్లో ఉంచాలని గుర్తుంచుకుంటుంది. Wi-Fi, బ్లూటూత్ ఆన్లో ఉండకూడదనుకుంటే వాటిని ఆఫ్ చేయండి."
diff --git a/android/app/res/values-th/strings.xml b/android/app/res/values-th/strings.xml
index c9f98e26219d736b266c036070c8c363cee702c2..a39afb5f83d4e9807e50390ed5d33e3a957911c0 100644
--- a/android/app/res/values-th/strings.xml
+++ b/android/app/res/values-th/strings.xml
@@ -129,9 +129,9 @@
"โอนไฟล์ที่มีขนาดใหญ่กว่า 4 GB ไม่ได้""เชื่อมต่อบลูทูธ""บลูทูธเปิดอยู่ในโหมดบนเครื่องบิน"
- "หากคุณเปิดบลูทูธไว้ ระบบจะเปิดบลูทูธในครั้งถัดไปที่คุณอยู่ในโหมดบนเครื่องบิน"
+ "หากเปิดบลูทูธไว้ โทรศัพท์จะจำว่าต้องเปิดบลูทูธในครั้งถัดไปที่คุณอยู่ในโหมดบนเครื่องบิน""บลูทูธเปิดอยู่"
- "โทรศัพท์จำว่าจะต้องเปิดบลูทูธไว้ในโหมดบนเครื่องบิน หากต้องการเปลี่ยนการตั้งค่านี้ ให้ปิดบลูทูธ"
+ "โทรศัพท์จำว่าจะต้องเปิดบลูทูธไว้ในโหมดบนเครื่องบิน ปิดบลูทูธหากคุณไม่ต้องการให้เปิดไว้""Wi-Fi และบลูทูธยังเปิดอยู่"
- "โทรศัพท์จำว่าจะต้องเปิด Wi-Fi และบลูทูธไว้ในโหมดบนเครื่องบิน หากต้องการเปลี่ยนการตั้งค่านี้ ให้ปิด Wi-Fi และบลูทูธ"
+ "โทรศัพท์จำว่าจะต้องเปิด Wi-Fi และบลูทูธไว้ในโหมดบนเครื่องบิน ปิด Wi-Fi และบลูทูธหากคุณไม่ต้องการให้เปิดไว้"
diff --git a/android/app/res/values-tl/strings.xml b/android/app/res/values-tl/strings.xml
index b1ca6867079901d4ea23cb47aa77a140a556dd34..b84dc9aa4de169db9be5295d7f9967ad22338335 100644
--- a/android/app/res/values-tl/strings.xml
+++ b/android/app/res/values-tl/strings.xml
@@ -129,9 +129,9 @@
"Hindi maililipat ang mga file na mas malaki sa 4GB""Kumonekta sa Bluetooth""Naka-on ang Bluetooth sa airplane mode"
- "Kung papanatilihin mong naka-on ang Bluetooth, mananatili itong naka-on sa susunod na naka-airplane mode ka"
+ "Kung papanatilihin mong naka-on ang Bluetooth, tatandaan ng iyong telepono na panatilihin itong naka-on sa susunod na nasa airplane mode ka""Mananatiling naka-on ang Bluetooth"
- "Tinatandaan ng iyong telepono na panatilihing naka-on ang Bluetooth habang nasa airplane mode. Para baguhin ito, i-off ang Bluetooth."
+ "Tinatandaan ng iyong telepono na panatilihing naka-on ang Bluetooth habang nasa airplane mode. I-off ang Bluetooth kung ayaw mo itong manatiling naka-on.""Mananatiling naka-on ang Wi-Fi at Bluetooth"
- "Tinatandaan ng iyong telepono na panatilihing naka-on ang Wi-Fi at Bluetooth habang nasa airplane mode. Para baguhin ito, i-off ang mga iyon."
+ "Tinatandaan ng iyong telepono na panatilihing naka-on ang Wi-Fi at Bluetooth habang nasa airplane mode. I-off ang Wi-Fi at Bluetooth kung ayaw mong manatiling naka-on ang mga ito."
diff --git a/android/app/res/values-tr/strings.xml b/android/app/res/values-tr/strings.xml
index 6b7c82dbd970b3bce1c05f3f78cb2ab2d4294714..c3c65759d5b2407f3d3b80f1390c5f508e49fae4 100644
--- a/android/app/res/values-tr/strings.xml
+++ b/android/app/res/values-tr/strings.xml
@@ -129,9 +129,9 @@
"4 GB\'tan büyük dosyalar aktarılamaz""Bluetooth\'a bağlan""Uçak modundayken Bluetooth açık"
- "Bluetooth\'u açık tutarsanız daha sonra tekrar uçak modunda olduğunuzda açık kalmaya devam eder"
+ "Kablosuz bağlantıyı açık tutarsanız telefonunuz, daha sonra tekrar uçak modunda olduğunuzda kablosuz bağlantıyı açık tutar""Bluetooth açık kalır"
- "Telefonunuz, uçak modundayken Bluetooth\'u açık tutmayı hatırlar. Bunu değiştirmek için Bluetooth\'u kapatın."
+ "Telefonunuz, uçak modundayken Bluetooth\'u açık tutmayı hatırlar. Açık kalmasını istemiyorsanız Bluetooth\'u kapatın.""Kablosuz bağlantı ve Bluetooth açık kalır"
- "Telefonunuz, uçak modundayken kablosuz bağlantıyı ve Bluetooth\'u açık tutmayı hatırlar. Bunu değiştirmek için her ikisini de kapatın."
+ "Telefonunuz, uçak modundayken kablosuz bağlantıyı ve Bluetooth\'u açık tutmayı hatırlar. Açık kalmasını istemiyorsanız kablosuz bağlantıyı ve Bluetooth\'u kapatın."
diff --git a/android/app/res/values-uk/strings.xml b/android/app/res/values-uk/strings.xml
index 7efb443d3b0d66fc85f3c4c372b6dc8a323612e8..4290e6a9b42035c46cf9c769d9f7276ec28f7b71 100644
--- a/android/app/res/values-uk/strings.xml
+++ b/android/app/res/values-uk/strings.xml
@@ -129,9 +129,9 @@
"Не можна перенести файли, більші за 4 ГБ""Підключитися до Bluetooth""Bluetooth увімкнено в режимі польоту"
- "Якщо не вимикати Bluetooth, ця функція залишатиметься ввімкненою під час наступного використання режиму польоту"
+ "Якщо ви не вимкнете Bluetooth на телефоні, ця функція залишатиметься ввімкненою під час наступного використання режиму польоту""Bluetooth не буде вимкнено"
- "У режимі польоту функція Bluetooth на телефоні залишатиметься ввімкненою. Щоб змінити це, вимкніть Bluetooth."
+ "У режимі польоту функція Bluetooth на телефоні залишатиметься ввімкненою. За бажання її можна вимкнути.""Wi-Fi і Bluetooth залишаються ввімкненими"
- "У режимі польоту функції Wi-Fi і Bluetooth на телефоні залишатимуться ввімкненими. Щоб змінити це, вимкніть їх."
+ "У режимі польоту функції Wi-Fi і Bluetooth на телефоні залишатимуться ввімкненими. За бажання їх можна вимкнути."
diff --git a/android/app/res/values-ur/strings.xml b/android/app/res/values-ur/strings.xml
index eb69d0b00fe3db3037d20aabf04ed7ed6abb4553..fdbffdc055ae25e288ce410c72364ebdaefcf60b 100644
--- a/android/app/res/values-ur/strings.xml
+++ b/android/app/res/values-ur/strings.xml
@@ -129,9 +129,9 @@
"4GB سے بڑی فائلیں منتقل نہیں کی جا سکتیں""بلوٹوتھ سے منسلک کریں""ہوائی جہاز وضع میں بلوٹوتھ آن ہے"
- "اگر آپ بلوٹوتھ آن رکھتے ہیں تو آپ کے اگلی مرتبہ ہوائی جہاز وضع میں ہونے پر یہ آن ہی رہے گا"
+ "اگر آپ بلوٹوتھ کو آن رکھتے ہیں تو آپ کا فون آپ کے اگلی مرتبہ ہوائی جہاز وضع میں ہونے پر اسے آن رکھنا یاد رکھے گا""بلوٹوتھ آن رہتا ہے"
- "آپ کا فون ہوائی جہاز وضع میں بلوٹوتھ کو آن رکھنا یاد رکھتا ہے۔ اسے تبدیل کرنے کے لیے، بلوٹوتھ آف کریں۔"
+ "آپ کا فون ہوائی جہاز وضع میں بلوٹوتھ کو آن رکھنا یاد رکھتا ہے۔ اگر آپ نہیں چاہتے ہیں کہ بلوٹوتھ آن رہے تو اسے آف کریں۔""Wi-Fi اور بلوٹوتھ آن رہنے دیں"
- "آپ کا فون ہوائی جہاز وضع میں Wi-Fi اور بلوٹوتھ کو آن رکھنا یاد رکھتا ہے۔ اسے تبدیل کرنے کیلئے، انہیں آف کریں۔"
+ "آپ کا فون ہوائی جہاز وضع میں Wi-Fi اور بلوٹوتھ کو آن رکھنا یاد رکھتا ہے۔ اگر آپ نہیں چاہتے ہیں کہ Wi-Fi اور بلوٹوتھ آن رہیں تو انہیں آف کریں۔"
diff --git a/android/app/res/values-uz/strings.xml b/android/app/res/values-uz/strings.xml
index 1af9351b5aa7d6974250ecb6b23e3a2a3a3d462d..8b857fafcf157cde3e27bb88e649ca8e286a4eaa 100644
--- a/android/app/res/values-uz/strings.xml
+++ b/android/app/res/values-uz/strings.xml
@@ -129,9 +129,9 @@
"4 GBdan katta hajmli videolar o‘tkazilmaydi""Bluetoothga ulanish""Bluetooth parvoz rejimida yoniq"
- "Bluetooth aloqani yoniq qoldirsangiz, keyingi safar parvoz rejimini faollashtirganingizda yoniq qoladi"
+ "Bluetooth yoniq qolsa, telefon keyingi safar parvoz rejimida ham uni yoniq qoldiradi.""Bluetooth yoniq turadi"
- "Telefoningiz parvoz rejimida Bluetooth yoqilganini eslab qoladi. Buni oʻzgartirish uchun Bluetoothni faolsizlantiring."
+ "Telefoningiz parvoz rejimida Bluetooth yoqilganini eslab qoladi. Yoniq qolmasligi uchun Bluetooth aloqasini oʻchiring.""Wi-Fi va Bluetooth yoniq qoladi"
- "Telefoningiz parvoz rejimida Wi‑Fi va Bluetooth yoqilganini eslab qoladi. Buni oʻzgartirish uchun ularni faolsizlantiring."
+ "Telefoningiz parvoz rejimida Wi‑Fi va Bluetooth yoqilganini eslab qoladi. Yoniq qolmasligi uchun Wi-Fi va Bluetooth aloqasini oʻchiring."
diff --git a/android/app/res/values-vi/strings.xml b/android/app/res/values-vi/strings.xml
index 1c56e42a075eeee4148567666637ffe5bb1cf81d..f061077d9a4147c7f0f50990bbb389edb2181a26 100644
--- a/android/app/res/values-vi/strings.xml
+++ b/android/app/res/values-vi/strings.xml
@@ -129,9 +129,9 @@
"Không thể chuyển những tệp lớn hơn 4 GB""Kết nối với Bluetooth""Bluetooth đang bật ở chế độ trên máy bay"
- "Nếu bạn không tắt, Bluetooth sẽ vẫn bật ở lần tiếp theo bạn dùng chế độ trên máy bay"
+ "Nếu bạn không tắt Bluetooth, điện thoại sẽ luôn bật Bluetooth vào lần tiếp theo bạn dùng chế độ trên máy bay""Bluetooth luôn bật"
- "Điện thoại của bạn sẽ luôn bật Bluetooth ở chế độ trên máy bay. Để thay đổi chế độ này, hãy tắt Bluetooth."
+ "Điện thoại của bạn sẽ luôn bật Bluetooth ở chế độ trên máy bay. Nếu không muốn như vậy thì bạn có thể tắt Bluetooth.""Wi-Fi và Bluetooth vẫn đang bật"
- "Điện thoại của bạn sẽ luôn bật Wi-Fi và Bluetooth ở chế độ trên máy bay. Để thay đổi chế độ này, hãy tắt Wi-Fi và Bluetooth."
+ "Điện thoại của bạn sẽ luôn bật Wi-Fi và Bluetooth ở chế độ trên máy bay. Tắt Wi-Fi và Bluetooth nếu bạn không muốn tiếp tục bật."
diff --git a/android/app/res/values-zh-rCN/strings.xml b/android/app/res/values-zh-rCN/strings.xml
index e3ce417253cae17989c32d54ec71af7bf80a7bbc..0eda93813e4605197db8488e1f601e39ac435ffd 100644
--- a/android/app/res/values-zh-rCN/strings.xml
+++ b/android/app/res/values-zh-rCN/strings.xml
@@ -109,8 +109,8 @@
"所有内容都将从列表中清除。""蓝牙共享:已发送文件""蓝牙共享:已接收文件"
- "{count,plural, =1{# 个文件发送失败。}other{# 个文件发送失败。}}"
- "{count,plural, =1{# 个文件发送成功,%1$s}other{# 个文件发送成功,%1$s}}"
+ "{count,plural, =1{# 个文件传输失败。}other{# 个文件传输失败。}}"
+ "{count,plural, =1{# 个文件传输成功,%1$s}other{# 个文件传输成功,%1$s}}""清除列表""打开""从列表中清除"
@@ -129,9 +129,9 @@
"无法传输 4GB 以上的文件""连接到蓝牙""在飞行模式下蓝牙保持开启状态"
- "如果您不关闭蓝牙,那么您下次进入飞行模式时蓝牙将保持开启状态"
+ "如果您不关闭蓝牙,那么您下次进入飞行模式时手机将记住保持开启蓝牙""蓝牙保持开启状态"
- "在飞行模式下手机将记住保持蓝牙开启。如需更改此设置,请关闭蓝牙。"
+ "在飞行模式下手机将记住保持开启蓝牙。如果您不想保持开启和蓝牙,请关闭蓝牙。""WLAN 和蓝牙保持开启状态"
- "在飞行模式下手机将记住保持 WLAN 和蓝牙开启。如需更改此设置,请关闭相应功能。"
+ "在飞行模式下手机将记住保持开启 WLAN 和蓝牙。如果您不想保持开启 WLAN 和蓝牙,请关闭 WLAN 和蓝牙。"
diff --git a/android/app/res/values-zh-rHK/strings.xml b/android/app/res/values-zh-rHK/strings.xml
index 8a54f61d70f319a6252d59fadbcdb43d896b0878..cce5f3efd389caec2da743246b31775978a12334 100644
--- a/android/app/res/values-zh-rHK/strings.xml
+++ b/android/app/res/values-zh-rHK/strings.xml
@@ -129,9 +129,9 @@
"無法轉移 4 GB 以上的檔案""連接藍牙""在飛航模式中保持藍牙開啟"
- "如果您一直開啟藍牙,下次手機進入飛行模式時,藍牙將保持開啟"
+ "如果您不關閉藍牙,下次手機進入飛行模式時,藍牙將保持開啟""保持藍牙連線"
- "手機會記得在飛行模式下保持藍牙開啟。如要變更此設定,請關閉藍牙。"
+ "手機會記得在飛行模式下保持藍牙開啟。如果您不希望保持開啟,請關閉藍牙。""Wi-Fi 和藍牙保持開啟"
- "手機會記得在飛行模式下保持 Wi-Fi 及藍牙開啟。如要變更此設定,請關閉這些功能。"
+ "手機會記得在飛行模式下保持 Wi-Fi 及藍牙開啟。如果您不希望保持開啟,請關閉 Wi-Fi 及藍牙。"
diff --git a/android/app/res/values-zh-rTW/strings.xml b/android/app/res/values-zh-rTW/strings.xml
index 3e6c7e929c2fb42a3890d5459c8314014edd94b4..cbaadb23825d033a15fd5c2c94b49db8013f932d 100644
--- a/android/app/res/values-zh-rTW/strings.xml
+++ b/android/app/res/values-zh-rTW/strings.xml
@@ -129,9 +129,9 @@
"無法轉移大於 4GB 的檔案""使用藍牙連線""在飛航模式下保持藍牙開啟狀態"
- "如果你不關閉藍牙,下次手機進入飛航模式時,藍牙將保持開啟狀態"
+ "如果不關閉藍牙,下次手機進入飛航模式時,藍牙將保持開啟""藍牙會保持開啟狀態"
- "手機會記得在飛航模式下繼續開啟藍牙。如要變更此設定,請關閉藍牙。"
+ "手機會記得在飛航模式下保持藍牙開啟。如果不要保持開啟,請關閉藍牙。""Wi-Fi 和藍牙會保持開啟狀態"
- "手機會記得在飛航模式下繼續開啟 Wi-Fi 和藍牙。如要變更此設定,請關閉這些功能。"
+ "手機會記得在飛航模式下保持 Wi-Fi 和藍牙開啟。如果不要保持開啟,請關閉 Wi-Fi 和藍牙。"
diff --git a/android/app/res/values-zu/strings.xml b/android/app/res/values-zu/strings.xml
index 7cb25cde22668c3fc634de45bbfcfd25ac0283a8..c4c03cd67f0f8e603e6886b8b79a5fafc2042b20 100644
--- a/android/app/res/values-zu/strings.xml
+++ b/android/app/res/values-zu/strings.xml
@@ -129,9 +129,9 @@
"Amafayela amakhulu kuno-4GB awakwazi ukudluliselwa""Xhumeka ku-Bluetooth""I-Bluetooth ivuliwe kumodi yendiza"
- "Uma ugcina i-Bluetooth ivuliwe, izohlala ivuliwe esikhathini esizayo lapho ukumodi yendiza"
+ "Uma ugcina i-Wi‑Fi ivuliwe, ifoni yakho izokhumbula ukuyigcina ivuliwe ngesikhathi esilandelayo uma ukumodi yendiza.""I-Bluetooth ihlala ivuliwe"
- "Ifoni yakho ikhumbula ukugcina i-Bluetooth ivuliwe kumodi yendiza. Ukuze ushintshe lokhu, vala i-Bluetooth."
+ "Ifoni yakho ikhumbula ukugcina i-Bluetooth ivuliwe kumodi yendiza. Vala i-Bluetooth uma ungafuni ukuthi ihlale ivuliwe.""I-Wi-Fi ne-Bluetooth kuhlala kuvuliwe"
- "Ifoni yakho ikhumbula ukugcina i-Wi-Fi ne-Bluetooth kuvuliwe kumodi yendiza. Ukushintsha lokhu, kuvale."
+ "Ifoni yakho ikhumbula ukugcina i-Wi-Fi ne-Bluetooth kuvuliwe kumodi yendiza. Vala i-Wi-Fi ne-Bluetooth uma ungafuni ukuthi ihlale ivuliwe."
diff --git a/android/app/res/values/config.xml b/android/app/res/values/config.xml
index 5435b62205aa9be80f492419f3c109252895ae44..913c77f98ac5c35f4f807894415076765c2c42ac 100644
--- a/android/app/res/values/config.xml
+++ b/android/app/res/values/config.xml
@@ -62,11 +62,8 @@
false
-
- false
-
- false
+ truefalse
diff --git a/android/app/res/values/strings.xml b/android/app/res/values/strings.xml
index 2ca1d0e72541051d3b7dd8aa758c4cd57da726b7..46b9ad4d3a11c16ee73551fb34fbedb242668d5e 100644
--- a/android/app/res/values/strings.xml
+++ b/android/app/res/values/strings.xml
@@ -249,9 +249,9 @@
Files bigger than 4GB cannot be transferredConnect to BluetoothBluetooth on in airplane mode
- If you keep Bluetooth on, it will stay on the next time you\'re in airplane mode
+ If you keep Bluetooth on, your phone will remember to keep it on the next time you\'re in airplane modeBluetooth stays on
- Your phone remembers to keep Bluetooth on in airplane mode. To change this, turn off Bluetooth.
+ Your phone remembers to keep Bluetooth on in airplane mode. Turn off Bluetooth if you don\'t want it to stay on.Wi-Fi and Bluetooth stay on
- Your phone remembers to keep Wi-Fi and Bluetooth on in airplane mode. To change this, turn them off.
+ Your phone remembers to keep Wi-Fi and Bluetooth on in airplane mode. Turn off Wi-Fi and Bluetooth if you don\'t want them to stay on.
diff --git a/android/app/src/com/android/bluetooth/AlertActivity.java b/android/app/src/com/android/bluetooth/AlertActivity.java
index 858317350473aad4bb94e3c6630715d0fb1e1763..cb5e15eff3028342c8e76904b372ec42e69cf0d8 100644
--- a/android/app/src/com/android/bluetooth/AlertActivity.java
+++ b/android/app/src/com/android/bluetooth/AlertActivity.java
@@ -27,6 +27,9 @@ import android.os.Bundle;
import android.view.ViewGroup;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.Button;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* An activity that follows the visual style of an AlertDialog.
@@ -119,6 +122,11 @@ public abstract class AlertActivity extends Activity implements DialogInterface.
mAlert.getButton(identifier).setEnabled(enable);
}
+ @VisibleForTesting
+ public Button getButton(int identifier) {
+ return mAlert.getButton(identifier);
+ }
+
@Override
protected void onDestroy() {
if (mAlert != null) {
diff --git a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1ea7d4e952c4c01cd2cb635132505bdbaa387cd
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2022 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;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.PeriodicAdvertisingCallback;
+import android.bluetooth.le.PeriodicAdvertisingManager;
+import android.bluetooth.le.ScanResult;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.provider.Telephony;
+import android.util.Log;
+
+import com.android.bluetooth.gatt.AppAdvertiseStats;
+import com.android.bluetooth.gatt.ContextMap;
+import com.android.bluetooth.gatt.GattService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.obex.HeaderSet;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+
+/**
+ * Proxy class for method calls to help with unit testing
+ */
+public class BluetoothMethodProxy {
+ private static final String TAG = BluetoothMethodProxy.class.getSimpleName();
+ private static final Object INSTANCE_LOCK = new Object();
+ private static BluetoothMethodProxy sInstance;
+
+ private BluetoothMethodProxy() {
+ }
+
+ /**
+ * Get the singleton instance of proxy
+ *
+ * @return the singleton instance, guaranteed not null
+ */
+ public static BluetoothMethodProxy getInstance() {
+ synchronized (INSTANCE_LOCK) {
+ if (sInstance == null) {
+ sInstance = new BluetoothMethodProxy();
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Allow unit tests to substitute BluetoothPbapMethodCallProxy with a test instance
+ *
+ * @param proxy a test instance of the BluetoothPbapMethodCallProxy
+ */
+ @VisibleForTesting
+ public static void setInstanceForTesting(BluetoothMethodProxy proxy) {
+ Utils.enforceInstrumentationTestMode();
+ synchronized (INSTANCE_LOCK) {
+ Log.d(TAG, "setInstanceForTesting(), set to " + proxy);
+ sInstance = proxy;
+ }
+ }
+
+ /**
+ * Proxies {@link ContentResolver#query(Uri, String[], String, String[], String)}.
+ */
+ public Cursor contentResolverQuery(ContentResolver contentResolver, final Uri contentUri,
+ final String[] projection, final String selection, final String[] selectionArgs,
+ final String sortOrder) {
+ return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+ */
+ public Cursor contentResolverQuery(ContentResolver contentResolver, final Uri contentUri,
+ final String[] projection, final Bundle queryArgs,
+ final CancellationSignal cancellationSignal) {
+ return contentResolver.query(contentUri, projection, queryArgs, cancellationSignal);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#insert(Uri, ContentValues)}.
+ */
+ public Uri contentResolverInsert(ContentResolver contentResolver, final Uri contentUri,
+ final ContentValues contentValues) {
+ return contentResolver.insert(contentUri, contentValues);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#update(Uri, ContentValues, String, String[])}.
+ */
+ public int contentResolverUpdate(ContentResolver contentResolver, final Uri contentUri,
+ final ContentValues contentValues, String where, String[] selectionArgs) {
+ return contentResolver.update(contentUri, contentValues, where, selectionArgs);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#delete(Uri, String, String[])}.
+ */
+ public int contentResolverDelete(ContentResolver contentResolver, final Uri url,
+ final String where,
+ final String[] selectionArgs) {
+ return contentResolver.delete(url, where, selectionArgs);
+ }
+
+ /**
+ * Proxies {@link BluetoothAdapter#isEnabled()}.
+ */
+ public boolean bluetoothAdapterIsEnabled(BluetoothAdapter adapter) {
+ return adapter.isEnabled();
+ }
+
+ /**
+ * Proxies {@link ContentResolver#openFileDescriptor(Uri, String)}.
+ */
+ public ParcelFileDescriptor contentResolverOpenFileDescriptor(ContentResolver contentResolver,
+ final Uri uri, final String mode) throws FileNotFoundException {
+ return contentResolver.openFileDescriptor(uri, mode);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#openAssetFileDescriptor(Uri, String)}.
+ */
+ public AssetFileDescriptor contentResolverOpenAssetFileDescriptor(
+ ContentResolver contentResolver, final Uri uri, final String mode)
+ throws FileNotFoundException {
+ return contentResolver.openAssetFileDescriptor(uri, mode);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#openInputStream(Uri)}.
+ */
+ public InputStream contentResolverOpenInputStream(ContentResolver contentResolver,
+ final Uri uri) throws FileNotFoundException {
+ return contentResolver.openInputStream(uri);
+ }
+
+ /**
+ * Proxies {@link ContentResolver#acquireUnstableContentProviderClient(String)}.
+ */
+ public ContentProviderClient contentResolverAcquireUnstableContentProviderClient(
+ ContentResolver contentResolver, @NonNull String name) {
+ return contentResolver.acquireUnstableContentProviderClient(name);
+ }
+
+ /**
+ * Proxies {@link Context#sendBroadcast(Intent)}.
+ */
+ public void contextSendBroadcast(Context context, @RequiresPermission Intent intent) {
+ context.sendBroadcast(intent);
+ }
+
+ /**
+ * Proxies {@link HeaderSet#getHeader}.
+ */
+ public Object getHeader(HeaderSet headerSet, int headerId) throws IOException {
+ return headerSet.getHeader(headerId);
+ }
+
+ /**
+ * Proxies {@link Context#getSystemService(Class)}.
+ */
+ public T getSystemService(Context context, Class serviceClass) {
+ return context.getSystemService(serviceClass);
+ }
+
+ /**
+ * Proxies {@link Telephony.Threads#getOrCreateThreadId(Context, Set )}.
+ */
+ public long telephonyGetOrCreateThreadId(Context context, Set recipients) {
+ return Telephony.Threads.getOrCreateThreadId(context, recipients);
+ }
+
+ /**
+ * Proxies {@link PeriodicAdvertisingManager#registerSync(ScanResult, int, int,
+ * PeriodicAdvertisingCallback, Handler)}.
+ */
+ public void periodicAdvertisingManagerRegisterSync(PeriodicAdvertisingManager manager,
+ ScanResult scanResult, int skip, int timeout,
+ PeriodicAdvertisingCallback callback, Handler handler) {
+ manager.registerSync(scanResult, skip, timeout, callback, handler);
+ }
+
+ /**
+ * Proxies {@link PeriodicAdvertisingManager#transferSync}.
+ */
+ public void periodicAdvertisingManagerTransferSync(PeriodicAdvertisingManager manager,
+ BluetoothDevice bda, int serviceData, int syncHandle) {
+ manager.transferSync(bda, serviceData, syncHandle);
+ }
+
+ /**
+ * Proxies {@link PeriodicAdvertisingManager#transferSetInfo}.
+ */
+ public void periodicAdvertisingManagerTransferSetInfo(
+ PeriodicAdvertisingManager manager, BluetoothDevice bda, int serviceData,
+ int advHandle, PeriodicAdvertisingCallback callback) {
+ manager.transferSetInfo(bda, serviceData, advHandle, callback);
+ }
+
+ /**
+ * Proxies {@link AppAdvertiseStats}.
+ */
+ public AppAdvertiseStats createAppAdvertiseStats(int appUid, int id, String name,
+ ContextMap map, GattService service) {
+ return new AppAdvertiseStats(appUid, id, name, map, service);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/mapclient/obex/ObexAppParameters.java b/android/app/src/com/android/bluetooth/ObexAppParameters.java
similarity index 99%
rename from android/app/src/com/android/bluetooth/mapclient/obex/ObexAppParameters.java
rename to android/app/src/com/android/bluetooth/ObexAppParameters.java
index 28e8b1c8f58fda06d4e5e47104ef878acc6ec792..80a3645cda603490c4967c91d73c6e0b9d0e05f6 100644
--- a/android/app/src/com/android/bluetooth/mapclient/obex/ObexAppParameters.java
+++ b/android/app/src/com/android/bluetooth/ObexAppParameters.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.bluetooth.mapclient;
+package com.android.bluetooth;
import com.android.obex.HeaderSet;
diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java
index abe63bafe5944cd16c0c45d0f682460676ee9d3a..dc2c79aac529fd1eb6d4c98fcb4e77df70ba44ec 100644
--- a/android/app/src/com/android/bluetooth/Utils.java
+++ b/android/app/src/com/android/bluetooth/Utils.java
@@ -373,9 +373,12 @@ public final class Utils {
*/
public static boolean isPackageNameAccurate(Context context, String callingPackage,
int callingUid) {
+ UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
+
// Verifies the integrity of the calling package name
try {
- int packageUid = context.getPackageManager().getPackageUid(callingPackage, 0);
+ int packageUid = context.createContextAsUser(callingUser, 0)
+ .getPackageManager().getPackageUid(callingPackage, 0);
if (packageUid != callingUid) {
Log.e(TAG, "isPackageNameAccurate: App with package name " + callingPackage
+ " is UID " + packageUid + " but caller is " + callingUid);
@@ -424,8 +427,6 @@ public final class Utils {
"Need DUMP permission");
}
- /**
- */
public static AttributionSource getCallingAttributionSource(Context context) {
int callingUid = Binder.getCallingUid();
if (callingUid == android.os.Process.ROOT_UID) {
@@ -460,6 +461,9 @@ public final class Utils {
@SuppressLint("AndroidFrameworkRequiresPermission")
private static boolean checkPermissionForDataDelivery(Context context, String permission,
AttributionSource attributionSource, String message) {
+ if (isInstrumentationTestMode()) {
+ return true;
+ }
// STOPSHIP(b/188391719): enable this security enforcement
// attributionSource.enforceCallingUid();
AttributionSource currentAttribution = new AttributionSource
@@ -617,7 +621,7 @@ public final class Utils {
return false;
}
- public static boolean checkCallerIsSystemOrActiveUser() {
+ private static boolean checkCallerIsSystemOrActiveUser() {
int callingUid = Binder.getCallingUid();
UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
@@ -638,7 +642,7 @@ public final class Utils {
return checkCallerIsSystemOrActiveUser(tag + "." + method + "()");
}
- public static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context) {
+ private static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context) {
if (context == null) {
return checkCallerIsSystemOrActiveUser();
}
@@ -666,6 +670,9 @@ public final class Utils {
}
public static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context, String tag) {
+ if (isInstrumentationTestMode()) {
+ return true;
+ }
final boolean res = checkCallerIsSystemOrActiveOrManagedUser(context);
if (!res) {
Log.w(TAG, tag + " - Not allowed for"
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 313f64cf106f55d841b60cafb4a246af51e0012b..9bc4fad2b933c47038befb0fb24d707cf683697c 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -1292,7 +1292,7 @@ public class A2dpService extends ProfileService {
/**
* Retrieves the most recently connected device in the A2DP connected devices list.
*/
- private BluetoothDevice getFallbackDevice() {
+ public BluetoothDevice getFallbackDevice() {
DatabaseManager dbManager = mAdapterService.getDatabase();
return dbManager != null ? dbManager
.getMostRecentlyConnectedDevicesInList(getConnectedDevices())
@@ -1309,8 +1309,11 @@ public class A2dpService extends ProfileService {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private A2dpService getService(AttributionSource source) {
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
return null;
}
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 52cbd2176630b4d06509739e08b858efb47f3598..5c945d78064b2052b169802381704cfd38f48a61 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -190,14 +190,18 @@ public class A2dpSinkService extends ProfileService {
}
//Binder object: Must be static class or memory leak may occur
- private static class A2dpSinkServiceBinder extends IBluetoothA2dpSink.Stub
+ @VisibleForTesting
+ static class A2dpSinkServiceBinder extends IBluetoothA2dpSink.Stub
implements IProfileServiceBinder {
private A2dpSinkService mService;
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private A2dpSinkService getService(AttributionSource source) {
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
return null;
}
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index c440d863d5044232bce32b08d050c003acd5cfe7..47f325c839559103d1dec38a0266a453007d23ca 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -509,11 +509,8 @@ public class AvrcpTargetService extends ProfileService {
@Override
public void sendVolumeChanged(int volume) {
- if (!Utils.callerIsSystemOrActiveUser(TAG, "sendVolumeChanged")) {
- return;
- }
-
- if (mService == null) {
+ if (mService == null
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)) {
return;
}
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java
index 4f943bbca12e57ad7f4006ad4ecb350621ace4bc..3f46b4419e411b7d73dbd1c7adf4e0a5be91ed72 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java
@@ -26,6 +26,7 @@ import android.os.Message;
import android.util.Log;
import com.android.bluetooth.BluetoothObexTransport;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.obex.ClientSession;
import com.android.obex.HeaderSet;
import com.android.obex.ResponseCodes;
@@ -229,7 +230,8 @@ public class AvrcpBipClient {
/**
* Update our client's connection state and notify of the new status
*/
- private void setConnectionState(int state) {
+ @VisibleForTesting
+ void setConnectionState(int state) {
int oldState = -1;
synchronized (this) {
oldState = mState;
@@ -428,7 +430,8 @@ public class AvrcpBipClient {
}
}
- private String getStateName() {
+ @VisibleForTesting
+ String getStateName() {
int state = getState();
switch (state) {
case BluetoothProfile.STATE_DISCONNECTED:
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 0a3ed2c118ef5faee2aea15fadfa84bfb15d6767..91efbe61ef077e86b242659f74a17e641d2a62b0 100755
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -35,6 +35,7 @@ import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;
import java.util.ArrayList;
@@ -67,7 +68,8 @@ public class AvrcpControllerService extends ProfileService {
private static final byte JNI_PLAY_STATUS_PLAYING = 0x01;
private static final byte JNI_PLAY_STATUS_PAUSED = 0x02;
private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03;
- private static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04;
+ @VisibleForTesting
+ static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04;
private static final byte JNI_PLAY_STATUS_ERROR = -1;
/* Folder/Media Item scopes.
@@ -173,6 +175,7 @@ public class AvrcpControllerService extends ProfileService {
for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) {
stateMachine.quitNow();
}
+ mDeviceStateMap.clear();
sService = null;
sBrowseTree = null;
@@ -201,7 +204,8 @@ public class AvrcpControllerService extends ProfileService {
/**
* Set the current active device, notify devices of activity status
*/
- private boolean setActiveDevice(BluetoothDevice device) {
+ @VisibleForTesting
+ boolean setActiveDevice(BluetoothDevice device) {
A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
if (a2dpSinkService == null) {
return false;
@@ -277,7 +281,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private void refreshContents(BrowseTree.BrowseNode node) {
+ @VisibleForTesting
+ void refreshContents(BrowseTree.BrowseNode node) {
BluetoothDevice device = node.getDevice();
if (device == null) {
return;
@@ -359,14 +364,18 @@ public class AvrcpControllerService extends ProfileService {
}
//Binder object: Must be static class or memory leak may occur
- private static class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub
+ @VisibleForTesting
+ static class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub
implements IProfileServiceBinder {
private AvrcpControllerService mService;
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private AvrcpControllerService getService(AttributionSource source) {
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
return null;
}
@@ -467,14 +476,16 @@ public class AvrcpControllerService extends ProfileService {
/* JNI API*/
// Called by JNI when a passthrough key was received.
- private void handlePassthroughRsp(int id, int keyState, byte[] address) {
+ @VisibleForTesting
+ void handlePassthroughRsp(int id, int keyState, byte[] address) {
if (DBG) {
Log.d(TAG, "passthrough response received as: key: " + id
+ " state: " + keyState + "address:" + Arrays.toString(address));
}
}
- private void handleGroupNavigationRsp(int id, int keyState) {
+ @VisibleForTesting
+ void handleGroupNavigationRsp(int id, int keyState) {
if (DBG) {
Log.d(TAG, "group navigation response received as: key: " + id + " state: "
+ keyState);
@@ -482,17 +493,14 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI when a device has connected or disconnected.
- private synchronized void onConnectionStateChanged(boolean remoteControlConnected,
+ @VisibleForTesting
+ synchronized void onConnectionStateChanged(boolean remoteControlConnected,
boolean browsingConnected, byte[] address) {
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
if (DBG) {
Log.d(TAG, "onConnectionStateChanged " + remoteControlConnected + " "
+ browsingConnected + device);
}
- if (device == null) {
- Log.e(TAG, "onConnectionStateChanged Device is null");
- return;
- }
StackEvent event =
StackEvent.connectionStateChanged(remoteControlConnected, browsingConnected);
@@ -512,12 +520,14 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI to notify Avrcp of features supported by the Remote device.
- private void getRcFeatures(byte[] address, int features) {
+ @VisibleForTesting
+ void getRcFeatures(byte[] address, int features) {
/* Do Nothing. */
}
// Called by JNI to notify Avrcp of a remote device's Cover Art PSM
- private void getRcPsm(byte[] address, int psm) {
+ @VisibleForTesting
+ void getRcPsm(byte[] address, int psm) {
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
if (DBG) Log.d(TAG, "getRcPsm(device=" + device + ", psm=" + psm + ")");
AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device);
@@ -528,12 +538,14 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI
- private void setPlayerAppSettingRsp(byte[] address, byte accepted) {
+ @VisibleForTesting
+ void setPlayerAppSettingRsp(byte[] address, byte accepted) {
/* Do Nothing. */
}
// Called by JNI when remote wants to receive absolute volume notifications.
- private synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) {
+ @VisibleForTesting
+ synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) {
if (DBG) {
Log.d(TAG, "handleRegisterNotificationAbsVol");
}
@@ -546,7 +558,8 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI when remote wants to set absolute volume.
- private synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) {
+ @VisibleForTesting
+ synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) {
if (DBG) {
Log.d(TAG, "handleSetAbsVolume ");
}
@@ -559,7 +572,8 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI when a track changes and local AvrcpController is registered for updates.
- private synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes,
+ @VisibleForTesting
+ synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes,
String[] attribVals) {
if (DBG) {
Log.d(TAG, "onTrackChanged");
@@ -586,7 +600,8 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI periodically based upon timer to update play position
- private synchronized void onPlayPositionChanged(byte[] address, int songLen,
+ @VisibleForTesting
+ synchronized void onPlayPositionChanged(byte[] address, int songLen,
int currSongPosition) {
if (DBG) {
Log.d(TAG, "onPlayPositionChanged pos " + currSongPosition);
@@ -601,7 +616,8 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI on changes of play status
- private synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {
+ @VisibleForTesting
+ synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {
if (DBG) {
Log.d(TAG, "onPlayStatusChanged " + playStatus);
}
@@ -615,7 +631,8 @@ public class AvrcpControllerService extends ProfileService {
}
// Called by JNI to report remote Player's capabilities
- private synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp,
+ @VisibleForTesting
+ synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp,
int rspLen) {
if (DBG) {
Log.d(TAG, "handlePlayerAppSetting rspLen = " + rspLen);
@@ -631,7 +648,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
+ @VisibleForTesting
+ synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
int rspLen) {
if (DBG) {
Log.d(TAG, "onPlayerAppSettingChanged ");
@@ -648,7 +666,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private void onAvailablePlayerChanged(byte[] address) {
+ @VisibleForTesting
+ void onAvailablePlayerChanged(byte[] address) {
if (DBG) {
Log.d(TAG," onAvailablePlayerChanged");
}
@@ -766,7 +785,8 @@ public class AvrcpControllerService extends ProfileService {
return apb.build();
}
- private void handleChangeFolderRsp(byte[] address, int count) {
+ @VisibleForTesting
+ void handleChangeFolderRsp(byte[] address, int count) {
if (DBG) {
Log.d(TAG, "handleChangeFolderRsp count: " + count);
}
@@ -778,7 +798,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) {
+ @VisibleForTesting
+ void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) {
if (DBG) {
Log.d(TAG, "handleSetBrowsedPlayerRsp depth: " + depth);
}
@@ -791,7 +812,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private void handleSetAddressedPlayerRsp(byte[] address, int status) {
+ @VisibleForTesting
+ void handleSetAddressedPlayerRsp(byte[] address, int status) {
if (DBG) {
Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status);
}
@@ -804,7 +826,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private void handleAddressedPlayerChanged(byte[] address, int id) {
+ @VisibleForTesting
+ void handleAddressedPlayerChanged(byte[] address, int id) {
if (DBG) {
Log.d(TAG, "handleAddressedPlayerChanged id: " + id);
}
@@ -817,7 +840,8 @@ public class AvrcpControllerService extends ProfileService {
}
}
- private void handleNowPlayingContentChanged(byte[] address) {
+ @VisibleForTesting
+ void handleNowPlayingContentChanged(byte[] address) {
if (DBG) {
Log.d(TAG, "handleNowPlayingContentChanged");
}
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java b/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
index 508c68cb91e433e32d3a86bebe74cc6ec1f9033c..ecfd441a6059040b7b76720c1243a55698d66272 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
@@ -23,6 +23,8 @@ import android.util.Log;
import com.android.bluetooth.Utils;
+import com.google.common.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -57,7 +59,8 @@ public class BrowseTree {
public static final String PLAYER_PREFIX = "PLAYER";
// Static instance of Folder ID <-> Folder Instance (for navigation purposes)
- private final HashMap mBrowseMap = new HashMap();
+ @VisibleForTesting
+ final HashMap mBrowseMap = new HashMap();
private BrowseNode mCurrentBrowseNode;
private BrowseNode mCurrentBrowsedPlayer;
private BrowseNode mCurrentAddressedPlayer;
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java b/android/app/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
index 362548e5fac1c0c3b56477c27ffb35ab11c51a50..90446b3ea279409914bef19e845a781390c59c97 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
@@ -20,6 +20,8 @@ import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
/*
@@ -39,20 +41,28 @@ class PlayerApplicationSettings {
private static final byte JNI_EQUALIZER_STATUS_OFF = 0x01;
private static final byte JNI_EQUALIZER_STATUS_ON = 0x02;
- private static final byte JNI_REPEAT_STATUS_OFF = 0x01;
- private static final byte JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT = 0x02;
- private static final byte JNI_REPEAT_STATUS_ALL_TRACK_REPEAT = 0x03;
- private static final byte JNI_REPEAT_STATUS_GROUP_REPEAT = 0x04;
-
- private static final byte JNI_SHUFFLE_STATUS_OFF = 0x01;
- private static final byte JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE = 0x02;
- private static final byte JNI_SHUFFLE_STATUS_GROUP_SHUFFLE = 0x03;
+ @VisibleForTesting
+ static final byte JNI_REPEAT_STATUS_OFF = 0x01;
+ @VisibleForTesting
+ static final byte JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT = 0x02;
+ @VisibleForTesting
+ static final byte JNI_REPEAT_STATUS_ALL_TRACK_REPEAT = 0x03;
+ @VisibleForTesting
+ static final byte JNI_REPEAT_STATUS_GROUP_REPEAT = 0x04;
+
+ @VisibleForTesting
+ static final byte JNI_SHUFFLE_STATUS_OFF = 0x01;
+ @VisibleForTesting
+ static final byte JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE = 0x02;
+ @VisibleForTesting
+ static final byte JNI_SHUFFLE_STATUS_GROUP_SHUFFLE = 0x03;
private static final byte JNI_SCAN_STATUS_OFF = 0x01;
private static final byte JNI_SCAN_STATUS_ALL_TRACK_SCAN = 0x02;
private static final byte JNI_SCAN_STATUS_GROUP_SCAN = 0x03;
- private static final byte JNI_STATUS_INVALID = -1;
+ @VisibleForTesting
+ static final byte JNI_STATUS_INVALID = -1;
/*
* Hash map of current settings.
@@ -123,7 +133,8 @@ class PlayerApplicationSettings {
}
// Convert a native Attribute Id/Value pair into the AVRCP equivalent value.
- private static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) {
+ @VisibleForTesting
+ static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) {
if (attribId == REPEAT_STATUS) {
switch (attribVal) {
case JNI_REPEAT_STATUS_ALL_TRACK_REPEAT:
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/bip/RequestGetImage.java b/android/app/src/com/android/bluetooth/avrcpcontroller/bip/RequestGetImage.java
index d6fcabf82ab2e79323f55393c29f3c171441efb4..fc5f49960b494d405d5ea3432ecafdfba814c711 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/bip/RequestGetImage.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/bip/RequestGetImage.java
@@ -16,6 +16,7 @@
package com.android.bluetooth.avrcpcontroller;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.obex.ClientSession;
import com.android.obex.HeaderSet;
@@ -29,7 +30,8 @@ import java.io.InputStream;
public class RequestGetImage extends BipRequest {
// Expected inputs
private final String mImageHandle;
- private final BipImageDescriptor mImageDescriptor;
+ @VisibleForTesting
+ final BipImageDescriptor mImageDescriptor;
// Expected return type
private static final String TYPE = "x-bt/img-img";
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryService.java b/android/app/src/com/android/bluetooth/bas/BatteryService.java
index 43b4e766ffadc8badf7dc718017f51fc5caae6d0..71a5e645e3d49ca06c80a7ec4a69397288d269f8 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryService.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryService.java
@@ -214,6 +214,7 @@ public class BatteryService extends ProfileService {
BatteryStateMachine sm = getOrCreateStateMachine(device);
if (sm == null) {
Log.e(TAG, "Cannot connect to " + device + " : no state machine");
+ return false;
}
sm.sendMessage(BatteryStateMachine.CONNECT);
}
@@ -527,9 +528,12 @@ public class BatteryService extends ProfileService {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private BatteryService getService(AttributionSource source) {
BatteryService service = mServiceRef.get();
+ if (Utils.isInstrumentationTestMode()) {
+ return service;
+ }
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(service, TAG)
+ if (!Utils.checkServiceAvailable(service, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return null;
}
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
index 878c71ee5a882168771201ce98863b1b1246193e..394f4172222d58878b0ff3e86a8e866c32df84c5 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
@@ -18,7 +18,7 @@ package com.android.bluetooth.bas;
import static android.bluetooth.BluetoothDevice.PHY_LE_1M_MASK;
import static android.bluetooth.BluetoothDevice.PHY_LE_2M_MASK;
-import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO;
+import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
@@ -233,7 +233,7 @@ public class BatteryStateMachine extends StateMachine {
mBluetoothGatt.close();
}
mBluetoothGatt = mDevice.connectGatt(service, /*autoConnect=*/false,
- mGattCallback, TRANSPORT_AUTO, /*opportunistic=*/true,
+ mGattCallback, TRANSPORT_LE, /*opportunistic=*/true,
PHY_LE_1M_MASK | PHY_LE_2M_MASK, getHandler());
return mBluetoothGatt != null;
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index 9ba993aff5b40ac6e58ef2e38e2a393737dc39b1..53c5c3466b763e2f418daaae5fa572da5c4d5b7f 100755
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -649,6 +649,8 @@ public class BassClientService extends ProfileService {
return;
}
if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+ Log.i(TAG, "Disconnecting device because it was unbonded.");
+ disconnect(device);
return;
}
removeStateMachine(device);
@@ -1492,11 +1494,14 @@ public class BassClientService extends ProfileService {
@VisibleForTesting
static class BluetoothLeBroadcastAssistantBinder extends IBluetoothLeBroadcastAssistant.Stub
implements IProfileServiceBinder {
- private BassClientService mService;
+ BassClientService mService;
private BassClientService getService() {
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)) {
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)) {
return null;
}
return mService;
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
index bc14f6d3c779c51dc7c92ba3b786efa78fbbe57c..f8e8388f5c774727a863507c6356164a1400e74f 100755
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
@@ -14,50 +14,11 @@
* limitations under the License.
*/
-/**
- * Bluetooth Bassclient StateMachine. There is one instance per remote device.
- * - "Disconnected" and "Connected" are steady states.
- * - "Connecting" and "Disconnecting" are transient states until the
- * connection / disconnection is completed.
- * - "ConnectedProcessing" is an intermediate state to ensure, there is only
- * one Gatt transaction from the profile at any point of time
- *
- *
- * (Disconnected)
- * | ^
- * CONNECT | | DISCONNECTED
- * V |
- * (Connecting)<--->(Disconnecting)
- * | ^
- * CONNECTED | | DISCONNECT
- * V |
- * (Connected)
- * | ^
- * GATT_TXN | | GATT_TXN_DONE/GATT_TXN_TIMEOUT
- * V |
- * (ConnectedProcessing)
- * NOTES:
- * - If state machine is in "Connecting" state and the remote device sends
- * DISCONNECT request, the state machine transitions to "Disconnecting" state.
- * - Similarly, if the state machine is in "Disconnecting" state and the remote device
- * sends CONNECT request, the state machine transitions to "Connecting" state.
- * - Whenever there is any Gatt Write/read, State machine will moved "ConnectedProcessing" and
- * all other requests (add, update, remove source) operations will be deferred in
- * "ConnectedProcessing" state
- * - Once the gatt transaction is done (or after a specified timeout of no response),
- * State machine will move back to "Connected" and try to process the deferred requests
- * as needed
- *
- * DISCONNECT
- * (Connecting) ---------------> (Disconnecting)
- * <---------------
- * CONNECT
- *
- */
package com.android.bluetooth.bass_client;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
@@ -87,6 +48,7 @@ import android.os.ParcelUuid;
import android.provider.DeviceConfig;
import android.util.Log;
+import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
@@ -106,13 +68,16 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.UUID;
import java.util.stream.IntStream;
@VisibleForTesting
public class BassClientStateMachine extends StateMachine {
private static final String TAG = "BassClientStateMachine";
- private static final byte[] REMOTE_SCAN_STOP = {00};
- private static final byte[] REMOTE_SCAN_START = {01};
+ @VisibleForTesting
+ static final byte[] REMOTE_SCAN_STOP = {00};
+ @VisibleForTesting
+ static final byte[] REMOTE_SCAN_START = {01};
private static final byte OPCODE_ADD_SOURCE = 0x02;
private static final byte OPCODE_UPDATE_SOURCE = 0x03;
private static final byte OPCODE_SET_BCAST_PIN = 0x04;
@@ -148,46 +113,56 @@ public class BassClientStateMachine extends StateMachine {
private final Disconnected mDisconnected = new Disconnected();
private final Connected mConnected = new Connected();
private final Connecting mConnecting = new Connecting();
- private final Disconnecting mDisconnecting = new Disconnecting();
private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing();
- private final List mBroadcastCharacteristics =
+ @VisibleForTesting
+ final List mBroadcastCharacteristics =
new ArrayList();
@VisibleForTesting
BluetoothDevice mDevice;
private boolean mIsAllowedList = false;
private int mLastConnectionState = -1;
- private boolean mMTUChangeRequested = false;
- private boolean mDiscoveryInitiated = false;
+ @VisibleForTesting
+ boolean mMTUChangeRequested = false;
+ @VisibleForTesting
+ boolean mDiscoveryInitiated = false;
@VisibleForTesting
BassClientService mService;
-
- private BluetoothGattCharacteristic mBroadcastScanControlPoint;
+ @VisibleForTesting
+ BluetoothGattCharacteristic mBroadcastScanControlPoint;
private boolean mFirstTimeBisDiscovery = false;
private int mPASyncRetryCounter = 0;
private ScanResult mScanRes = null;
- private int mNumOfBroadcastReceiverStates = 0;
+ @VisibleForTesting
+ int mNumOfBroadcastReceiverStates = 0;
private BluetoothAdapter mBluetoothAdapter =
BluetoothAdapter.getDefaultAdapter();
private ServiceFactory mFactory = new ServiceFactory();
- private int mPendingOperation = -1;
- private byte mPendingSourceId = -1;
- private BluetoothLeBroadcastMetadata mPendingMetadata = null;
+ @VisibleForTesting
+ int mPendingOperation = -1;
+ @VisibleForTesting
+ byte mPendingSourceId = -1;
+ @VisibleForTesting
+ BluetoothLeBroadcastMetadata mPendingMetadata = null;
private BluetoothLeBroadcastReceiveState mSetBroadcastPINRcvState = null;
- private boolean mSetBroadcastCodePending = false;
+ @VisibleForTesting
+ boolean mSetBroadcastCodePending = false;
private final Map mPendingRemove = new HashMap();
// Psync and PAST interfaces
private PeriodicAdvertisingManager mPeriodicAdvManager;
private boolean mAutoAssist = false;
- private boolean mAutoTriggered = false;
- private boolean mNoStopScanOffload = false;
+ @VisibleForTesting
+ boolean mAutoTriggered = false;
+ @VisibleForTesting
+ boolean mNoStopScanOffload = false;
private boolean mDefNoPAS = false;
private boolean mForceSB = false;
private int mBroadcastSourceIdLength = 3;
- private byte mNextSourceId = 0;
+ @VisibleForTesting
+ byte mNextSourceId = 0;
private boolean mAllowReconnect = false;
-
- BluetoothGatt mBluetoothGatt = null;
+ @VisibleForTesting
+ BluetoothGattTestableWrapper mBluetoothGatt = null;
BluetoothGattCallback mGattCallback = null;
BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper,
@@ -197,7 +172,6 @@ public class BassClientStateMachine extends StateMachine {
mService = svc;
mConnectTimeoutMs = connectTimeoutMs;
addState(mDisconnected);
- addState(mDisconnecting);
addState(mConnected);
addState(mConnecting);
addState(mConnectedProcessing);
@@ -383,8 +357,9 @@ public class BassClientStateMachine extends StateMachine {
mNoStopScanOffload = true;
cancelActiveSync(null);
try {
- mPeriodicAdvManager.registerSync(scanRes, 0,
- BassConstants.PSYNC_TIMEOUT, mPeriodicAdvCallback);
+ BluetoothMethodProxy.getInstance().periodicAdvertisingManagerRegisterSync(
+ mPeriodicAdvManager, scanRes, 0, BassConstants.PSYNC_TIMEOUT,
+ mPeriodicAdvCallback, null);
} catch (IllegalArgumentException ex) {
Log.w(TAG, "registerSync:IllegalArgumentException");
Message message = obtainMessage(STOP_SCAN_OFFLOAD);
@@ -558,7 +533,8 @@ public class BassClientStateMachine extends StateMachine {
mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state);
}
- private static boolean isEmpty(final byte[] data) {
+ @VisibleForTesting
+ static boolean isEmpty(final byte[] data) {
return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0);
}
@@ -589,7 +565,8 @@ public class BassClientStateMachine extends StateMachine {
& (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
log("Initiate PAST for: " + mDevice + ", syncHandle: " + syncHandle
+ "serviceData" + serviceData);
- mPeriodicAdvManager.transferSync(mDevice, serviceData, syncHandle);
+ BluetoothMethodProxy.getInstance().periodicAdvertisingManagerTransferSync(
+ mPeriodicAdvManager, mDevice, serviceData, syncHandle);
}
} else {
if (mService.isLocalBroadcast(mPendingMetadata)) {
@@ -602,8 +579,9 @@ public class BassClientStateMachine extends StateMachine {
log("Initiate local broadcast PAST for: " + mDevice
+ ", advSID/Handle: " + advHandle
+ ", serviceData: " + serviceData);
- mPeriodicAdvManager.transferSetInfo(mDevice, serviceData,
- advHandle, mPeriodicAdvCallback);
+ BluetoothMethodProxy.getInstance().periodicAdvertisingManagerTransferSetInfo(
+ mPeriodicAdvManager, mDevice, serviceData, advHandle,
+ mPeriodicAdvCallback);
} else {
Log.e(TAG, "There is no valid sync handle for this Source");
if (mAutoAssist) {
@@ -750,6 +728,7 @@ public class BassClientStateMachine extends StateMachine {
BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE);
byte sourceAddressType = receiverState[BassConstants
.BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX];
+ BassUtils.reverse(sourceAddress);
String address = Utils.getAddressStringFromByte(sourceAddress);
BluetoothDevice device = btAdapter.getRemoteLeDevice(
address, sourceAddressType);
@@ -852,157 +831,157 @@ public class BassClientStateMachine extends StateMachine {
// Implements callback methods for GATT events that the app cares about.
// For example, connection change and services discovered.
final class GattCallback extends BluetoothGattCallback {
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- boolean isStateChanged = false;
- log("onConnectionStateChange : Status=" + status + "newState" + newState);
- if (newState == BluetoothProfile.STATE_CONNECTED
- && getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
- isStateChanged = true;
- Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice);
- if (mService.okToConnect(mDevice)) {
- log("Bassclient Connected to: " + mDevice);
- if (mBluetoothGatt != null) {
- log("Attempting to start service discovery:"
- + mBluetoothGatt.discoverServices());
- mDiscoveryInitiated = true;
- }
- } else if (mBluetoothGatt != null) {
- // Reject the connection
- Log.w(TAG, "Bassclient Connect request rejected: " + mDevice);
- mBluetoothGatt.disconnect();
- mBluetoothGatt.close();
- mBluetoothGatt = null;
- // force move to disconnected
- newState = BluetoothProfile.STATE_DISCONNECTED;
- }
- } else if (newState == BluetoothProfile.STATE_DISCONNECTED
- && getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
- isStateChanged = true;
- log("Disconnected from Bass GATT server.");
- }
- if (isStateChanged) {
- Message m = obtainMessage(CONNECTION_STATE_CHANGED);
- m.obj = newState;
- sendMessage(m);
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ boolean isStateChanged = false;
+ log("onConnectionStateChange : Status=" + status + "newState" + newState);
+ if (newState == BluetoothProfile.STATE_CONNECTED
+ && getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+ isStateChanged = true;
+ Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice);
+ if (mService.okToConnect(mDevice)) {
+ log("Bassclient Connected to: " + mDevice);
+ if (mBluetoothGatt != null) {
+ log("Attempting to start service discovery:"
+ + mBluetoothGatt.discoverServices());
+ mDiscoveryInitiated = true;
}
+ } else if (mBluetoothGatt != null) {
+ // Reject the connection
+ Log.w(TAG, "Bassclient Connect request rejected: " + mDevice);
+ mBluetoothGatt.disconnect();
+ mBluetoothGatt.close();
+ mBluetoothGatt = null;
+ // force move to disconnected
+ newState = BluetoothProfile.STATE_DISCONNECTED;
}
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED
+ && getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+ isStateChanged = true;
+ log("Disconnected from Bass GATT server.");
+ }
+ if (isStateChanged) {
+ Message m = obtainMessage(CONNECTION_STATE_CHANGED);
+ m.obj = newState;
+ sendMessage(m);
+ }
+ }
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- log("onServicesDiscovered:" + status);
- if (mDiscoveryInitiated) {
- mDiscoveryInitiated = false;
- if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) {
- mBluetoothGatt.requestMtu(BassConstants.BASS_MAX_BYTES);
- mMTUChangeRequested = true;
- } else {
- Log.w(TAG, "onServicesDiscovered received: "
- + status + "mBluetoothGatt" + mBluetoothGatt);
- }
- } else {
- log("remote initiated callback");
- }
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ log("onServicesDiscovered:" + status);
+ if (mDiscoveryInitiated) {
+ mDiscoveryInitiated = false;
+ if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) {
+ mBluetoothGatt.requestMtu(BassConstants.BASS_MAX_BYTES);
+ mMTUChangeRequested = true;
+ } else {
+ Log.w(TAG, "onServicesDiscovered received: "
+ + status + "mBluetoothGatt" + mBluetoothGatt);
}
+ } else {
+ log("remote initiated callback");
+ }
+ }
- @Override
- public void onCharacteristicRead(
- BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic,
- int status) {
- log("onCharacteristicRead:: status: " + status + "char:" + characteristic);
- if (status == BluetoothGatt.GATT_SUCCESS && characteristic.getUuid()
- .equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
- log("onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status);
- logByteArray("Received ", characteristic.getValue(), 0,
- characteristic.getValue().length);
- if (characteristic.getValue() == null) {
- Log.e(TAG, "Remote receiver state is NULL");
- return;
- }
- processBroadcastReceiverState(characteristic.getValue(), characteristic);
- }
- // switch to receiving notifications after initial characteristic read
- BluetoothGattDescriptor desc = characteristic
- .getDescriptor(BassConstants.CLIENT_CHARACTERISTIC_CONFIG);
- if (mBluetoothGatt != null && desc != null) {
- log("Setting the value for Desc");
- mBluetoothGatt.setCharacteristicNotification(characteristic, true);
- desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- mBluetoothGatt.writeDescriptor(desc);
- } else {
- Log.w(TAG, "CCC for " + characteristic + "seem to be not present");
- // at least move the SM to stable state
- Message m = obtainMessage(GATT_TXN_PROCESSED);
- m.arg1 = status;
- sendMessage(m);
- }
+ @Override
+ public void onCharacteristicRead(
+ BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic,
+ int status) {
+ log("onCharacteristicRead:: status: " + status + "char:" + characteristic);
+ if (status == BluetoothGatt.GATT_SUCCESS && characteristic.getUuid()
+ .equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
+ log("onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status);
+ if (characteristic.getValue() == null) {
+ Log.e(TAG, "Remote receiver state is NULL");
+ return;
}
+ logByteArray("Received ", characteristic.getValue(), 0,
+ characteristic.getValue().length);
+ processBroadcastReceiverState(characteristic.getValue(), characteristic);
+ }
+ // switch to receiving notifications after initial characteristic read
+ BluetoothGattDescriptor desc = characteristic
+ .getDescriptor(BassConstants.CLIENT_CHARACTERISTIC_CONFIG);
+ if (mBluetoothGatt != null && desc != null) {
+ log("Setting the value for Desc");
+ mBluetoothGatt.setCharacteristicNotification(characteristic, true);
+ desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+ mBluetoothGatt.writeDescriptor(desc);
+ } else {
+ Log.w(TAG, "CCC for " + characteristic + "seem to be not present");
+ // at least move the SM to stable state
+ Message m = obtainMessage(GATT_TXN_PROCESSED);
+ m.arg1 = status;
+ sendMessage(m);
+ }
+ }
- @Override
- public void onDescriptorWrite(
- BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
- log("onDescriptorWrite");
- if (status == BluetoothGatt.GATT_SUCCESS
- && descriptor.getUuid()
- .equals(BassConstants.CLIENT_CHARACTERISTIC_CONFIG)) {
- log("CCC write resp");
- }
+ @Override
+ public void onDescriptorWrite(
+ BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ log("onDescriptorWrite");
+ if (status == BluetoothGatt.GATT_SUCCESS
+ && descriptor.getUuid()
+ .equals(BassConstants.CLIENT_CHARACTERISTIC_CONFIG)) {
+ log("CCC write resp");
+ }
- // Move the SM to connected so further reads happens
- Message m = obtainMessage(GATT_TXN_PROCESSED);
- m.arg1 = status;
- sendMessage(m);
- }
+ // Move the SM to connected so further reads happens
+ Message m = obtainMessage(GATT_TXN_PROCESSED);
+ m.arg1 = status;
+ sendMessage(m);
+ }
- @Override
- public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
- log("onMtuChanged: mtu:" + mtu);
- if (mMTUChangeRequested && mBluetoothGatt != null) {
- acquireAllBassChars();
- mMTUChangeRequested = false;
- } else {
- log("onMtuChanged is remote initiated trigger, mBluetoothGatt:"
- + mBluetoothGatt);
- }
- }
+ @Override
+ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+ log("onMtuChanged: mtu:" + mtu);
+ if (mMTUChangeRequested && mBluetoothGatt != null) {
+ acquireAllBassChars();
+ mMTUChangeRequested = false;
+ } else {
+ log("onMtuChanged is remote initiated trigger, mBluetoothGatt:"
+ + mBluetoothGatt);
+ }
+ }
- @Override
- public void onCharacteristicChanged(
- BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- log("onCharacteristicChanged :: " + characteristic.getUuid().toString());
- if (characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
- log("onCharacteristicChanged is rcvr State :: "
- + characteristic.getUuid().toString());
- if (characteristic.getValue() == null) {
- Log.e(TAG, "Remote receiver state is NULL");
- return;
- }
- logByteArray("onCharacteristicChanged: Received ",
- characteristic.getValue(),
- 0,
- characteristic.getValue().length);
- processBroadcastReceiverState(characteristic.getValue(), characteristic);
- }
+ @Override
+ public void onCharacteristicChanged(
+ BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ log("onCharacteristicChanged :: " + characteristic.getUuid().toString());
+ if (characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
+ log("onCharacteristicChanged is rcvr State :: "
+ + characteristic.getUuid().toString());
+ if (characteristic.getValue() == null) {
+ Log.e(TAG, "Remote receiver state is NULL");
+ return;
}
+ logByteArray("onCharacteristicChanged: Received ",
+ characteristic.getValue(),
+ 0,
+ characteristic.getValue().length);
+ processBroadcastReceiverState(characteristic.getValue(), characteristic);
+ }
+ }
- @Override
- public void onCharacteristicWrite(
- BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic,
- int status) {
- log("onCharacteristicWrite: " + characteristic.getUuid().toString()
- + "status:" + status);
- if (status == 0
- && characteristic.getUuid()
- .equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
- log("BASS_BCAST_AUDIO_SCAN_CTRL_POINT is written successfully");
- }
- Message m = obtainMessage(GATT_TXN_PROCESSED);
- m.arg1 = status;
- sendMessage(m);
- }
- };
+ @Override
+ public void onCharacteristicWrite(
+ BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic,
+ int status) {
+ log("onCharacteristicWrite: " + characteristic.getUuid().toString()
+ + "status:" + status);
+ if (status == 0
+ && characteristic.getUuid()
+ .equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
+ log("BASS_BCAST_AUDIO_SCAN_CTRL_POINT is written successfully");
+ }
+ Message m = obtainMessage(GATT_TXN_PROCESSED);
+ m.arg1 = status;
+ sendMessage(m);
+ }
+ }
/**
* Connects to the GATT server of the device.
@@ -1015,11 +994,16 @@ public class BassClientStateMachine extends StateMachine {
mGattCallback = new GattCallback();
}
- mBluetoothGatt = mDevice.connectGatt(mService, autoConnect,
+ BluetoothGatt gatt = mDevice.connectGatt(mService, autoConnect,
mGattCallback, BluetoothDevice.TRANSPORT_LE,
(BluetoothDevice.PHY_LE_1M_MASK
| BluetoothDevice.PHY_LE_2M_MASK
| BluetoothDevice.PHY_LE_CODED_MASK), null);
+
+ if (gatt != null) {
+ mBluetoothGatt = new BluetoothGattTestableWrapper(gatt);
+ }
+
return mBluetoothGatt != null;
}
@@ -1228,18 +1212,6 @@ public class BassClientStateMachine extends StateMachine {
}
}
- private byte[] bluetoothAddressToBytes(String s) {
- log("BluetoothAddressToBytes: input string:" + s);
- String[] splits = s.split(":");
- byte[] addressBytes = new byte[6];
- for (int i = 0; i < 6; i++) {
- int hexValue = Integer.parseInt(splits[i], 16);
- log("hexValue:" + hexValue);
- addressBytes[i] = (byte) hexValue;
- }
- return addressBytes;
- }
-
private static int getBisSyncFromChannelPreference(
List channels) {
int bisSync = 0;
@@ -1267,7 +1239,9 @@ public class BassClientStateMachine extends StateMachine {
stream.write(metaData.getSourceAddressType());
// Advertiser_Address
- stream.write(Utils.getBytesFromAddress(advSource.getAddress()), 0, 6);
+ byte[] bcastSourceAddr = Utils.getBytesFromAddress(advSource.getAddress());
+ BassUtils.reverse(bcastSourceAddr);
+ stream.write(bcastSourceAddr, 0, 6);
log("Address bytes: " + advSource.getAddress());
// Advertising_SID
@@ -1387,15 +1361,6 @@ public class BassClientStateMachine extends StateMachine {
return res;
}
- private byte[] convertAsciitoValues(byte[] val) {
- byte[] ret = new byte[val.length];
- for (int i = 0; i < val.length; i++) {
- ret[i] = (byte) (val[i] - (byte) '0');
- }
- log("convertAsciitoValues: returns:" + Arrays.toString(val));
- return ret;
- }
-
private byte[] convertRecvStateToSetBroadcastCodeByteArray(
BluetoothLeBroadcastReceiveState recvState) {
byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN];
@@ -1448,8 +1413,7 @@ public class BassClientStateMachine extends StateMachine {
continue;
}
if (sourceId == state.getSourceId() && state.getBigEncryptionState()
- == BluetoothLeBroadcastReceiveState
- .BIG_ENCRYPTION_STATE_CODE_REQUIRED) {
+ == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED) {
retval = true;
break;
}
@@ -1467,6 +1431,12 @@ public class BassClientStateMachine extends StateMachine {
removeDeferredMessages(CONNECT);
if (mLastConnectionState == BluetoothProfile.STATE_CONNECTED) {
log("CONNECTED->CONNECTED: Ignore");
+ // Broadcast for testing purpose only
+ if (Utils.isInstrumentationTestMode()) {
+ Intent intent = new Intent("android.bluetooth.bass_client.NOTIFY_TEST");
+ mService.sendBroadcast(intent, BLUETOOTH_CONNECT,
+ Utils.getTempAllowlistBroadcastOptions());
+ }
} else {
broadcastConnectionState(mDevice, mLastConnectionState,
BluetoothProfile.STATE_CONNECTED);
@@ -1605,7 +1575,6 @@ public class BassClientStateMachine extends StateMachine {
case SET_BCAST_CODE:
BluetoothLeBroadcastReceiveState recvState =
(BluetoothLeBroadcastReceiveState) message.obj;
- sourceId = message.arg2;
log("SET_BCAST_CODE metaData: " + recvState);
if (!isItRightTimeToUpdateBroadcastPin((byte) recvState.getSourceId())) {
mSetBroadcastCodePending = true;
@@ -1726,12 +1695,20 @@ public class BassClientStateMachine extends StateMachine {
}
}
+ // public for testing, but private for non-testing
@VisibleForTesting
class ConnectedProcessing extends State {
@Override
public void enter() {
log("Enter ConnectedProcessing(" + mDevice + "): "
+ messageWhatToString(getCurrentMessage().what));
+
+ // Broadcast for testing purpose only
+ if (Utils.isInstrumentationTestMode()) {
+ Intent intent = new Intent("android.bluetooth.bass_client.NOTIFY_TEST");
+ mService.sendBroadcast(intent, BLUETOOTH_CONNECT,
+ Utils.getTempAllowlistBroadcastOptions());
+ }
}
@Override
public void exit() {
@@ -1794,7 +1771,7 @@ public class BassClientStateMachine extends StateMachine {
transitionTo(mConnected);
break;
case GATT_TXN_TIMEOUT:
- log("GATT transaction timedout for" + mDevice);
+ log("GATT transaction timeout for" + mDevice);
sendPendingCallbacks(
mPendingOperation,
BluetoothStatusCodes.ERROR_UNKNOWN);
@@ -1820,67 +1797,6 @@ public class BassClientStateMachine extends StateMachine {
}
}
- @VisibleForTesting
- class Disconnecting extends State {
- @Override
- public void enter() {
- log("Enter Disconnecting(" + mDevice + "): "
- + messageWhatToString(getCurrentMessage().what));
- sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs);
- broadcastConnectionState(
- mDevice, mLastConnectionState, BluetoothProfile.STATE_DISCONNECTING);
- }
-
- @Override
- public void exit() {
- log("Exit Disconnecting(" + mDevice + "): "
- + messageWhatToString(getCurrentMessage().what));
- removeMessages(CONNECT_TIMEOUT);
- mLastConnectionState = BluetoothProfile.STATE_DISCONNECTING;
- }
-
- @Override
- public boolean processMessage(Message message) {
- log("Disconnecting process message(" + mDevice + "): "
- + messageWhatToString(message.what));
- switch (message.what) {
- case CONNECT:
- log("Disconnecting to " + mDevice);
- log("deferring this connection request " + mDevice);
- deferMessage(message);
- break;
- case DISCONNECT:
- Log.w(TAG, "Already disconnecting: DISCONNECT ignored: " + mDevice);
- break;
- case CONNECTION_STATE_CHANGED:
- int state = (int) message.obj;
- Log.w(TAG, "Disconnecting: connection state changed:" + state);
- if (state == BluetoothProfile.STATE_CONNECTED) {
- Log.e(TAG, "should never happen from this state");
- transitionTo(mConnected);
- } else {
- Log.w(TAG, "disconnection successful to " + mDevice);
- cancelActiveSync(null);
- transitionTo(mDisconnected);
- }
- break;
- case CONNECT_TIMEOUT:
- Log.w(TAG, "CONNECT_TIMEOUT");
- BluetoothDevice device = (BluetoothDevice) message.obj;
- if (!mDevice.equals(device)) {
- Log.e(TAG, "Unknown device timeout " + device);
- break;
- }
- transitionTo(mDisconnected);
- break;
- default:
- log("Disconnecting: not handled message:" + message.what);
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
log("broadcastConnectionState " + device + ": " + fromState + "->" + toState);
if (fromState == BluetoothProfile.STATE_CONNECTED
@@ -1907,9 +1823,6 @@ public class BassClientStateMachine extends StateMachine {
case "Disconnected":
log("Disconnected");
return BluetoothProfile.STATE_DISCONNECTED;
- case "Disconnecting":
- log("Disconnecting");
- return BluetoothProfile.STATE_DISCONNECTING;
case "Connecting":
log("Connecting");
return BluetoothProfile.STATE_CONNECTING;
@@ -1971,22 +1884,6 @@ public class BassClientStateMachine extends StateMachine {
return Integer.toString(what);
}
- private static String profileStateToString(int state) {
- switch (state) {
- case BluetoothProfile.STATE_DISCONNECTED:
- return "DISCONNECTED";
- case BluetoothProfile.STATE_CONNECTING:
- return "CONNECTING";
- case BluetoothProfile.STATE_CONNECTED:
- return "CONNECTED";
- case BluetoothProfile.STATE_DISCONNECTING:
- return "DISCONNECTING";
- default:
- break;
- }
- return Integer.toString(state);
- }
-
/**
* Dump info
*/
@@ -2025,4 +1922,81 @@ public class BassClientStateMachine extends StateMachine {
}
Log.d(TAG, builder.toString());
}
+
+ /** Mockable wrapper of {@link BluetoothGatt}. */
+ @VisibleForTesting
+ public static class BluetoothGattTestableWrapper {
+ public final BluetoothGatt mWrappedBluetoothGatt;
+
+ BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt) {
+ mWrappedBluetoothGatt = bluetoothGatt;
+ }
+
+ /** See {@link BluetoothGatt#getServices()}. */
+ public List getServices() {
+ return mWrappedBluetoothGatt.getServices();
+ }
+
+ /** See {@link BluetoothGatt#getService(UUID)}. */
+ @Nullable
+ public BluetoothGattService getService(UUID uuid) {
+ return mWrappedBluetoothGatt.getService(uuid);
+ }
+
+ /** See {@link BluetoothGatt#discoverServices()}. */
+ public boolean discoverServices() {
+ return mWrappedBluetoothGatt.discoverServices();
+ }
+
+ /**
+ * See {@link BluetoothGatt#readCharacteristic(
+ * BluetoothGattCharacteristic)}.
+ */
+ public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
+ return mWrappedBluetoothGatt.readCharacteristic(characteristic);
+ }
+
+ /**
+ * See {@link BluetoothGatt#writeCharacteristic(
+ * BluetoothGattCharacteristic, byte[], int)} .
+ */
+ public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
+ return mWrappedBluetoothGatt.writeCharacteristic(characteristic);
+ }
+
+ /** See {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)}. */
+ public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
+ return mWrappedBluetoothGatt.readDescriptor(descriptor);
+ }
+
+ /**
+ * See {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor,
+ * byte[])}.
+ */
+ public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
+ return mWrappedBluetoothGatt.writeDescriptor(descriptor);
+ }
+
+ /** See {@link BluetoothGatt#requestMtu(int)}. */
+ public boolean requestMtu(int mtu) {
+ return mWrappedBluetoothGatt.requestMtu(mtu);
+ }
+
+ /** See {@link BluetoothGatt#setCharacteristicNotification}. */
+ public boolean setCharacteristicNotification(
+ BluetoothGattCharacteristic characteristic, boolean enable) {
+ return mWrappedBluetoothGatt.setCharacteristicNotification(characteristic, enable);
+ }
+
+ /** See {@link BluetoothGatt#disconnect()}. */
+ public void disconnect() {
+ mWrappedBluetoothGatt.disconnect();
+ }
+
+ /** See {@link BluetoothGatt#close()}. */
+ public void close() {
+ mWrappedBluetoothGatt.close();
+ }
+ }
+
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassUtils.java b/android/app/src/com/android/bluetooth/bass_client/BassUtils.java
index 42f88cad0cd414090e4e900bfecec99a566fd3ad..2850192012e073058b74228380f2fbd3cbaa4e30 100755
--- a/android/app/src/com/android/bluetooth/bass_client/BassUtils.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassUtils.java
@@ -141,4 +141,13 @@ class BassUtils {
log("array[" + i + "] :" + Byte.toUnsignedInt(array[i]));
}
}
+
+ static void reverse(byte[] address) {
+ int len = address.length;
+ for (int i = 0; i < len / 2; ++i) {
+ byte b = address[i];
+ address[i] = address[len - 1 - i];
+ address[len - 1 - i] = b;
+ }
+ }
}
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index ff301c66198299488d9835ca6bb8aad66372bdd0..f043394a59ac762b3bca2aca68f9ecd48a9d328a 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -21,6 +21,7 @@ import android.annotation.SuppressLint;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
@@ -39,19 +40,22 @@ import android.os.Message;
import android.util.Log;
import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The active device manager is responsible for keeping track of the
- * connected A2DP/HFP/AVRCP/HearingAid devices and select which device is
+ * connected A2DP/HFP/AVRCP/HearingAid/LE audio devices and select which device is
* active (for each profile).
+ * The active device manager selects a fallback device when the currently active device
+ * is disconnected, and it selects BT devices that are lastly activated one.
*
* Current policy (subject to change):
* 1) If the maximum number of connected devices is one, the manager doesn't
@@ -65,24 +69,24 @@ import java.util.Objects;
* - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP
* - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP
* - BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED for HearingAid
+ * - BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED for LE audio
* If such broadcast is received (e.g., triggered indirectly by user
- * action on the UI), the device in the received broacast is marked
+ * action on the UI), the device in the received broadcast is marked
* as the current active device for that profile.
- * 5) If there is a HearingAid active device, then A2DP and HFP active devices
- * must be set to null (i.e., A2DP and HFP cannot have active devices).
- * The reason is because A2DP or HFP cannot be used together with HearingAid.
+ * 5) If there is a HearingAid active device, then A2DP, HFP and LE audio active devices
+ * must be set to null (i.e., A2DP, HFP and LE audio cannot have active devices).
+ * The reason is that A2DP, HFP or LE audio cannot be used together with HearingAid.
* 6) If there are no connected devices (e.g., during startup, or after all
* devices have been disconnected, the active device per profile
- * (A2DP/HFP/HearingAid) is selected as follows:
+ * (A2DP/HFP/HearingAid/LE audio) is selected as follows:
* 6.1) The last connected HearingAid device is selected as active.
- * If there is an active A2DP or HFP device, those must be set to null.
- * 6.2) The last connected A2DP or HFP device is selected as active.
+ * If there is an active A2DP, HFP or LE audio device, those must be set to null.
+ * 6.2) The last connected A2DP, HFP or LE audio device is selected as active.
* However, if there is an active HearingAid device, then the
- * A2DP or HFP active device is not set (must remain null).
+ * A2DP, HFP, or LE audio active device is not set (must remain null).
* 7) If the currently active device (per profile) is disconnected, the
* Active Device Manager just marks that the profile has no active device,
- * but does not attempt to select a new one. Currently, the expectation is
- * that the user will explicitly select the new active device.
+ * and the lastly activated BT device that is still connected would be selected.
* 8) If there is already an active device, and the corresponding
* ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device
* contained in the broadcast is marked as active. However, if
@@ -90,7 +94,7 @@ import java.util.Objects;
* as having no active device.
* 9) If a wired audio device is connected, the audio output is switched
* by the Audio Framework itself to that device. We detect this here,
- * and the active device for each profile (A2DP/HFP/HearingAid) is set
+ * and the active device for each profile (A2DP/HFP/HearingAid/LE audio) is set
* to null to reflect the output device state change. However, if the
* wired audio device is disconnected, we don't do anything explicit
* and apply the default behavior instead:
@@ -113,8 +117,12 @@ class ActiveDeviceManager {
private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 3;
private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 4;
private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 5;
- private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 6;
- private static final int MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED = 7;
+ private static final int MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE_CHANGED = 6;
+ private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 7;
+ private static final int MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE_CHANGED = 8;
+ private static final int MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED = 9;
+ private static final int MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED = 10;
+ private static final int MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED = 11;
private final AdapterService mAdapterService;
private final ServiceFactory mFactory;
@@ -123,12 +131,17 @@ class ActiveDeviceManager {
private final AudioManager mAudioManager;
private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
- private final List mA2dpConnectedDevices = new LinkedList<>();
- private final List mHfpConnectedDevices = new LinkedList<>();
+ private final List mA2dpConnectedDevices = new ArrayList<>();
+ private final List mHfpConnectedDevices = new ArrayList<>();
+ private final List mHearingAidConnectedDevices = new ArrayList<>();
+ private final List mLeAudioConnectedDevices = new ArrayList<>();
+ private final List mLeHearingAidConnectedDevices = new ArrayList<>();
+ private List mPendingLeHearingAidActiveDevice = new ArrayList<>();
private BluetoothDevice mA2dpActiveDevice = null;
private BluetoothDevice mHfpActiveDevice = null;
private BluetoothDevice mHearingAidActiveDevice = null;
private BluetoothDevice mLeAudioActiveDevice = null;
+ private BluetoothDevice mLeHearingAidActiveDevice = null;
// Broadcast receiver for all changes
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -142,32 +155,48 @@ class ActiveDeviceManager {
switch (action) {
case BluetoothAdapter.ACTION_STATE_CHANGED:
mHandler.obtainMessage(MESSAGE_ADAPTER_ACTION_STATE_CHANGED,
- intent).sendToTarget();
+ intent).sendToTarget();
break;
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
mHandler.obtainMessage(MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED,
- intent).sendToTarget();
+ intent).sendToTarget();
break;
case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
mHandler.obtainMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED,
- intent).sendToTarget();
+ intent).sendToTarget();
break;
case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
mHandler.obtainMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED,
- intent).sendToTarget();
+ intent).sendToTarget();
break;
case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
mHandler.obtainMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED,
- intent).sendToTarget();
+ intent).sendToTarget();
+ break;
+ case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
+ mHandler.obtainMessage(MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE_CHANGED,
+ intent).sendToTarget();
break;
case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
mHandler.obtainMessage(MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED,
intent).sendToTarget();
break;
+ case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
+ mHandler.obtainMessage(MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE_CHANGED,
+ intent).sendToTarget();
+ break;
case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
mHandler.obtainMessage(MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED,
intent).sendToTarget();
break;
+ case BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED:
+ mHandler.obtainMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED,
+ intent).sendToTarget();
+ break;
+ case BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE:
+ mHandler.obtainMessage(MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED,
+ intent).sendToTarget();
+ break;
default:
Log.e(TAG, "Received unexpected intent, action=" + action);
break;
@@ -217,11 +246,10 @@ class ActiveDeviceManager {
break; // The device is already connected
}
mA2dpConnectedDevices.add(device);
- if (mHearingAidActiveDevice == null) {
+ if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) {
// New connected device: select it as active
setA2dpActiveDevice(device);
setLeAudioActiveDevice(null);
- break;
}
break;
}
@@ -233,9 +261,11 @@ class ActiveDeviceManager {
+ "device " + device + " disconnected");
}
mA2dpConnectedDevices.remove(device);
- if (mA2dpConnectedDevices.isEmpty()
- && Objects.equals(mA2dpActiveDevice, device)) {
- setA2dpActiveDevice(null);
+ if (Objects.equals(mA2dpActiveDevice, device)) {
+ if (mA2dpConnectedDevices.isEmpty()) {
+ setA2dpActiveDevice(null);
+ }
+ setFallbackDeviceActive();
}
}
}
@@ -253,6 +283,9 @@ class ActiveDeviceManager {
setHearingAidActiveDevice(null);
setLeAudioActiveDevice(null);
}
+ if (mHfpConnectedDevices.contains(device)) {
+ setHfpActiveDevice(device);
+ }
// Just assign locally the new value
mA2dpActiveDevice = device;
}
@@ -279,11 +312,10 @@ class ActiveDeviceManager {
break; // The device is already connected
}
mHfpConnectedDevices.add(device);
- if (mHearingAidActiveDevice == null) {
+ if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) {
// New connected device: select it as active
setHfpActiveDevice(device);
setLeAudioActiveDevice(null);
- break;
}
break;
}
@@ -295,9 +327,11 @@ class ActiveDeviceManager {
+ "device " + device + " disconnected");
}
mHfpConnectedDevices.remove(device);
- if (mHfpConnectedDevices.isEmpty()
- && Objects.equals(mHfpActiveDevice, device)) {
- setHfpActiveDevice(null);
+ if (Objects.equals(mHfpActiveDevice, device)) {
+ if (mHfpConnectedDevices.isEmpty()) {
+ setHfpActiveDevice(null);
+ }
+ setFallbackDeviceActive();
}
}
}
@@ -315,11 +349,58 @@ class ActiveDeviceManager {
setHearingAidActiveDevice(null);
setLeAudioActiveDevice(null);
}
+ if (mA2dpConnectedDevices.contains(device)) {
+ setA2dpActiveDevice(device);
+ }
// Just assign locally the new value
mHfpActiveDevice = device;
}
break;
+ case MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE_CHANGED: {
+ Intent intent = (Intent) msg.obj;
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
+ int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ if (prevState == nextState) {
+ // Nothing has changed
+ break;
+ }
+ if (nextState == BluetoothProfile.STATE_CONNECTED) {
+ // Device connected
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE"
+ + "_CHANGED): device " + device + " connected");
+ }
+ if (mHearingAidConnectedDevices.contains(device)) {
+ break; // The device is already connected
+ }
+ mHearingAidConnectedDevices.add(device);
+ // New connected device: select it as active
+ setHearingAidActiveDevice(device);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ setLeAudioActiveDevice(null);
+ break;
+ }
+ if (prevState == BluetoothProfile.STATE_CONNECTED) {
+ // Device disconnected
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE"
+ + "_CHANGED): device " + device + " disconnected");
+ }
+ mHearingAidConnectedDevices.remove(device);
+ if (Objects.equals(mHearingAidActiveDevice, device)) {
+ if (mHearingAidConnectedDevices.isEmpty()) {
+ setHearingAidActiveDevice(null);
+ }
+ setFallbackDeviceActive();
+ }
+ }
+ }
+ break;
+
case MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED: {
Intent intent = (Intent) msg.obj;
BluetoothDevice device =
@@ -338,10 +419,65 @@ class ActiveDeviceManager {
}
break;
+ case MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE_CHANGED: {
+ Intent intent = (Intent) msg.obj;
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
+ int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ if (prevState == nextState) {
+ // Nothing has changed
+ break;
+ }
+ if (nextState == BluetoothProfile.STATE_CONNECTED) {
+ // Device connected
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE"
+ + "_CHANGED): device " + device + " connected");
+ }
+ if (mLeAudioConnectedDevices.contains(device)) {
+ break; // The device is already connected
+ }
+ mLeAudioConnectedDevices.add(device);
+ if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null
+ && mPendingLeHearingAidActiveDevice.isEmpty()) {
+ // New connected device: select it as active
+ setLeAudioActiveDevice(device);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ } else if (mPendingLeHearingAidActiveDevice.contains(device)) {
+ setLeHearingAidActiveDevice(device);
+ setHearingAidActiveDevice(null);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ }
+ break;
+ }
+ if (prevState == BluetoothProfile.STATE_CONNECTED) {
+ // Device disconnected
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE"
+ + "_CHANGED): device " + device + " disconnected");
+ }
+ mLeAudioConnectedDevices.remove(device);
+ mLeHearingAidConnectedDevices.remove(device);
+ if (Objects.equals(mLeAudioActiveDevice, device)) {
+ if (mLeAudioConnectedDevices.isEmpty()) {
+ setLeAudioActiveDevice(null);
+ }
+ setFallbackDeviceActive();
+ }
+ }
+ }
+ break;
+
case MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED: {
Intent intent = (Intent) msg.obj;
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device != null && !mLeAudioConnectedDevices.contains(device)) {
+ mLeAudioConnectedDevices.add(device);
+ }
if (DBG) {
Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED): "
+ "device= " + device);
@@ -355,6 +491,75 @@ class ActiveDeviceManager {
mLeAudioActiveDevice = device;
}
break;
+
+ case MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED: {
+ Intent intent = (Intent) msg.obj;
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
+ int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ if (prevState == nextState) {
+ // Nothing has changed
+ break;
+ }
+ if (nextState == BluetoothProfile.STATE_CONNECTED) {
+ // Device connected
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE"
+ + "_CHANGED): device " + device + " connected");
+ }
+ if (mLeHearingAidConnectedDevices.contains(device)) {
+ break; // The device is already connected
+ }
+ mLeHearingAidConnectedDevices.add(device);
+ if (!mLeAudioConnectedDevices.contains(device)) {
+ mPendingLeHearingAidActiveDevice.add(device);
+ } else if (Objects.equals(mLeAudioActiveDevice, device)) {
+ mLeHearingAidActiveDevice = device;
+ } else {
+ // New connected device: select it as active
+ setLeHearingAidActiveDevice(device);
+ setHearingAidActiveDevice(null);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ }
+ break;
+ }
+ if (prevState == BluetoothProfile.STATE_CONNECTED) {
+ // Device disconnected
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE"
+ + "_CHANGED): device " + device + " disconnected");
+ }
+ mLeHearingAidConnectedDevices.remove(device);
+ mPendingLeHearingAidActiveDevice.remove(device);
+ if (Objects.equals(mLeHearingAidActiveDevice, device)) {
+ mLeHearingAidActiveDevice = null;
+ }
+ }
+ }
+ break;
+
+ case MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED: {
+ Intent intent = (Intent) msg.obj;
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device != null && !mLeHearingAidConnectedDevices.contains(device)) {
+ mLeHearingAidConnectedDevices.add(device);
+ }
+ if (DBG) {
+ Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED): "
+ + "device= " + device);
+ }
+ // Just assign locally the new value
+ if (device != null && !Objects.equals(mLeHearingAidActiveDevice, device)) {
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ setHearingAidActiveDevice(null);
+ }
+ mLeHearingAidActiveDevice = mLeAudioActiveDevice = device;
+ }
+ break;
}
}
}
@@ -422,8 +627,12 @@ class ActiveDeviceManager {
filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+ filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
+ filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+ filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE);
mAdapterService.registerReceiver(mReceiver, filter);
mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
@@ -512,6 +721,133 @@ class ActiveDeviceManager {
return;
}
mLeAudioActiveDevice = device;
+ if (device == null) {
+ mLeHearingAidActiveDevice = null;
+ mPendingLeHearingAidActiveDevice.remove(device);
+ }
+ }
+
+ private void setLeHearingAidActiveDevice(BluetoothDevice device) {
+ if (!Objects.equals(mLeAudioActiveDevice, device)) {
+ setLeAudioActiveDevice(device);
+ }
+ if (Objects.equals(mLeAudioActiveDevice, device)) {
+ // setLeAudioActiveDevice succeed
+ mLeHearingAidActiveDevice = device;
+ mPendingLeHearingAidActiveDevice.remove(device);
+ }
+ }
+
+ private void setFallbackDeviceActive() {
+ if (DBG) {
+ Log.d(TAG, "setFallbackDeviceActive");
+ }
+ DatabaseManager dbManager = mAdapterService.getDatabase();
+ if (dbManager == null) {
+ return;
+ }
+ List connectedHearingAidDevices = new ArrayList<>();
+ if (!mHearingAidConnectedDevices.isEmpty()) {
+ connectedHearingAidDevices.addAll(mHearingAidConnectedDevices);
+ }
+ if (!mLeHearingAidConnectedDevices.isEmpty()) {
+ connectedHearingAidDevices.addAll(mLeHearingAidConnectedDevices);
+ }
+ if (!connectedHearingAidDevices.isEmpty()) {
+ BluetoothDevice device =
+ dbManager.getMostRecentlyConnectedDevicesInList(connectedHearingAidDevices);
+ if (device != null) {
+ if (mHearingAidConnectedDevices.contains(device)) {
+ if (DBG) {
+ Log.d(TAG, "set hearing aid device active: " + device);
+ }
+ setHearingAidActiveDevice(device);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ setLeAudioActiveDevice(null);
+ } else {
+ if (DBG) {
+ Log.d(TAG, "set LE hearing aid device active: " + device);
+ }
+ setLeHearingAidActiveDevice(device);
+ setHearingAidActiveDevice(null);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ }
+ return;
+ }
+ }
+
+ A2dpService a2dpService = mFactory.getA2dpService();
+ BluetoothDevice a2dpFallbackDevice = null;
+ if (a2dpService != null) {
+ a2dpFallbackDevice = a2dpService.getFallbackDevice();
+ }
+
+ HeadsetService headsetService = mFactory.getHeadsetService();
+ BluetoothDevice headsetFallbackDevice = null;
+ if (headsetService != null) {
+ headsetFallbackDevice = headsetService.getFallbackDevice();
+ }
+
+ List connectedDevices = new ArrayList<>();
+ connectedDevices.addAll(mLeAudioConnectedDevices);
+ switch (mAudioManager.getMode()) {
+ case AudioManager.MODE_NORMAL:
+ if (a2dpFallbackDevice != null) {
+ connectedDevices.add(a2dpFallbackDevice);
+ }
+ break;
+ case AudioManager.MODE_RINGTONE:
+ if (headsetFallbackDevice != null && headsetService.isInbandRingingEnabled()) {
+ connectedDevices.add(headsetFallbackDevice);
+ }
+ break;
+ default:
+ if (headsetFallbackDevice != null) {
+ connectedDevices.add(headsetFallbackDevice);
+ }
+ }
+ BluetoothDevice device = dbManager.getMostRecentlyConnectedDevicesInList(connectedDevices);
+ if (device != null) {
+ if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
+ if (Objects.equals(a2dpFallbackDevice, device)) {
+ if (DBG) {
+ Log.d(TAG, "set A2DP device active: " + device);
+ }
+ setA2dpActiveDevice(device);
+ if (headsetFallbackDevice != null) {
+ setHfpActiveDevice(device);
+ setLeAudioActiveDevice(null);
+ }
+ } else {
+ if (DBG) {
+ Log.d(TAG, "set LE audio device active: " + device);
+ }
+ setLeAudioActiveDevice(device);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ }
+ } else {
+ if (Objects.equals(headsetFallbackDevice, device)) {
+ if (DBG) {
+ Log.d(TAG, "set HFP device active: " + device);
+ }
+ setHfpActiveDevice(device);
+ if (a2dpFallbackDevice != null) {
+ setA2dpActiveDevice(a2dpFallbackDevice);
+ setLeAudioActiveDevice(null);
+ }
+ } else {
+ if (DBG) {
+ Log.d(TAG, "set LE audio device active: " + device);
+ }
+ setLeAudioActiveDevice(device);
+ setA2dpActiveDevice(null);
+ setHfpActiveDevice(null);
+ }
+ }
+ }
}
private void resetState() {
@@ -521,8 +857,15 @@ class ActiveDeviceManager {
mHfpConnectedDevices.clear();
mHfpActiveDevice = null;
+ mHearingAidConnectedDevices.clear();
mHearingAidActiveDevice = null;
+
+ mLeAudioConnectedDevices.clear();
mLeAudioActiveDevice = null;
+
+ mLeHearingAidConnectedDevices.clear();
+ mLeHearingAidActiveDevice = null;
+ mPendingLeHearingAidActiveDevice.clear();
}
@VisibleForTesting
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterApp.java b/android/app/src/com/android/bluetooth/btservice/AdapterApp.java
index 0c22c7cb5aa38db7c119d029a33e414f533ad394..99e3f4fc4cec6a28bb5b845e215a725c34d0d763 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterApp.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterApp.java
@@ -52,6 +52,11 @@ public class AdapterApp extends Application {
if (DBG) {
Log.d(TAG, "onCreate");
}
+ try {
+ DataMigration.run(this);
+ } catch (Exception e) {
+ Log.e(TAG, "Migration failure: ", e);
+ }
Config.init(this);
}
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 4018b48e0cee7394beaad2f97397a755cd6a81ec..c5fb78b1e6f396d83da8934df0352a3a9096364e 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -21,7 +21,6 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
-import static com.android.bluetooth.Utils.callerIsSystemOrActiveUser;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.Utils.enforceCdmAssociation;
import static com.android.bluetooth.Utils.enforceDumpPermission;
@@ -205,11 +204,11 @@ public class AdapterService extends Service {
static final String LOCAL_MAC_ADDRESS_PERM = android.Manifest.permission.LOCAL_MAC_ADDRESS;
static final String RECEIVE_MAP_PERM = android.Manifest.permission.RECEIVE_BLUETOOTH_MAP;
- private static final String PHONEBOOK_ACCESS_PERMISSION_PREFERENCE_FILE =
+ static final String PHONEBOOK_ACCESS_PERMISSION_PREFERENCE_FILE =
"phonebook_access_permission";
- private static final String MESSAGE_ACCESS_PERMISSION_PREFERENCE_FILE =
+ static final String MESSAGE_ACCESS_PERMISSION_PREFERENCE_FILE =
"message_access_permission";
- private static final String SIM_ACCESS_PERMISSION_PREFERENCE_FILE = "sim_access_permission";
+ static final String SIM_ACCESS_PERMISSION_PREFERENCE_FILE = "sim_access_permission";
private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30;
@@ -263,7 +262,8 @@ public class AdapterService extends Service {
}
private BluetoothAdapter mAdapter;
- private AdapterProperties mAdapterProperties;
+ @VisibleForTesting
+ AdapterProperties mAdapterProperties;
private AdapterState mAdapterStateMachine;
private BondStateMachine mBondStateMachine;
private JniCallbacks mJniCallbacks;
@@ -750,8 +750,9 @@ public class AdapterService extends Service {
nonSupportedProfiles.add(BassClientService.class);
}
- if (isLeAudioBroadcastSourceSupported()) {
- Config.addSupportedProfile(BluetoothProfile.LE_AUDIO_BROADCAST);
+ if (!isLeAudioBroadcastSourceSupported()) {
+ Config.updateSupportedProfileMask(
+ false, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST);
}
if (!nonSupportedProfiles.isEmpty()) {
@@ -1361,7 +1362,8 @@ public class AdapterService extends Service {
}
@BluetoothAdapter.RfcommListenerResult
- private int stopRfcommListener(ParcelUuid uuid, AttributionSource attributionSource) {
+ @VisibleForTesting
+ int stopRfcommListener(ParcelUuid uuid, AttributionSource attributionSource) {
RfcommListenerData listenerData = mBluetoothServerSockets.get(uuid.getUuid());
if (listenerData == null) {
@@ -1380,7 +1382,8 @@ public class AdapterService extends Service {
return listenerData.closeServerAndPendingSockets(mHandler);
}
- private IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
+ @VisibleForTesting
+ IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
ParcelUuid uuid, AttributionSource attributionSource) {
IncomingRfcommSocketInfo socketInfo = new IncomingRfcommSocketInfo();
@@ -1548,7 +1551,8 @@ public class AdapterService extends Service {
}
}
- private boolean isAvailable() {
+ @VisibleForTesting
+ boolean isAvailable() {
return !mCleaningUp;
}
@@ -1618,7 +1622,7 @@ public class AdapterService extends Service {
}
private boolean enable(boolean quietMode, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "enable")
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "enable")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService enable")) {
return false;
@@ -1637,7 +1641,7 @@ public class AdapterService extends Service {
}
private boolean disable(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "disable")
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "disable")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService disable")) {
return false;
@@ -1686,7 +1690,7 @@ public class AdapterService extends Service {
}
private List getUuids(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getUuids")
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getUuids")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getUuids")) {
return new ArrayList<>();
@@ -1709,7 +1713,8 @@ public class AdapterService extends Service {
}
public String getIdentityAddress(String address) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getIdentityAddress")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getIdentityAddress")
|| !Utils.checkConnectPermissionForDataDelivery(
service, Utils.getCallingAttributionSource(mService),
"AdapterService getIdentityAddress")) {
@@ -1729,7 +1734,7 @@ public class AdapterService extends Service {
}
private String getName(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getName")
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getName")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getName")) {
return null;
@@ -1749,7 +1754,9 @@ public class AdapterService extends Service {
}
private int getNameLengthForAdvertise(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getNameLengthForAdvertise")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "getNameLengthForAdvertise")
|| !Utils.checkAdvertisePermissionForDataDelivery(
service, attributionSource, TAG)) {
return -1;
@@ -1769,7 +1776,7 @@ public class AdapterService extends Service {
}
private boolean setName(String name, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "setName")
+ if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setName")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService setName")) {
return false;
@@ -1789,7 +1796,8 @@ public class AdapterService extends Service {
}
private BluetoothClass getBluetoothClass(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getBluetoothClass")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getBluetoothClass")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterSource getBluetoothClass")) {
return null;
@@ -1810,7 +1818,7 @@ public class AdapterService extends Service {
private boolean setBluetoothClass(BluetoothClass bluetoothClass, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setBluetoothClass")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -1837,7 +1845,8 @@ public class AdapterService extends Service {
}
private int getIoCapability(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getIoCapability")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getIoCapability")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getIoCapability")) {
return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
@@ -1858,7 +1867,7 @@ public class AdapterService extends Service {
private boolean setIoCapability(int capability, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setIoCapability")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -1883,7 +1892,8 @@ public class AdapterService extends Service {
}
private int getLeIoCapability(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getLeIoCapability")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getLeIoCapability")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getLeIoCapability")) {
return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
@@ -1904,7 +1914,7 @@ public class AdapterService extends Service {
private boolean setLeIoCapability(int capability, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setLeIoCapability")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -1949,7 +1959,8 @@ public class AdapterService extends Service {
}
private int setScanMode(int mode, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "setScanMode")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setScanMode")
|| !Utils.checkScanPermissionForDataDelivery(
service, attributionSource, "AdapterService setScanMode")) {
return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
@@ -1971,7 +1982,8 @@ public class AdapterService extends Service {
}
private long getDiscoverableTimeout(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getDiscoverableTimeout")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getDiscoverableTimeout")
|| !Utils.checkScanPermissionForDataDelivery(
service, attributionSource, "AdapterService getDiscoverableTimeout")) {
return -1;
@@ -1991,7 +2003,8 @@ public class AdapterService extends Service {
}
private int setDiscoverableTimeout(long timeout, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "setDiscoverableTimeout")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setDiscoverableTimeout")
|| !Utils.checkScanPermissionForDataDelivery(
service, attributionSource, "AdapterService setDiscoverableTimeout")) {
return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION;
@@ -2012,7 +2025,8 @@ public class AdapterService extends Service {
}
private boolean startDiscovery(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "startDiscovery")) {
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "startDiscovery")) {
return false;
}
@@ -2034,7 +2048,8 @@ public class AdapterService extends Service {
}
private boolean cancelDiscovery(AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "cancelDiscovery")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "cancelDiscovery")
|| !Utils.checkScanPermissionForDataDelivery(
service, attributionSource, "AdapterService cancelDiscovery")) {
return false;
@@ -2076,7 +2091,7 @@ public class AdapterService extends Service {
private long getDiscoveryEndMillis(AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getDiscoveryEndMillis")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return -1;
}
@@ -2210,7 +2225,8 @@ public class AdapterService extends Service {
private boolean cancelBondProcess(
BluetoothDevice device, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "cancelBondProcess")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "cancelBondProcess")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService cancelBondProcess")) {
return false;
@@ -2237,7 +2253,8 @@ public class AdapterService extends Service {
}
private boolean removeBond(BluetoothDevice device, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "removeBond")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "removeBond")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService removeBond")) {
return false;
@@ -2312,7 +2329,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "generateLocalOobData")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return;
}
@@ -2403,7 +2420,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "removeActiveDevice")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2423,7 +2440,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setActiveDevice")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2446,7 +2463,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getActiveDevices")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return new ArrayList<>();
}
@@ -2471,7 +2488,7 @@ public class AdapterService extends Service {
if (service == null) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (!callerIsSystemOrActiveUser(TAG, "connectAllEnabledProfiles")) {
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "connectAllEnabledProfiles")) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
}
if (device == null) {
@@ -2510,7 +2527,8 @@ public class AdapterService extends Service {
if (service == null) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (!callerIsSystemOrActiveUser(TAG, "disconnectAllEnabledProfiles")) {
+ if (!callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "disconnectAllEnabledProfiles")) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
}
if (device == null) {
@@ -2625,7 +2643,7 @@ public class AdapterService extends Service {
if (service == null) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (!callerIsSystemOrActiveUser(TAG, "setRemoteAlias")) {
+ if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setRemoteAlias")) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
}
if (name != null && name.isEmpty()) {
@@ -2744,7 +2762,8 @@ public class AdapterService extends Service {
private boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode,
AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "setPin")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPin")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService setPin")) {
return false;
@@ -2779,7 +2798,8 @@ public class AdapterService extends Service {
private boolean setPasskey(BluetoothDevice device, boolean accept, int len, byte[] passkey,
AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "setPasskey")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPasskey")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService setPasskey")) {
return false;
@@ -2815,7 +2835,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setPairingConfirmation")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2846,7 +2866,7 @@ public class AdapterService extends Service {
private boolean getSilenceMode(BluetoothDevice device, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getSilenceMode")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2869,7 +2889,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setSilenceMode")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2892,7 +2912,9 @@ public class AdapterService extends Service {
private int getPhonebookAccessPermission(
BluetoothDevice device, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getPhonebookAccessPermission")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(
+ service, TAG, "getPhonebookAccessPermission")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getPhonebookAccessPermission")) {
return BluetoothDevice.ACCESS_UNKNOWN;
@@ -2914,7 +2936,8 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "setPhonebookAccessPermission")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2937,7 +2960,9 @@ public class AdapterService extends Service {
private int getMessageAccessPermission(
BluetoothDevice device, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getMessageAccessPermission")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "getMessageAccessPermission")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getMessageAccessPermission")) {
return BluetoothDevice.ACCESS_UNKNOWN;
@@ -2959,7 +2984,8 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "setMessageAccessPermission")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -2982,7 +3008,9 @@ public class AdapterService extends Service {
private int getSimAccessPermission(
BluetoothDevice device, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getSimAccessPermission")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "getSimAccessPermission")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getSimAccessPermission")) {
return BluetoothDevice.ACCESS_UNKNOWN;
@@ -3004,7 +3032,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setSimAccessPermission")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -3037,7 +3065,8 @@ public class AdapterService extends Service {
private boolean sdpSearch(
BluetoothDevice device, ParcelUuid uuid, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "sdpSearch")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "sdpSearch")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService sdpSearch")) {
return false;
@@ -3061,7 +3090,8 @@ public class AdapterService extends Service {
}
private int getBatteryLevel(BluetoothDevice device, AttributionSource attributionSource) {
AdapterService service = getService();
- if (service == null || !callerIsSystemOrActiveUser(TAG, "getBatteryLevel")
+ if (service == null
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getBatteryLevel")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService getBatteryLevel")) {
return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -3157,7 +3187,8 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "registerBluetoothConnectionCallback")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -3179,7 +3210,8 @@ public class AdapterService extends Service {
IBluetoothConnectionCallback callback, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "unregisterBluetoothConnectionCallback")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -3201,7 +3233,7 @@ public class AdapterService extends Service {
void registerCallback(IBluetoothCallback callback, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "registerCallback")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return;
}
@@ -3225,7 +3257,7 @@ public class AdapterService extends Service {
void unregisterCallback(IBluetoothCallback callback, AttributionSource source) {
AdapterService service = getService();
if (service == null || service.mCallbacks == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "unregisterCallback")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return;
}
@@ -3400,7 +3432,8 @@ public class AdapterService extends Service {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (service.isLeAudioBroadcastSourceSupported()) {
+ long supportBitMask = Config.getSupportedProfilesBitMask();
+ if ((supportBitMask & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) {
return BluetoothStatusCodes.FEATURE_SUPPORTED;
}
@@ -3500,7 +3533,8 @@ public class AdapterService extends Service {
BluetoothDevice device, AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "registerMetadataListener")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -3535,7 +3569,8 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "unregisterMetadataListener")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -3564,7 +3599,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "setMetadata")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return false;
}
@@ -3590,7 +3625,7 @@ public class AdapterService extends Service {
AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "getMetadata")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return null;
}
@@ -3624,7 +3659,7 @@ public class AdapterService extends Service {
void onLeServiceUp(AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "onLeServiceUp")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return;
}
@@ -3647,7 +3682,7 @@ public class AdapterService extends Service {
void onBrEdrDown(AttributionSource source) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "onBrEdrDown")
|| !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
return;
}
@@ -3687,7 +3722,7 @@ public class AdapterService extends Service {
private boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "allowLowLatencyAudio")
|| !Utils.checkConnectPermissionForDataDelivery(
service, Utils.getCallingAttributionSource(service),
"AdapterService allowLowLatencyAudio")) {
@@ -3717,7 +3752,7 @@ public class AdapterService extends Service {
AttributionSource attributionSource) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "startRfcommListener")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService startRfcommListener")) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
@@ -3742,7 +3777,7 @@ public class AdapterService extends Service {
private int stopRfcommListener(ParcelUuid uuid, AttributionSource attributionSource) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service, TAG, "stopRfcommListener")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService stopRfcommListener")) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
@@ -3768,7 +3803,8 @@ public class AdapterService extends Service {
ParcelUuid uuid, AttributionSource attributionSource) {
AdapterService service = getService();
if (service == null
- || !Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !callerIsSystemOrActiveOrManagedUser(service,
+ TAG, "retrievePendingSocketForServiceRecord")
|| !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource,
"AdapterService retrievePendingSocketForServiceRecord")) {
@@ -3832,7 +3868,8 @@ public class AdapterService extends Service {
return mAdapterProperties.getName().length();
}
- private static boolean isValidIoCapability(int capability) {
+ @VisibleForTesting
+ static boolean isValidIoCapability(int capability) {
if (capability < 0 || capability >= BluetoothAdapter.IO_CAPABILITY_MAX) {
Log.e(TAG, "Invalid IO capability value - " + capability);
return false;
@@ -4232,8 +4269,8 @@ public class AdapterService extends Service {
Log.e(TAG, "getActiveDevices: LeAudioService is null");
} else {
activeDevices = mLeAudioService.getActiveDevices();
- Log.i(TAG, "getActiveDevices: LeAudio devices: Out["
- + activeDevices.get(0) + "] - In[" + activeDevices.get(1) + "]");
+ Log.i(TAG, "getActiveDevices: LeAudio devices: Lead["
+ + activeDevices.get(0) + "] - member_1[" + activeDevices.get(1) + "]");
}
break;
default:
@@ -4386,95 +4423,131 @@ public class AdapterService extends Service {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (mA2dpService != null && mA2dpService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mA2dpService != null && (mA2dpService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mA2dpService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting A2dp");
mA2dpService.disconnect(device);
}
- if (mA2dpSinkService != null && mA2dpSinkService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mA2dpSinkService != null && (mA2dpSinkService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mA2dpSinkService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting A2dp Sink");
mA2dpSinkService.disconnect(device);
}
- if (mHeadsetService != null && mHeadsetService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mHeadsetService != null && (mHeadsetService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mHeadsetService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG,
"disconnectAllEnabledProfiles: Disconnecting Headset Profile");
mHeadsetService.disconnect(device);
}
- if (mHeadsetClientService != null && mHeadsetClientService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mHeadsetClientService != null && (mHeadsetClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mHeadsetClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting HFP");
mHeadsetClientService.disconnect(device);
}
- if (mMapClientService != null && mMapClientService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mMapClientService != null && (mMapClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mMapClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting MAP Client");
mMapClientService.disconnect(device);
}
- if (mMapService != null && mMapService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mMapService != null && (mMapService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mMapService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting MAP");
mMapService.disconnect(device);
}
- if (mHidDeviceService != null && mHidDeviceService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mHidDeviceService != null && (mHidDeviceService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mHidDeviceService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Hid Device Profile");
mHidDeviceService.disconnect(device);
}
- if (mHidHostService != null && mHidHostService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mHidHostService != null && (mHidHostService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mHidHostService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Hid Host Profile");
mHidHostService.disconnect(device);
}
- if (mPanService != null && mPanService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mPanService != null && (mPanService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mPanService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Pan Profile");
mPanService.disconnect(device);
}
- if (mPbapClientService != null && mPbapClientService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mPbapClientService != null && (mPbapClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mPbapClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Pbap Client");
mPbapClientService.disconnect(device);
}
- if (mPbapService != null && mPbapService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mPbapService != null && (mPbapService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mPbapService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Pbap Server");
mPbapService.disconnect(device);
}
- if (mHearingAidService != null && mHearingAidService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mHearingAidService != null && (mHearingAidService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mHearingAidService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Hearing Aid Profile");
mHearingAidService.disconnect(device);
}
- if (mHapClientService != null && mHapClientService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mHapClientService != null && (mHapClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mHapClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Hearing Access Profile Client");
mHapClientService.disconnect(device);
}
- if (mVolumeControlService != null && mVolumeControlService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mVolumeControlService != null && (mVolumeControlService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mVolumeControlService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Volume Control Profile");
mVolumeControlService.disconnect(device);
}
- if (mSapService != null && mSapService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mSapService != null && (mSapService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mSapService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Sap Profile");
mSapService.disconnect(device);
}
if (mCsipSetCoordinatorService != null
- && mCsipSetCoordinatorService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ && (mCsipSetCoordinatorService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mCsipSetCoordinatorService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting Coordinater Set Profile");
mCsipSetCoordinatorService.disconnect(device);
}
- if (mLeAudioService != null && mLeAudioService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mLeAudioService != null && (mLeAudioService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mLeAudioService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting LeAudio profile (BAP)");
mLeAudioService.disconnect(device);
}
- if (mBassClientService != null && mBassClientService.getConnectionState(device)
- == BluetoothProfile.STATE_CONNECTED) {
+ if (mBassClientService != null && (mBassClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED
+ || mBassClientService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTING)) {
Log.i(TAG, "disconnectAllEnabledProfiles: Disconnecting "
+ "LE Broadcast Assistant Profile");
mBassClientService.disconnect(device);
@@ -4577,6 +4650,8 @@ public class AdapterService extends Service {
return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS;
case /*HCI_ERR_PEER_USER*/ 0x13:
return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST;
+ case /*HCI_ERR_REMOTE_POWER_OFF*/ 0x15:
+ return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST;
case /*HCI_ERR_CONN_CAUSE_LOCAL_HOST*/ 0x16:
return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST;
case /*HCI_ERR_UNSUPPORTED_REM_FEATURE*/ 0x1A:
@@ -4673,8 +4748,7 @@ public class AdapterService extends Service {
* @return true, if the LE audio broadcast source is supported
*/
public boolean isLeAudioBroadcastSourceSupported() {
- return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)
- && mAdapterProperties.isLePeriodicAdvertisingSupported()
+ return mAdapterProperties.isLePeriodicAdvertisingSupported()
&& mAdapterProperties.isLeExtendedAdvertisingSupported()
&& mAdapterProperties.isLeIsochronousBroadcasterSupported();
}
@@ -4691,6 +4765,10 @@ public class AdapterService extends Service {
|| mAdapterProperties.isLePeriodicAdvertisingSyncTransferRecipientSupported());
}
+ public long getSupportedProfilesBitMask() {
+ return Config.getSupportedProfilesBitMask();
+ }
+
/**
* Check if the LE audio CIS central feature is supported.
*
@@ -4722,7 +4800,8 @@ public class AdapterService extends Service {
return mAdapterProperties.isA2dpOffloadEnabled();
}
- private BluetoothActivityEnergyInfo reportActivityInfo() {
+ @VisibleForTesting
+ BluetoothActivityEnergyInfo reportActivityInfo() {
if (mAdapterProperties.getState() != BluetoothAdapter.STATE_ON
|| !mAdapterProperties.isActivityAndEnergyReportingSupported()) {
return null;
@@ -4938,6 +5017,12 @@ public class AdapterService extends Service {
@VisibleForTesting
public void metadataChanged(String address, int key, byte[] value) {
BluetoothDevice device = mRemoteDevices.getDevice(Utils.getBytesFromAddress(address));
+
+ // pass just interesting metadata to native, to reduce spam
+ if (key == BluetoothDevice.METADATA_LE_AUDIO) {
+ metadataChangedNative(Utils.getBytesFromAddress(address), key, value);
+ }
+
if (mMetadataListeners.containsKey(device)) {
ArrayList list = mMetadataListeners.get(device);
for (IBluetoothMetadataListener listener : list) {
@@ -5086,105 +5171,16 @@ public class AdapterService extends Service {
}
}
- // Boolean flags
- private static final String GD_CORE_FLAG = "INIT_gd_core";
- private static final String GD_ADVERTISING_FLAG = "INIT_gd_advertising";
- private static final String GD_SCANNING_FLAG = "INIT_gd_scanning";
- private static final String GD_HCI_FLAG = "INIT_gd_hci";
- private static final String GD_CONTROLLER_FLAG = "INIT_gd_controller";
- private static final String GD_ACL_FLAG = "INIT_gd_acl";
- private static final String GD_L2CAP_FLAG = "INIT_gd_l2cap";
- private static final String GD_RUST_FLAG = "INIT_gd_rust";
- private static final String GD_LINK_POLICY_FLAG = "INIT_gd_link_policy";
- private static final String GATT_ROBUST_CACHING_CLIENT_FLAG = "INIT_gatt_robust_caching_client";
- private static final String GATT_ROBUST_CACHING_SERVER_FLAG = "INIT_gatt_robust_caching_server";
- private static final String IRK_ROTATION_FLAG = "INIT_irk_rotation";
- private static final String PASS_PHY_UPDATE_CALLBACK_FLAG = "INIT_pass_phy_update_callback";
-
- /**
- * Logging flags logic (only applies to DEBUG and VERBOSE levels):
- * if LOG_TAG in LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG:
- * DO NOT LOG
- * else if LOG_TAG in LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG:
- * DO LOG
- * else if LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG:
- * DO LOG
- * else:
- * DO NOT LOG
- */
- private static final String LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG =
- "INIT_logging_debug_enabled_for_all";
- // String flags
- // Comma separated tags
- private static final String LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG =
- "INIT_logging_debug_enabled_for_tags";
- private static final String LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG =
- "INIT_logging_debug_disabled_for_tags";
- private static final String BTAA_HCI_LOG_FLAG = "INIT_btaa_hci";
-
+ @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG)
private String[] getInitFlags() {
+ final DeviceConfig.Properties properties =
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BLUETOOTH);
ArrayList initFlags = new ArrayList<>();
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_CORE_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_CORE_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_ADVERTISING_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_ADVERTISING_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_SCANNING_FLAG,
- Config.isGdEnabledUpToScanningLayer())) {
- initFlags.add(String.format("%s=%s", GD_SCANNING_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_HCI_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_HCI_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_CONTROLLER_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_CONTROLLER_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_ACL_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_ACL_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_L2CAP_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_L2CAP_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_RUST_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_RUST_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_LINK_POLICY_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GD_LINK_POLICY_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
- GATT_ROBUST_CACHING_CLIENT_FLAG, true)) {
- initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_CLIENT_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
- GATT_ROBUST_CACHING_SERVER_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_SERVER_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, IRK_ROTATION_FLAG, false)) {
- initFlags.add(String.format("%s=%s", IRK_ROTATION_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_BLUETOOTH, PASS_PHY_UPDATE_CALLBACK_FLAG, true)) {
- initFlags.add(String.format("%s=%s", PASS_PHY_UPDATE_CALLBACK_FLAG, "true"));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
- LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, false)) {
- initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, "true"));
- }
- String debugLoggingEnabledTags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BLUETOOTH,
- LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG, "");
- if (!debugLoggingEnabledTags.isEmpty()) {
- initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG,
- debugLoggingEnabledTags));
- }
- String debugLoggingDisabledTags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BLUETOOTH,
- LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG, "");
- if (!debugLoggingDisabledTags.isEmpty()) {
- initFlags.add(String.format("%s=%s", LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG,
- debugLoggingDisabledTags));
- }
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, BTAA_HCI_LOG_FLAG, true)) {
- initFlags.add(String.format("%s=%s", BTAA_HCI_LOG_FLAG, "true"));
+ for (String property: properties.getKeyset()) {
+ if (property.startsWith("INIT_")) {
+ initFlags.add(String.format("%s=%s", property,
+ properties.getString(property, null)));
+ }
}
return initFlags.toArray(new String[0]);
}
@@ -5575,6 +5571,8 @@ public class AdapterService extends Service {
private native boolean allowLowLatencyAudioNative(boolean allowed, byte[] address);
+ private native void metadataChangedNative(byte[] address, int key, byte[] value);
+
// Returns if this is a mock object. This is currently used in testing so that we may not call
// System.exit() while finalizing the object. Otherwise GC of mock objects unfortunately ends up
// calling finalize() which in turn calls System.exit() and the process crashes.
diff --git a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
index d62d7ba06d434422979938a4da776eec3e3c037a..713545fbb21447c17999cbcff10bf50f265e7414 100644
--- a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -28,6 +28,7 @@ import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.OobData;
import android.content.Intent;
import android.os.Build;
+import android.os.Bundle;
import android.os.Message;
import android.os.UserHandle;
import android.util.Log;
@@ -48,6 +49,7 @@ import com.android.internal.util.StateMachine;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
/**
@@ -86,6 +88,7 @@ final class BondStateMachine extends StateMachine {
public static final String OOBDATAP192 = "oobdatap192";
public static final String OOBDATAP256 = "oobdatap256";
+ public static final String DISPLAY_PASSKEY = "display_passkey";
@VisibleForTesting Set mPendingBondedDevices = new HashSet<>();
@@ -249,7 +252,14 @@ final class BondStateMachine extends StateMachine {
case SSP_REQUEST:
int passkey = msg.arg1;
int variant = msg.arg2;
- sendDisplayPinIntent(devProp.getAddress(), passkey, variant);
+ boolean displayPasskey =
+ (msg.getData() != null)
+ ? msg.getData().getByte(DISPLAY_PASSKEY) == 1 /* 1 == true */
+ : false;
+ sendDisplayPinIntent(
+ devProp.getAddress(),
+ displayPasskey ? Optional.of(passkey) : Optional.empty(),
+ variant);
break;
case PIN_REQUEST:
BluetoothClass btClass = dev.getBluetoothClass();
@@ -264,18 +274,24 @@ final class BondStateMachine extends StateMachine {
// Generate a variable 6-digit PIN in range of 100000-999999
// This is not truly random but good enough.
int pin = 100000 + (int) Math.floor((Math.random() * (999999 - 100000)));
- sendDisplayPinIntent(devProp.getAddress(), pin,
+ sendDisplayPinIntent(
+ devProp.getAddress(),
+ Optional.of(pin),
BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
break;
}
if (msg.arg2 == 1) { // Minimum 16 digit pin required here
- sendDisplayPinIntent(devProp.getAddress(), 0,
+ sendDisplayPinIntent(
+ devProp.getAddress(),
+ Optional.empty(),
BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS);
} else {
// In PIN_REQUEST, there is no passkey to display.So do not send the
- // EXTRA_PAIRING_KEY type in the intent( 0 in SendDisplayPinIntent() )
- sendDisplayPinIntent(devProp.getAddress(), 0,
+ // EXTRA_PAIRING_KEY type in the intent
+ sendDisplayPinIntent(
+ devProp.getAddress(),
+ Optional.empty(),
BluetoothDevice.PAIRING_VARIANT_PIN);
}
break;
@@ -326,9 +342,19 @@ final class BondStateMachine extends StateMachine {
boolean result;
// If we have some data
if (remoteP192Data != null || remoteP256Data != null) {
+ BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
+ mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
+ BluetoothDevice.BOND_BONDING,
+ BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING_OOB,
+ BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
result = mAdapterService.createBondOutOfBandNative(addr, transport,
remoteP192Data, remoteP256Data);
} else {
+ BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
+ mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
+ BluetoothDevice.BOND_BONDING,
+ BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING,
+ BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
result = mAdapterService.createBondNative(addr, transport);
}
BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_DEVICE_NAME_REPORTED,
@@ -358,12 +384,10 @@ final class BondStateMachine extends StateMachine {
return false;
}
- private void sendDisplayPinIntent(byte[] address, int pin, int variant) {
+ private void sendDisplayPinIntent(byte[] address, Optional maybePin, int variant) {
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address));
- if (pin != 0) {
- intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin);
- }
+ maybePin.ifPresent(pin -> intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin));
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// Workaround for Android Auto until pre-accepting pairing requests is added.
@@ -531,6 +555,9 @@ final class BondStateMachine extends StateMachine {
msg.obj = device;
if (displayPasskey) {
msg.arg1 = passkey;
+ Bundle bundle = new Bundle();
+ bundle.putByte(BondStateMachine.DISPLAY_PASSKEY, (byte) 1 /* true */);
+ msg.setData(bundle);
}
msg.arg2 = variant;
sendMessage(msg);
diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java
index 1c021591bbb29501c65fb1f4c50fdf936c5e802d..c1493294a8bf92db7e1d82d0cd51eb9191eba4a5 100644
--- a/android/app/src/com/android/bluetooth/btservice/Config.java
+++ b/android/app/src/com/android/bluetooth/btservice/Config.java
@@ -60,10 +60,11 @@ public class Config {
private static final String FEATURE_HEARING_AID = "settings_bluetooth_hearing_aid";
private static final String FEATURE_BATTERY = "settings_bluetooth_battery";
- private static long sSupportedMask = 0;
private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY =
"ro.bluetooth.leaudio_switcher.supported";
+ private static final String LE_AUDIO_BROADCAST_DYNAMIC_SWITCH_PROPERTY =
+ "ro.bluetooth.leaudio_broadcast_switcher.supported";
private static final String LE_AUDIO_DYNAMIC_ENABLED_PROPERTY =
"persist.bluetooth.leaudio_switcher.enabled";
@@ -165,6 +166,11 @@ public class Config {
private static boolean sIsGdEnabledUptoScanningLayer = false;
static void init(Context ctx) {
+ if (LeAudioService.isBroadcastEnabled()) {
+ updateSupportedProfileMask(
+ true, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST);
+ }
+
final boolean leAudioDynamicSwitchSupported =
SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false);
@@ -205,6 +211,15 @@ public class Config {
setProfileEnabled(TbsService.class, enable);
setProfileEnabled(McpService.class, enable);
setProfileEnabled(VolumeControlService.class, enable);
+
+ final boolean broadcastDynamicSwitchSupported =
+ SystemProperties.getBoolean(LE_AUDIO_BROADCAST_DYNAMIC_SWITCH_PROPERTY, false);
+
+ if (broadcastDynamicSwitchSupported) {
+ setProfileEnabled(BassClientService.class, enable);
+ updateSupportedProfileMask(
+ enable, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST);
+ }
}
/**
@@ -226,8 +241,17 @@ public class Config {
sSupportedProfiles = profilesList.toArray(new Class[profilesList.size()]);
}
- static void addSupportedProfile(int supportedProfile) {
- sSupportedMask |= (1 << supportedProfile);
+ static void updateSupportedProfileMask(Boolean enable, Class profile, int supportedProfile) {
+ for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
+ if (config.mClass == profile) {
+ if (enable) {
+ config.mMask |= 1 << supportedProfile;
+ } else {
+ config.mMask &= ~(1 << supportedProfile);
+ }
+ return;
+ }
+ }
}
static HashSet geLeAudioUnicastProfiles() {
@@ -253,7 +277,7 @@ public class Config {
}
static long getSupportedProfilesBitMask() {
- long mask = sSupportedMask;
+ long mask = 0;
for (final Class profileClass : getSupportedProfiles()) {
mask |= getProfileMask(profileClass);
}
diff --git a/android/app/src/com/android/bluetooth/btservice/DataMigration.java b/android/app/src/com/android/bluetooth/btservice/DataMigration.java
new file mode 100644
index 0000000000000000000000000000000000000000..4846d606cfbb49db4beef0b939aff2eb5f3a69a8
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/btservice/DataMigration.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2022 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.btservice;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.bluetooth.btservice.storage.BluetoothDatabaseMigration;
+import com.android.bluetooth.opp.BluetoothOppProvider;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+final class DataMigration {
+ private DataMigration(){}
+ private static final String TAG = "DataMigration";
+
+ @VisibleForTesting
+ static final String AUTHORITY = "bluetooth_legacy.provider";
+
+ @VisibleForTesting
+ static final String START_MIGRATION_CALL = "start_legacy_migration";
+ @VisibleForTesting
+ static final String FINISH_MIGRATION_CALL = "finish_legacy_migration";
+
+ @VisibleForTesting
+ static final String BLUETOOTH_DATABASE = "bluetooth_db";
+ @VisibleForTesting
+ static final String OPP_DATABASE = "btopp.db";
+
+ // AvrcpVolumeManager.VOLUME_MAP
+ private static final String VOLUME_MAP_PREFERENCE_FILE = "bluetooth_volume_map";
+ // com.android.blueotooth.opp.Constants.BLUETOOTHOPP_CHANNEL_PREFERENCE
+ private static final String BLUETOOTHOPP_CHANNEL_PREFERENCE = "btopp_channels";
+
+ // com.android.blueotooth.opp.Constants.BLUETOOTHOPP_NAME_PREFERENCE
+ private static final String BLUETOOTHOPP_NAME_PREFERENCE = "btopp_names";
+
+ // com.android.blueotooth.opp.OPP_PREFERENCE_FILE
+ private static final String OPP_PREFERENCE_FILE = "OPPMGR";
+
+ @VisibleForTesting
+ static final String[] sharedPreferencesKeys = {
+ // Bundles of Boolean
+ AdapterService.PHONEBOOK_ACCESS_PERMISSION_PREFERENCE_FILE,
+ AdapterService.MESSAGE_ACCESS_PERMISSION_PREFERENCE_FILE,
+ AdapterService.SIM_ACCESS_PERMISSION_PREFERENCE_FILE,
+
+ // Bundles of Integer
+ VOLUME_MAP_PREFERENCE_FILE,
+ BLUETOOTHOPP_CHANNEL_PREFERENCE,
+
+ // Bundles of String
+ BLUETOOTHOPP_NAME_PREFERENCE,
+
+ // Bundle of Boolean and String
+ OPP_PREFERENCE_FILE,
+ };
+
+ // Main key use for storing all the key in the associate bundle
+ @VisibleForTesting
+ static final String KEY_LIST = "key_list";
+
+ @VisibleForTesting
+ static final String BLUETOOTH_CONFIG = "bluetooth_config";
+ static final String MIGRATION_DONE_PROPERTY = "migration_done";
+ @VisibleForTesting
+ static final String MIGRATION_ATTEMPT_PROPERTY = "migration_attempt";
+
+ @VisibleForTesting
+ public static final int MIGRATION_STATUS_TO_BE_DONE = 0;
+ @VisibleForTesting
+ public static final int MIGRATION_STATUS_COMPLETED = 1;
+ @VisibleForTesting
+ public static final int MIGRATION_STATUS_MISSING_APK = 2;
+ @VisibleForTesting
+ public static final int MIGRATION_STATUS_MAX_ATTEMPT = 3;
+
+ @VisibleForTesting
+ static final int MAX_ATTEMPT = 3;
+
+ static int run(Context ctx) {
+ if (migrationStatus(ctx) == MIGRATION_STATUS_COMPLETED) {
+ Log.d(TAG, "Legacy migration skiped: already completed");
+ return MIGRATION_STATUS_COMPLETED;
+ }
+ if (!isMigrationApkInstalled(ctx)) {
+ Log.d(TAG, "Legacy migration skiped: no migration app installed");
+ markMigrationStatus(ctx, MIGRATION_STATUS_MISSING_APK);
+ return MIGRATION_STATUS_MISSING_APK;
+ }
+ if (!incrementeMigrationAttempt(ctx)) {
+ Log.d(TAG, "Legacy migration skiped: still failing after too many attempt");
+ markMigrationStatus(ctx, MIGRATION_STATUS_MAX_ATTEMPT);
+ return MIGRATION_STATUS_MAX_ATTEMPT;
+ }
+
+ for (String pref: sharedPreferencesKeys) {
+ sharedPreferencesMigration(pref, ctx);
+ }
+ // Migration for DefaultSharedPreferences used in PbapUtils. Contains Long
+ sharedPreferencesMigration(ctx.getPackageName() + "_preferences", ctx);
+
+ bluetoothDatabaseMigration(ctx);
+ oppDatabaseMigration(ctx);
+
+ markMigrationStatus(ctx, MIGRATION_STATUS_COMPLETED);
+ Log.d(TAG, "Legacy migration completed");
+ return MIGRATION_STATUS_COMPLETED;
+ }
+
+ @VisibleForTesting
+ static boolean bluetoothDatabaseMigration(Context ctx) {
+ final String logHeader = BLUETOOTH_DATABASE + ": ";
+ ContentResolver resolver = ctx.getContentResolver();
+ Cursor cursor = resolver.query(
+ Uri.parse("content://" + AUTHORITY + "/" + BLUETOOTH_DATABASE),
+ null, null, null, null);
+ if (cursor == null) {
+ Log.d(TAG, logHeader + "Nothing to migrate");
+ return true;
+ }
+ boolean status = BluetoothDatabaseMigration.run(ctx, cursor);
+ cursor.close();
+ if (status) {
+ resolver.call(AUTHORITY, FINISH_MIGRATION_CALL, BLUETOOTH_DATABASE, null);
+ Log.d(TAG, logHeader + "Migration complete. File is deleted");
+ } else {
+ Log.e(TAG, logHeader + "Invalid data. Incomplete migration. File is not deleted");
+ }
+ return status;
+ }
+
+ @VisibleForTesting
+ static boolean oppDatabaseMigration(Context ctx) {
+ final String logHeader = OPP_DATABASE + ": ";
+ ContentResolver resolver = ctx.getContentResolver();
+ Cursor cursor = resolver.query(
+ Uri.parse("content://" + AUTHORITY + "/" + OPP_DATABASE),
+ null, null, null, null);
+ if (cursor == null) {
+ Log.d(TAG, logHeader + "Nothing to migrate");
+ return true;
+ }
+ boolean status = BluetoothOppProvider.oppDatabaseMigration(ctx, cursor);
+ cursor.close();
+ if (status) {
+ resolver.call(AUTHORITY, FINISH_MIGRATION_CALL, OPP_DATABASE, null);
+ Log.d(TAG, logHeader + "Migration complete. File is deleted");
+ } else {
+ Log.e(TAG, logHeader + "Invalid data. Incomplete migration. File is not deleted");
+ }
+ return status;
+ }
+
+ private static boolean writeObjectToEditor(SharedPreferences.Editor editor, Bundle b,
+ String itemKey) {
+ Object value = b.get(itemKey);
+ if (value == null) {
+ Log.e(TAG, itemKey + ": No value associated with this itemKey");
+ return false;
+ }
+ if (value instanceof Boolean) {
+ editor.putBoolean(itemKey, (Boolean) value);
+ } else if (value instanceof Integer) {
+ editor.putInt(itemKey, (Integer) value);
+ } else if (value instanceof Long) {
+ editor.putLong(itemKey, (Long) value);
+ } else if (value instanceof String) {
+ editor.putString(itemKey, (String) value);
+ } else {
+ Log.e(TAG, itemKey + ": Failed to migrate: "
+ + value.getClass().getSimpleName() + ": Data type not handled");
+ return false;
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ static boolean sharedPreferencesMigration(String prefKey, Context ctx) {
+ final String logHeader = "SharedPreferencesMigration - " + prefKey + ": ";
+ ContentResolver resolver = ctx.getContentResolver();
+ Bundle b = resolver.call(AUTHORITY, START_MIGRATION_CALL, prefKey, null);
+ if (b == null) {
+ Log.d(TAG, logHeader + "Nothing to migrate");
+ return true;
+ }
+ List keys = b.getStringArrayList(KEY_LIST);
+ if (keys == null) {
+ Log.e(TAG, logHeader + "Wrong format of bundle: No keys to migrate");
+ return false;
+ }
+ SharedPreferences pref = ctx.getSharedPreferences(prefKey, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = pref.edit();
+ boolean status = true;
+ for (String itemKey : keys) {
+ // prevent overriding any user settings if it's a new attempt
+ if (!pref.contains(itemKey)) {
+ status &= writeObjectToEditor(editor, b, itemKey);
+ } else {
+ Log.d(TAG, logHeader + itemKey + ": Already exists, not overriding data.");
+ }
+ }
+ editor.apply();
+ if (status) {
+ resolver.call(AUTHORITY, FINISH_MIGRATION_CALL, prefKey, null);
+ Log.d(TAG, logHeader + "Migration complete. File is deleted");
+ } else {
+ Log.e(TAG, logHeader + "Invalid data. Incomplete migration. File is not deleted");
+ }
+ return status;
+ }
+
+ @VisibleForTesting
+ static int migrationStatus(Context ctx) {
+ SharedPreferences pref = ctx.getSharedPreferences(BLUETOOTH_CONFIG, Context.MODE_PRIVATE);
+ return pref.getInt(MIGRATION_DONE_PROPERTY, MIGRATION_STATUS_TO_BE_DONE);
+ }
+
+ @VisibleForTesting
+ static boolean incrementeMigrationAttempt(Context ctx) {
+ SharedPreferences pref = ctx.getSharedPreferences(BLUETOOTH_CONFIG, Context.MODE_PRIVATE);
+ int currentAttempt = Math.min(pref.getInt(MIGRATION_ATTEMPT_PROPERTY, 0), MAX_ATTEMPT);
+ pref.edit()
+ .putInt(MIGRATION_ATTEMPT_PROPERTY, currentAttempt + 1)
+ .apply();
+ return currentAttempt < MAX_ATTEMPT;
+ }
+
+ @VisibleForTesting
+ static boolean isMigrationApkInstalled(Context ctx) {
+ ContentResolver resolver = ctx.getContentResolver();
+ ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY);
+ if (client != null) {
+ client.close();
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ static void markMigrationStatus(Context ctx, int status) {
+ ctx.getSharedPreferences(BLUETOOTH_CONFIG, Context.MODE_PRIVATE)
+ .edit()
+ .putInt(MIGRATION_DONE_PROPERTY, status)
+ .apply();
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
index 12a4f8107c02124c4902aae6e13d0e49f2eea3a9..ecbac6062a117a85b7b70d70def058f958c3dff2 100644
--- a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
+++ b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
@@ -16,11 +16,7 @@
package com.android.bluetooth.btservice;
import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.SystemClock;
import android.util.Log;
@@ -39,11 +35,6 @@ public class MetricsLogger {
public static final boolean DEBUG = false;
- /**
- * Intent indicating Bluetooth counter metrics should send logs to BluetoothStatsLog
- */
- public static final String BLUETOOTH_COUNTER_METRICS_ACTION =
- "com.android.bluetooth.btservice.BLUETOOTH_COUNTER_METRICS_ACTION";
// 6 hours timeout for counter metrics
private static final long BLUETOOTH_COUNTER_METRICS_ACTION_DURATION_MILLIS = 6L * 3600L * 1000L;
@@ -56,16 +47,11 @@ public class MetricsLogger {
private boolean mInitialized = false;
static final private Object mLock = new Object();
- private BroadcastReceiver mDrainReceiver = new BroadcastReceiver() {
+ private AlarmManager.OnAlarmListener mOnAlarmListener = new AlarmManager.OnAlarmListener () {
@Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DEBUG) {
- Log.d(TAG, "onReceive: " + action);
- }
- if (action.equals(BLUETOOTH_COUNTER_METRICS_ACTION)) {
- drainBufferedCounters();
- }
+ public void onAlarm() {
+ drainBufferedCounters();
+ scheduleDrains();
}
};
@@ -90,14 +76,11 @@ public class MetricsLogger {
}
mInitialized = true;
mContext = context;
- IntentFilter filter = new IntentFilter();
- filter.addAction(BLUETOOTH_COUNTER_METRICS_ACTION);
- mContext.registerReceiver(mDrainReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
scheduleDrains();
return true;
}
- public boolean count(int key, long count) {
+ public boolean cacheCount(int key, long count) {
if (!mInitialized) {
Log.w(TAG, "MetricsLogger isn't initialized");
return false;
@@ -154,22 +137,30 @@ public class MetricsLogger {
}
protected void scheduleDrains() {
- if (DEBUG) {
- Log.d(TAG, "setCounterMetricsAlarm()");
- }
+ Log.i(TAG, "setCounterMetricsAlarm()");
if (mAlarmManager == null) {
mAlarmManager = mContext.getSystemService(AlarmManager.class);
}
- mAlarmManager.setRepeating(
+ mAlarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime(),
- BLUETOOTH_COUNTER_METRICS_ACTION_DURATION_MILLIS,
- getDrainIntent());
+ SystemClock.elapsedRealtime() + BLUETOOTH_COUNTER_METRICS_ACTION_DURATION_MILLIS,
+ TAG,
+ mOnAlarmListener,
+ null);
}
- protected void writeCounter(int key, long count) {
+ public boolean count(int key, long count) {
+ if (!mInitialized) {
+ Log.w(TAG, "MetricsLogger isn't initialized");
+ return false;
+ }
+ if (count <= 0) {
+ Log.w(TAG, "count is not larger than 0. count: " + count + " key: " + key);
+ return false;
+ }
BluetoothStatsLog.write(
BluetoothStatsLog.BLUETOOTH_CODE_PATH_COUNTER, key, count);
+ return true;
}
protected void drainBufferedCounters() {
@@ -177,7 +168,7 @@ public class MetricsLogger {
synchronized (mLock) {
// send mCounters to statsd
for (int key : mCounters.keySet()) {
- writeCounter(key, mCounters.get(key));
+ count(key, mCounters.get(key));
}
mCounters.clear();
}
@@ -198,15 +189,6 @@ public class MetricsLogger {
return true;
}
protected void cancelPendingDrain() {
- PendingIntent pIntent = getDrainIntent();
- pIntent.cancel();
- mAlarmManager.cancel(pIntent);
- }
-
- private PendingIntent getDrainIntent() {
- Intent counterMetricsIntent = new Intent(BLUETOOTH_COUNTER_METRICS_ACTION);
- counterMetricsIntent.setPackage(mContext.getPackageName());
- return PendingIntent.getBroadcast(
- mContext, 0, counterMetricsIntent, PendingIntent.FLAG_IMMUTABLE);
+ mAlarmManager.cancel(mOnAlarmListener);
}
}
diff --git a/android/app/src/com/android/bluetooth/btservice/storage/BluetoothDatabaseMigration.java b/android/app/src/com/android/bluetooth/btservice/storage/BluetoothDatabaseMigration.java
new file mode 100644
index 0000000000000000000000000000000000000000..a56c6a4322e1ce92046597982bf17cf65494f715
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/btservice/storage/BluetoothDatabaseMigration.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2022 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.btservice.storage;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUtils;
+import android.content.Context;
+import android.database.Cursor;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Class for regrouping the migration that occur when going mainline
+ * @hide
+ */
+public final class BluetoothDatabaseMigration {
+ private static final String TAG = "BluetoothDatabaseMigration";
+ /**
+ * @hide
+ */
+ public static boolean run(Context ctx, Cursor cursor) {
+ boolean result = true;
+ MetadataDatabase database = MetadataDatabase.createDatabaseWithoutMigration(ctx);
+ while (cursor.moveToNext()) {
+ try {
+ final String primaryKey = cursor.getString(cursor.getColumnIndexOrThrow("address"));
+ String logKey = BluetoothUtils.toAnonymizedAddress(primaryKey);
+ if (logKey == null) { // handle non device address
+ logKey = primaryKey;
+ }
+
+ Metadata metadata = new Metadata(primaryKey);
+
+ metadata.migrated = fetchInt(cursor, "migrated") > 0;
+ migrate_a2dpSupportsOptionalCodecs(cursor, logKey, metadata);
+ migrate_a2dpOptionalCodecsEnabled(cursor, logKey, metadata);
+ metadata.last_active_time = fetchInt(cursor, "last_active_time");
+ metadata.is_active_a2dp_device = fetchInt(cursor, "is_active_a2dp_device") > 0;
+ migrate_connectionPolicy(cursor, logKey, metadata);
+ migrate_customizedMeta(cursor, metadata);
+
+ database.insert(metadata);
+ Log.d(TAG, "One item migrated: " + metadata);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to migrate one item: " + e);
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ private static final List> CONNECTION_POLICIES =
+ Arrays.asList(
+ new Pair(BluetoothProfile.A2DP, "a2dp_connection_policy"),
+ new Pair(BluetoothProfile.A2DP_SINK, "a2dp_sink_connection_policy"),
+ new Pair(BluetoothProfile.HEADSET, "hfp_connection_policy"),
+ new Pair(BluetoothProfile.HEADSET_CLIENT, "hfp_client_connection_policy"),
+ new Pair(BluetoothProfile.HID_HOST, "hid_host_connection_policy"),
+ new Pair(BluetoothProfile.PAN, "pan_connection_policy"),
+ new Pair(BluetoothProfile.PBAP, "pbap_connection_policy"),
+ new Pair(BluetoothProfile.PBAP_CLIENT, "pbap_client_connection_policy"),
+ new Pair(BluetoothProfile.MAP, "map_connection_policy"),
+ new Pair(BluetoothProfile.SAP, "sap_connection_policy"),
+ new Pair(BluetoothProfile.HEARING_AID, "hearing_aid_connection_policy"),
+ new Pair(BluetoothProfile.HAP_CLIENT, "hap_client_connection_policy"),
+ new Pair(BluetoothProfile.MAP_CLIENT, "map_client_connection_policy"),
+ new Pair(BluetoothProfile.LE_AUDIO, "le_audio_connection_policy"),
+ new Pair(BluetoothProfile.VOLUME_CONTROL, "volume_control_connection_policy"),
+ new Pair(BluetoothProfile.CSIP_SET_COORDINATOR,
+ "csip_set_coordinator_connection_policy"),
+ new Pair(BluetoothProfile.LE_CALL_CONTROL, "le_call_control_connection_policy"),
+ new Pair(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
+ "bass_client_connection_policy"),
+ new Pair(BluetoothProfile.BATTERY, "battery_connection_policy")
+ );
+
+ private static final List> CUSTOMIZED_META_KEYS =
+ Arrays.asList(
+ new Pair(BluetoothDevice.METADATA_MANUFACTURER_NAME, "manufacturer_name"),
+ new Pair(BluetoothDevice.METADATA_MODEL_NAME, "model_name"),
+ new Pair(BluetoothDevice.METADATA_SOFTWARE_VERSION, "software_version"),
+ new Pair(BluetoothDevice.METADATA_HARDWARE_VERSION, "hardware_version"),
+ new Pair(BluetoothDevice.METADATA_COMPANION_APP, "companion_app"),
+ new Pair(BluetoothDevice.METADATA_MAIN_ICON, "main_icon"),
+ new Pair(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET, "is_untethered_headset"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON, "untethered_left_icon"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON, "untethered_right_icon"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_CASE_ICON, "untethered_case_icon"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,
+ "untethered_left_battery"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY,
+ "untethered_right_battery"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY,
+ "untethered_case_battery"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING,
+ "untethered_left_charging"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING,
+ "untethered_right_charging"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING,
+ "untethered_case_charging"),
+ new Pair(BluetoothDevice.METADATA_ENHANCED_SETTINGS_UI_URI,
+ "enhanced_settings_ui_uri"),
+ new Pair(BluetoothDevice.METADATA_DEVICE_TYPE, "device_type"),
+ new Pair(BluetoothDevice.METADATA_MAIN_BATTERY, "main_battery"),
+ new Pair(BluetoothDevice.METADATA_MAIN_CHARGING, "main_charging"),
+ new Pair(BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD,
+ "main_low_battery_threshold"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
+ "untethered_left_low_battery_threshold"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
+ "untethered_right_low_battery_threshold"),
+ new Pair(BluetoothDevice.METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD,
+ "untethered_case_low_battery_threshold"),
+ new Pair(BluetoothDevice.METADATA_SPATIAL_AUDIO, "spatial_audio"),
+ new Pair(BluetoothDevice.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
+ "fastpair_customized")
+ );
+
+ private static int fetchInt(Cursor cursor, String key) {
+ return cursor.getInt(cursor.getColumnIndexOrThrow(key));
+ }
+
+ private static void migrate_a2dpSupportsOptionalCodecs(Cursor cursor, String logKey,
+ Metadata metadata) {
+ final String key = "a2dpSupportsOptionalCodecs";
+ final List allowedValue = new ArrayList<>(Arrays.asList(
+ BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN,
+ BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED,
+ BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED));
+ final int value = fetchInt(cursor, key);
+ if (!allowedValue.contains(value)) {
+ throw new IllegalArgumentException(logKey + ": Bad value for [" + key + "]: " + value);
+ }
+ metadata.a2dpSupportsOptionalCodecs = value;
+ }
+
+ private static void migrate_a2dpOptionalCodecsEnabled(Cursor cursor, String logKey,
+ Metadata metadata) {
+ final String key = "a2dpOptionalCodecsEnabled";
+ final List allowedValue = new ArrayList<>(Arrays.asList(
+ BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN,
+ BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED,
+ BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED));
+ final int value = fetchInt(cursor, key);
+ if (!allowedValue.contains(value)) {
+ throw new IllegalArgumentException(logKey + ": Bad value for [" + key + "]: " + value);
+ }
+ metadata.a2dpOptionalCodecsEnabled = value;
+ }
+
+ private static void migrate_connectionPolicy(Cursor cursor, String logKey,
+ Metadata metadata) {
+ final List allowedValue = new ArrayList<>(Arrays.asList(
+ BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
+ BluetoothProfile.CONNECTION_POLICY_ALLOWED));
+ for (Pair p : CONNECTION_POLICIES) {
+ final int policy = cursor.getInt(cursor.getColumnIndexOrThrow(p.second));
+ if (allowedValue.contains(policy)) {
+ metadata.setProfileConnectionPolicy(p.first, policy);
+ } else {
+ throw new IllegalArgumentException(logKey + ": Bad value for ["
+ + BluetoothProfile.getProfileName(p.first)
+ + "]: " + policy);
+ }
+ }
+ }
+
+ private static void migrate_customizedMeta(Cursor cursor, Metadata metadata) {
+ for (Pair p : CUSTOMIZED_META_KEYS) {
+ final byte[] blob = cursor.getBlob(cursor.getColumnIndexOrThrow(p.second));
+ // There is no specific pattern to check the custom meta data
+ metadata.setCustomizedMeta(p.first, blob);
+ }
+ }
+
+}
diff --git a/android/app/src/com/android/bluetooth/btservice/storage/CustomizedMetadataEntity.java b/android/app/src/com/android/bluetooth/btservice/storage/CustomizedMetadataEntity.java
index 554e0753fc3b923ddf8a646100d7452321e04fc4..b3746a2fb58c680691ec361cd486c0466319f5c7 100644
--- a/android/app/src/com/android/bluetooth/btservice/storage/CustomizedMetadataEntity.java
+++ b/android/app/src/com/android/bluetooth/btservice/storage/CustomizedMetadataEntity.java
@@ -46,6 +46,7 @@ class CustomizedMetadataEntity {
public byte[] untethered_case_low_battery_threshold;
public byte[] spatial_audio;
public byte[] fastpair_customized;
+ public byte[] le_audio;
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -100,7 +101,10 @@ class CustomizedMetadataEntity {
.append("|spatial_audio=")
.append(metadataToString(spatial_audio))
.append("|fastpair_customized=")
- .append(metadataToString(fastpair_customized));
+ .append(metadataToString(fastpair_customized))
+ .append("|le_audio=")
+ .append(metadataToString(le_audio));
+
return builder.toString();
}
diff --git a/android/app/src/com/android/bluetooth/btservice/storage/Metadata.java b/android/app/src/com/android/bluetooth/btservice/storage/Metadata.java
index 91e33e044a0acdbf7fd259937ee109a73c595617..f64f35e6861df9fd75d346bdec9f2436e475b93a 100644
--- a/android/app/src/com/android/bluetooth/btservice/storage/Metadata.java
+++ b/android/app/src/com/android/bluetooth/btservice/storage/Metadata.java
@@ -21,17 +21,24 @@ import android.bluetooth.BluetoothA2dp.OptionalCodecsPreferenceStatus;
import android.bluetooth.BluetoothA2dp.OptionalCodecsSupportStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUtils;
import androidx.annotation.NonNull;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
+/**
+ * @hide
+ */
@Entity(tableName = "metadata")
-class Metadata {
+@VisibleForTesting
+public class Metadata {
@PrimaryKey
@NonNull
private String address;
@@ -62,7 +69,11 @@ class Metadata {
is_active_a2dp_device = true;
}
- String getAddress() {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public String getAddress() {
return address;
}
@@ -75,7 +86,7 @@ class Metadata {
*/
@NonNull
public String getAnonymizedAddress() {
- return "XX:XX:XX" + getAddress().substring(8);
+ return BluetoothUtils.toAnonymizedAddress(address);
}
void setProfileConnectionPolicy(int profile, int connectionPolicy) {
@@ -148,7 +159,11 @@ class Metadata {
}
}
- int getProfileConnectionPolicy(int profile) {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public int getProfileConnectionPolicy(int profile) {
switch (profile) {
case BluetoothProfile.A2DP:
return profileConnectionPolicies.a2dp_connection_policy;
@@ -272,10 +287,17 @@ class Metadata {
case BluetoothDevice.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS:
publicMetadata.fastpair_customized = value;
break;
+ case BluetoothDevice.METADATA_LE_AUDIO:
+ publicMetadata.le_audio = value;
+ break;
}
}
- byte[] getCustomizedMeta(int key) {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public byte[] getCustomizedMeta(int key) {
byte[] value = null;
switch (key) {
case BluetoothDevice.METADATA_MANUFACTURER_NAME:
@@ -356,6 +378,9 @@ class Metadata {
case BluetoothDevice.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS:
value = publicMetadata.fastpair_customized;
break;
+ case BluetoothDevice.METADATA_LE_AUDIO:
+ value = publicMetadata.le_audio;
+ break;
}
return value;
}
@@ -372,7 +397,8 @@ class Metadata {
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append(address)
+ builder.append(getAnonymizedAddress())
+ .append(" last_active_time=" + last_active_time)
.append(" {profile connection policy(")
.append(profileConnectionPolicies)
.append("), optional codec(support=")
diff --git a/android/app/src/com/android/bluetooth/btservice/storage/MetadataDatabase.java b/android/app/src/com/android/bluetooth/btservice/storage/MetadataDatabase.java
index a2db277c0d996c5719d58210328113ce89571b1a..a71a548e8eb6d5111da245652a41f2afc388d849 100644
--- a/android/app/src/com/android/bluetooth/btservice/storage/MetadataDatabase.java
+++ b/android/app/src/com/android/bluetooth/btservice/storage/MetadataDatabase.java
@@ -33,7 +33,7 @@ import java.util.List;
/**
* MetadataDatabase is a Room database stores Bluetooth persistence data
*/
-@Database(entities = {Metadata.class}, version = 113)
+@Database(entities = {Metadata.class}, version = 114)
public abstract class MetadataDatabase extends RoomDatabase {
/**
* The metadata database file name
@@ -66,6 +66,7 @@ public abstract class MetadataDatabase extends RoomDatabase {
.addMigrations(MIGRATION_110_111)
.addMigrations(MIGRATION_111_112)
.addMigrations(MIGRATION_112_113)
+ .addMigrations(MIGRATION_113_114)
.allowMainThreadQueries()
.build();
}
@@ -483,4 +484,20 @@ public abstract class MetadataDatabase extends RoomDatabase {
}
}
};
+
+ @VisibleForTesting
+ static final Migration MIGRATION_113_114 = new Migration(113, 114) {
+ @Override
+ public void migrate(SupportSQLiteDatabase database) {
+ try {
+ database.execSQL("ALTER TABLE metadata ADD COLUMN `le_audio` BLOB");
+ } catch (SQLException ex) {
+ // Check if user has new schema, but is just missing the version update
+ Cursor cursor = database.query("SELECT * FROM metadata");
+ if (cursor == null || cursor.getColumnIndex("le_audio") == -1) {
+ throw ex;
+ }
+ }
+ }
+ };
}
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index f85dbbc4bff64bd1a86e0022030792b1365ee2bb..6c87ed0a32cbcbe72c1164fb2ffccd8b3f0edf34 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -278,6 +278,7 @@ public class CsipSetCoordinatorService extends ProfileService {
CsipSetCoordinatorStateMachine smConnect = getOrCreateStateMachine(device);
if (smConnect == null) {
Log.e(TAG, "Cannot connect to " + device + " : no state machine");
+ return false;
}
smConnect.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
}
@@ -597,6 +598,24 @@ public class CsipSetCoordinatorService extends ProfileService {
.collect(Collectors.toList());
}
+ /**
+ * Get group ID for a given device and UUID
+ * @param device potential group member
+ * @param uuid profile context UUID
+ * @return group ID
+ */
+ public Integer getGroupId(BluetoothDevice device, ParcelUuid uuid) {
+ Map device_groups =
+ mDeviceGroupIdRankMap.getOrDefault(device, new HashMap<>());
+ return mGroupIdToUuidMap.entrySet()
+ .stream()
+ .filter(e -> (device_groups.containsKey(e.getKey())
+ && e.getValue().equals(uuid)))
+ .map(Map.Entry::getKey)
+ .findFirst()
+ .orElse(IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID);
+ }
+
/**
* Get device's groups/
* @param device group member device
@@ -888,6 +907,8 @@ public class CsipSetCoordinatorService extends ProfileService {
return;
}
if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+ Log.i(TAG, "Disconnecting device because it was unbonded.");
+ disconnect(device);
return;
}
removeStateMachine(device);
@@ -953,8 +974,11 @@ public class CsipSetCoordinatorService extends ProfileService {
private CsipSetCoordinatorService mService;
private CsipSetCoordinatorService getService(AttributionSource source) {
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)) {
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)) {
return null;
}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index e02fd6f73f2849beb42c025ce29cb6d8d62d03ed..4ba889dbbcb9c6a083f2d24632643ab8372269c6 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -30,6 +30,7 @@ import android.os.RemoteException;
import android.util.Log;
import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.gatt.GattService.AdvertiserMap;
import java.util.Collections;
import java.util.HashMap;
@@ -46,6 +47,7 @@ class AdvertiseManager {
private final GattService mService;
private final AdapterService mAdapterService;
+ private final AdvertiserMap mAdvertiserMap;
private Handler mHandler;
Map mAdvertisers = Collections.synchronizedMap(new HashMap<>());
static int sTempRegistrationId = -1;
@@ -53,12 +55,14 @@ class AdvertiseManager {
/**
* Constructor of {@link AdvertiseManager}.
*/
- AdvertiseManager(GattService service, AdapterService adapterService) {
+ AdvertiseManager(GattService service, AdapterService adapterService,
+ AdvertiserMap advertiserMap) {
if (DBG) {
Log.d(TAG, "advertise manager created");
}
mService = service;
mAdapterService = adapterService;
+ mAdvertiserMap = advertiserMap;
}
/**
@@ -157,10 +161,18 @@ class AdvertiseManager {
if (status == 0) {
entry.setValue(
new AdvertiserInfo(advertiserId, entry.getValue().deathRecipient, callback));
+
+ mAdvertiserMap.setAdvertiserIdByRegId(regId, advertiserId);
} else {
IBinder binder = entry.getKey();
binder.unlinkToDeath(entry.getValue().deathRecipient, 0);
mAdvertisers.remove(binder);
+
+ AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(regId);
+ if (stats != null) {
+ stats.recordAdvertiseStop();
+ }
+ mAdvertiserMap.removeAppAdvertiseStats(regId);
}
callback.onAdvertisingSetStarted(advertiserId, txPower, status);
@@ -181,6 +193,13 @@ class AdvertiseManager {
IAdvertisingSetCallback callback = entry.getValue().callback;
callback.onAdvertisingEnabled(advertiserId, enable, status);
+
+ if (!enable && status != 0) {
+ AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
+ if (stats != null) {
+ stats.recordAdvertiseStop();
+ }
+ }
}
void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData,
@@ -203,14 +222,19 @@ class AdvertiseManager {
byte[] periodicDataBytes =
AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName);
- int cbId = --sTempRegistrationId;
- mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
+ int cbId = --sTempRegistrationId;
+ mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
- if (DBG) {
- Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder);
- }
- startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes, periodicParameters,
- periodicDataBytes, duration, maxExtAdvEvents, cbId);
+ if (DBG) {
+ Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder);
+ }
+
+ mAdvertiserMap.add(cbId, callback, mService);
+ mAdvertiserMap.recordAdvertiseStart(cbId, parameters, advertiseData,
+ scanResponse, periodicParameters, periodicData, duration, maxExtAdvEvents);
+
+ startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes,
+ periodicParameters, periodicDataBytes, duration, maxExtAdvEvents, cbId);
} catch (IllegalArgumentException e) {
try {
@@ -276,6 +300,8 @@ class AdvertiseManager {
} catch (RemoteException e) {
Log.i(TAG, "error sending onAdvertisingSetStopped callback", e);
}
+
+ mAdvertiserMap.recordAdvertiseStop(advertiserId);
}
void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
@@ -285,6 +311,9 @@ class AdvertiseManager {
return;
}
enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents);
+
+ mAdvertiserMap.enableAdvertisingSet(advertiserId,
+ enable, duration, maxExtAdvEvents);
}
void setAdvertisingData(int advertiserId, AdvertiseData data) {
@@ -297,6 +326,8 @@ class AdvertiseManager {
try {
setAdvertisingDataNative(advertiserId,
AdvertiseHelper.advertiseDataToBytes(data, deviceName));
+
+ mAdvertiserMap.setAdvertisingData(advertiserId, data);
} catch (IllegalArgumentException e) {
try {
onAdvertisingDataSet(advertiserId,
@@ -317,6 +348,8 @@ class AdvertiseManager {
try {
setScanResponseDataNative(advertiserId,
AdvertiseHelper.advertiseDataToBytes(data, deviceName));
+
+ mAdvertiserMap.setScanResponseData(advertiserId, data);
} catch (IllegalArgumentException e) {
try {
onScanResponseDataSet(advertiserId,
@@ -334,6 +367,8 @@ class AdvertiseManager {
return;
}
setAdvertisingParametersNative(advertiserId, parameters);
+
+ mAdvertiserMap.setAdvertisingParameters(advertiserId, parameters);
}
void setPeriodicAdvertisingParameters(int advertiserId,
@@ -344,6 +379,8 @@ class AdvertiseManager {
return;
}
setPeriodicAdvertisingParametersNative(advertiserId, parameters);
+
+ mAdvertiserMap.setPeriodicAdvertisingParameters(advertiserId, parameters);
}
void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
@@ -356,6 +393,8 @@ class AdvertiseManager {
try {
setPeriodicAdvertisingDataNative(advertiserId,
AdvertiseHelper.advertiseDataToBytes(data, deviceName));
+
+ mAdvertiserMap.setPeriodicAdvertisingData(advertiserId, data);
} catch (IllegalArgumentException e) {
try {
onPeriodicAdvertisingDataSet(advertiserId,
@@ -473,6 +512,11 @@ class AdvertiseManager {
IAdvertisingSetCallback callback = entry.getValue().callback;
callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
+
+ AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
+ if (stats != null) {
+ stats.onPeriodicAdvertiseEnabled(enable);
+ }
}
static {
diff --git a/android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java b/android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e0724623c1b2064819cc830dcfa3c1f3a5c9798
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2021 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.gatt;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.os.ParcelUuid;
+import android.util.SparseArray;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ScanStats class helps keep track of information about scans
+ * on a per application basis.
+ * @hide
+ */
+@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+public class AppAdvertiseStats {
+ private static final String TAG = AppAdvertiseStats.class.getSimpleName();
+
+ private static DateTimeFormatter sDateFormat = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss")
+ .withZone(ZoneId.systemDefault());
+
+ static final String[] PHY_LE_STRINGS = {"LE_1M", "LE_2M", "LE_CODED"};
+ static final int UUID_STRING_FILTER_LEN = 8;
+
+ // ContextMap here is needed to grab Apps and Connections
+ ContextMap mContextMap;
+
+ // GattService is needed to add scan event protos to be dumped later
+ GattService mGattService;
+
+ static class AppAdvertiserData {
+ public boolean includeDeviceName = false;
+ public boolean includeTxPowerLevel = false;
+ public SparseArray manufacturerData;
+ public Map serviceData;
+ public List serviceUuids;
+ AppAdvertiserData(boolean includeDeviceName, boolean includeTxPowerLevel,
+ SparseArray manufacturerData, Map serviceData,
+ List serviceUuids) {
+ this.includeDeviceName = includeDeviceName;
+ this.includeTxPowerLevel = includeTxPowerLevel;
+ this.manufacturerData = manufacturerData;
+ this.serviceData = serviceData;
+ this.serviceUuids = serviceUuids;
+ }
+ }
+
+ static class AppAdvertiserRecord {
+ public Instant startTime = null;
+ public Instant stopTime = null;
+ public int duration = 0;
+ public int maxExtendedAdvertisingEvents = 0;
+ AppAdvertiserRecord(Instant startTime) {
+ this.startTime = startTime;
+ }
+ }
+
+ private int mAppUid;
+ private String mAppName;
+ private int mId;
+ private boolean mAdvertisingEnabled = false;
+ private boolean mPeriodicAdvertisingEnabled = false;
+ private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
+ private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
+ private int mInterval = 0;
+ private int mTxPowerLevel = 0;
+ private boolean mLegacy = false;
+ private boolean mAnonymous = false;
+ private boolean mConnectable = false;
+ private boolean mScannable = false;
+ private AppAdvertiserData mAdvertisingData = null;
+ private AppAdvertiserData mScanResponseData = null;
+ private AppAdvertiserData mPeriodicAdvertisingData = null;
+ private boolean mPeriodicIncludeTxPower = false;
+ private int mPeriodicInterval = 0;
+ public ArrayList mAdvertiserRecords =
+ new ArrayList();
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) {
+ this.mAppUid = appUid;
+ this.mId = id;
+ this.mAppName = name;
+ this.mContextMap = map;
+ this.mGattService = service;
+ }
+
+ void recordAdvertiseStart(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData,
+ int duration, int maxExtAdvEvents) {
+ mAdvertisingEnabled = true;
+ AppAdvertiserRecord record = new AppAdvertiserRecord(Instant.now());
+ record.duration = duration;
+ record.maxExtendedAdvertisingEvents = maxExtAdvEvents;
+ mAdvertiserRecords.add(record);
+ if (mAdvertiserRecords.size() > 5) {
+ mAdvertiserRecords.remove(0);
+ }
+
+ if (parameters != null) {
+ mPrimaryPhy = parameters.getPrimaryPhy();
+ mSecondaryPhy = parameters.getSecondaryPhy();
+ mInterval = parameters.getInterval();
+ mTxPowerLevel = parameters.getTxPowerLevel();
+ mLegacy = parameters.isLegacy();
+ mAnonymous = parameters.isAnonymous();
+ mConnectable = parameters.isConnectable();
+ mScannable = parameters.isScannable();
+ }
+
+ if (advertiseData != null) {
+ mAdvertisingData = new AppAdvertiserData(advertiseData.getIncludeDeviceName(),
+ advertiseData.getIncludeTxPowerLevel(),
+ advertiseData.getManufacturerSpecificData(),
+ advertiseData.getServiceData(),
+ advertiseData.getServiceUuids());
+ }
+
+ if (scanResponse != null) {
+ mScanResponseData = new AppAdvertiserData(scanResponse.getIncludeDeviceName(),
+ scanResponse.getIncludeTxPowerLevel(),
+ scanResponse.getManufacturerSpecificData(),
+ scanResponse.getServiceData(),
+ scanResponse.getServiceUuids());
+ }
+
+ if (periodicData != null) {
+ mPeriodicAdvertisingData = new AppAdvertiserData(
+ periodicData.getIncludeDeviceName(),
+ periodicData.getIncludeTxPowerLevel(),
+ periodicData.getManufacturerSpecificData(),
+ periodicData.getServiceData(),
+ periodicData.getServiceUuids());
+ }
+
+ if (periodicParameters != null) {
+ mPeriodicAdvertisingEnabled = true;
+ mPeriodicIncludeTxPower = periodicParameters.getIncludeTxPower();
+ mPeriodicInterval = periodicParameters.getInterval();
+ }
+ }
+
+ void recordAdvertiseStart(int duration, int maxExtAdvEvents) {
+ recordAdvertiseStart(null, null, null, null, null, duration, maxExtAdvEvents);
+ }
+
+ void recordAdvertiseStop() {
+ mAdvertisingEnabled = false;
+ mPeriodicAdvertisingEnabled = false;
+ if (!mAdvertiserRecords.isEmpty()) {
+ AppAdvertiserRecord record = mAdvertiserRecords.get(mAdvertiserRecords.size() - 1);
+ record.stopTime = Instant.now();
+ }
+ }
+
+ void enableAdvertisingSet(boolean enable, int duration, int maxExtAdvEvents) {
+ if (enable) {
+ //if the advertisingSet have not been disabled, skip enabling.
+ if (!mAdvertisingEnabled) {
+ recordAdvertiseStart(duration, maxExtAdvEvents);
+ }
+ } else {
+ //if the advertisingSet have not been enabled, skip disabling.
+ if (mAdvertisingEnabled) {
+ recordAdvertiseStop();
+ }
+ }
+ }
+
+ void setAdvertisingData(AdvertiseData data) {
+ if (mAdvertisingData == null) {
+ mAdvertisingData = new AppAdvertiserData(data.getIncludeDeviceName(),
+ data.getIncludeTxPowerLevel(),
+ data.getManufacturerSpecificData(),
+ data.getServiceData(),
+ data.getServiceUuids());
+ } else if (data != null) {
+ mAdvertisingData.includeDeviceName = data.getIncludeDeviceName();
+ mAdvertisingData.includeTxPowerLevel = data.getIncludeTxPowerLevel();
+ mAdvertisingData.manufacturerData = data.getManufacturerSpecificData();
+ mAdvertisingData.serviceData = data.getServiceData();
+ mAdvertisingData.serviceUuids = data.getServiceUuids();
+ }
+ }
+
+ void setScanResponseData(AdvertiseData data) {
+ if (mScanResponseData == null) {
+ mScanResponseData = new AppAdvertiserData(data.getIncludeDeviceName(),
+ data.getIncludeTxPowerLevel(),
+ data.getManufacturerSpecificData(),
+ data.getServiceData(),
+ data.getServiceUuids());
+ } else if (data != null) {
+ mScanResponseData.includeDeviceName = data.getIncludeDeviceName();
+ mScanResponseData.includeTxPowerLevel = data.getIncludeTxPowerLevel();
+ mScanResponseData.manufacturerData = data.getManufacturerSpecificData();
+ mScanResponseData.serviceData = data.getServiceData();
+ mScanResponseData.serviceUuids = data.getServiceUuids();
+ }
+ }
+
+ void setAdvertisingParameters(AdvertisingSetParameters parameters) {
+ if (parameters != null) {
+ mPrimaryPhy = parameters.getPrimaryPhy();
+ mSecondaryPhy = parameters.getSecondaryPhy();
+ mInterval = parameters.getInterval();
+ mTxPowerLevel = parameters.getTxPowerLevel();
+ mLegacy = parameters.isLegacy();
+ mAnonymous = parameters.isAnonymous();
+ mConnectable = parameters.isConnectable();
+ mScannable = parameters.isScannable();
+ }
+ }
+
+ void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
+ if (parameters != null) {
+ mPeriodicIncludeTxPower = parameters.getIncludeTxPower();
+ mPeriodicInterval = parameters.getInterval();
+ }
+ }
+
+ void setPeriodicAdvertisingData(AdvertiseData data) {
+ if (mPeriodicAdvertisingData == null) {
+ mPeriodicAdvertisingData = new AppAdvertiserData(data.getIncludeDeviceName(),
+ data.getIncludeTxPowerLevel(),
+ data.getManufacturerSpecificData(),
+ data.getServiceData(),
+ data.getServiceUuids());
+ } else if (data != null) {
+ mPeriodicAdvertisingData.includeDeviceName = data.getIncludeDeviceName();
+ mPeriodicAdvertisingData.includeTxPowerLevel = data.getIncludeTxPowerLevel();
+ mPeriodicAdvertisingData.manufacturerData = data.getManufacturerSpecificData();
+ mPeriodicAdvertisingData.serviceData = data.getServiceData();
+ mPeriodicAdvertisingData.serviceUuids = data.getServiceUuids();
+ }
+ }
+
+ void onPeriodicAdvertiseEnabled(boolean enable) {
+ mPeriodicAdvertisingEnabled = enable;
+ }
+
+ void setId(int id) {
+ this.mId = id;
+ }
+
+ private static String printByteArrayInHex(byte[] data) {
+ final StringBuilder hex = new StringBuilder();
+ for (byte b : data) {
+ hex.append(String.format("%02x", b));
+ }
+ return hex.toString();
+ }
+
+ private static void dumpAppAdvertiserData(StringBuilder sb, AppAdvertiserData advData) {
+ sb.append("\n └Include Device Name : "
+ + advData.includeDeviceName);
+ sb.append("\n └Include Tx Power Level : "
+ + advData.includeTxPowerLevel);
+
+ if (advData.manufacturerData.size() > 0) {
+ sb.append("\n └Manufacturer Data (length of data) : "
+ + advData.manufacturerData.size());
+ }
+
+ if (!advData.serviceData.isEmpty()) {
+ sb.append("\n └Service Data(UUID, length of data) : ");
+ for (ParcelUuid uuid : advData.serviceData.keySet()) {
+ sb.append("\n [" + uuid.toString().substring(0, UUID_STRING_FILTER_LEN)
+ + "-xxxx-xxxx-xxxx-xxxxxxxxxxxx, "
+ + advData.serviceData.get(uuid).length + "]");
+ }
+ }
+
+ if (!advData.serviceUuids.isEmpty()) {
+ sb.append("\n └Service Uuids : \n "
+ + advData.serviceUuids.toString().substring(0, UUID_STRING_FILTER_LEN)
+ + "-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
+ }
+ }
+
+ private static String dumpPhyString(int phy) {
+ if (phy > PHY_LE_STRINGS.length) {
+ return Integer.toString(phy);
+ } else {
+ return PHY_LE_STRINGS[phy - 1];
+ }
+ }
+
+ private static void dumpAppAdvertiseStats(StringBuilder sb, AppAdvertiseStats stats) {
+ sb.append("\n └Advertising:");
+ sb.append("\n └Interval(0.625ms) : "
+ + stats.mInterval);
+ sb.append("\n └TX POWER(dbm) : "
+ + stats.mTxPowerLevel);
+ sb.append("\n └Primary Phy : "
+ + dumpPhyString(stats.mPrimaryPhy));
+ sb.append("\n └Secondary Phy : "
+ + dumpPhyString(stats.mSecondaryPhy));
+ sb.append("\n └Legacy : "
+ + stats.mLegacy);
+ sb.append("\n └Anonymous : "
+ + stats.mAnonymous);
+ sb.append("\n └Connectable : "
+ + stats.mConnectable);
+ sb.append("\n └Scannable : "
+ + stats.mScannable);
+
+ if (stats.mAdvertisingData != null) {
+ sb.append("\n └Advertise Data:");
+ dumpAppAdvertiserData(sb, stats.mAdvertisingData);
+ }
+
+ if (stats.mScanResponseData != null) {
+ sb.append("\n └Scan Response:");
+ dumpAppAdvertiserData(sb, stats.mScanResponseData);
+ }
+
+ if (stats.mPeriodicInterval > 0) {
+ sb.append("\n └Periodic Advertising Enabled : "
+ + stats.mPeriodicAdvertisingEnabled);
+ sb.append("\n └Periodic Include TxPower : "
+ + stats.mPeriodicIncludeTxPower);
+ sb.append("\n └Periodic Interval(1.25ms) : "
+ + stats.mPeriodicInterval);
+ }
+
+ if (stats.mPeriodicAdvertisingData != null) {
+ sb.append("\n └Periodic Advertise Data:");
+ dumpAppAdvertiserData(sb, stats.mPeriodicAdvertisingData);
+ }
+
+ sb.append("\n");
+ }
+
+ static void dumpToString(StringBuilder sb, AppAdvertiseStats stats) {
+ Instant currentTime = Instant.now();
+
+ sb.append("\n " + stats.mAppName);
+ sb.append("\n Advertising ID : "
+ + stats.mId);
+ for (int i = 0; i < stats.mAdvertiserRecords.size(); i++) {
+ AppAdvertiserRecord record = stats.mAdvertiserRecords.get(i);
+
+ sb.append("\n " + (i + 1) + ":");
+ sb.append("\n └Start time : "
+ + sDateFormat.format(record.startTime));
+ if (record.stopTime == null) {
+ Duration timeElapsed = Duration.between(record.startTime, currentTime);
+ sb.append("\n └Elapsed time : "
+ + timeElapsed.toMillis() + "ms");
+ } else {
+ sb.append("\n └Stop time : "
+ + sDateFormat.format(record.stopTime));
+ }
+ sb.append("\n └Duration(10ms unit) : "
+ + record.duration);
+ sb.append("\n └Maximum number of extended advertising events : "
+ + record.maxExtendedAdvertisingEvents);
+ }
+
+ dumpAppAdvertiseStats(sb, stats);
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java b/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java
index f6035b392a5c0d9f1e0251f119615c7072414cac..ab3fe4ea4b8b32e6efcc0703637772410b227a6e 100644
--- a/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java
+++ b/android/app/src/com/android/bluetooth/gatt/CallbackInfo.java
@@ -20,9 +20,7 @@ package com.android.bluetooth.gatt;
* These are held during congestion and reported when congestion clears.
* @hide
*/
-/*package*/
-
-class CallbackInfo {
+/* package */ class CallbackInfo {
public String address;
public int status;
public int handle;
@@ -58,6 +56,6 @@ class CallbackInfo {
this.address = address;
this.status = status;
this.handle = handle;
+ this.value = value;
}
}
-
diff --git a/android/app/src/com/android/bluetooth/gatt/ContextMap.java b/android/app/src/com/android/bluetooth/gatt/ContextMap.java
index ed95301ad6756e6c45c3bf4e73f2bcd1239c16f3..dc9fe866aec4b887834293f0fe96704b9435c162 100644
--- a/android/app/src/com/android/bluetooth/gatt/ContextMap.java
+++ b/android/app/src/com/android/bluetooth/gatt/ContextMap.java
@@ -15,6 +15,9 @@
*/
package com.android.bluetooth.gatt;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
@@ -24,6 +27,13 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.bluetooth.BluetoothMethodProxy;
+import com.android.internal.annotations.GuardedBy;
+
+import com.google.common.collect.EvictingQueue;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -39,7 +49,8 @@ import java.util.UUID;
* This class manages application callbacks and keeps track of GATT connections.
* @hide
*/
-/*package*/ class ContextMap {
+@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+public class ContextMap {
private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap";
/**
@@ -125,6 +136,15 @@ import java.util.UUID;
this.appScanStats = appScanStats;
}
+ /**
+ * Creates a new app context for advertiser.
+ */
+ App(int id, C callback, String name) {
+ this.id = id;
+ this.callback = callback;
+ this.name = name;
+ }
+
/**
* Link death recipient
*/
@@ -169,11 +189,22 @@ import java.util.UUID;
}
/** Our internal application list */
+ private final Object mAppsLock = new Object();
+ @GuardedBy("mAppsLock")
private List mApps = new ArrayList();
/** Internal map to keep track of logging information by app name */
private HashMap mAppScanStats = new HashMap();
+ /** Internal map to keep track of logging information by advertise id */
+ private final Map mAppAdvertiseStats =
+ new HashMap();
+
+ private static final int ADVERTISE_STATE_MAX_SIZE = 5;
+
+ private final EvictingQueue mLastAdvertises =
+ EvictingQueue.create(ADVERTISE_STATE_MAX_SIZE);
+
/** Internal list of connected devices **/
private Set mConnections = new HashSet();
@@ -200,6 +231,34 @@ import java.util.UUID;
}
}
+ /**
+ * Add an entry to the application context list for advertiser.
+ */
+ App add(int id, C callback, GattService service) {
+ int appUid = Binder.getCallingUid();
+ String appName = service.getPackageManager().getNameForUid(appUid);
+ if (appName == null) {
+ // Assign an app name if one isn't found
+ appName = "Unknown App (UID: " + appUid + ")";
+ }
+
+ synchronized (mAppsLock) {
+ synchronized (this) {
+ if (!mAppAdvertiseStats.containsKey(id)) {
+ AppAdvertiseStats appAdvertiseStats = BluetoothMethodProxy.getInstance()
+ .createAppAdvertiseStats(appUid, id, appName, this, service);
+ mAppAdvertiseStats.put(id, appAdvertiseStats);
+ }
+ }
+ App app = getById(appUid);
+ if (app == null) {
+ app = new App(appUid, callback, appName);
+ mApps.add(app);
+ }
+ return app;
+ }
+ }
+
/**
* Remove the context for a given UUID
*/
@@ -382,6 +441,135 @@ import java.util.UUID;
return mAppScanStats.get(uid);
}
+ /**
+ * Remove the context for a given application ID.
+ */
+ void removeAppAdvertiseStats(int id) {
+ synchronized (this) {
+ mAppAdvertiseStats.remove(id);
+ }
+ }
+
+ /**
+ * Get Logging info by ID
+ */
+ AppAdvertiseStats getAppAdvertiseStatsById(int id) {
+ synchronized (this) {
+ return mAppAdvertiseStats.get(id);
+ }
+ }
+
+ /**
+ * update the advertiser ID by the regiseter ID
+ */
+ void setAdvertiserIdByRegId(int regId, int advertiserId) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(regId);
+ if (stats == null) {
+ return;
+ }
+ stats.setId(advertiserId);
+ mAppAdvertiseStats.remove(regId);
+ mAppAdvertiseStats.put(advertiserId, stats);
+ }
+ }
+
+ void recordAdvertiseStart(int id, AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData,
+ int duration, int maxExtAdvEvents) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.recordAdvertiseStart(parameters, advertiseData, scanResponse,
+ periodicParameters, periodicData, duration, maxExtAdvEvents);
+ }
+ }
+
+ void recordAdvertiseStop(int id) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.recordAdvertiseStop();
+ mAppAdvertiseStats.remove(id);
+ mLastAdvertises.add(stats);
+ }
+ }
+
+ void enableAdvertisingSet(int id, boolean enable, int duration, int maxExtAdvEvents) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.enableAdvertisingSet(enable, duration, maxExtAdvEvents);
+ }
+ }
+
+ void setAdvertisingData(int id, AdvertiseData data) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.setAdvertisingData(data);
+ }
+ }
+
+ void setScanResponseData(int id, AdvertiseData data) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.setScanResponseData(data);
+ }
+ }
+
+ void setAdvertisingParameters(int id, AdvertisingSetParameters parameters) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.setAdvertisingParameters(parameters);
+ }
+ }
+
+ void setPeriodicAdvertisingParameters(int id, PeriodicAdvertisingParameters parameters) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.setPeriodicAdvertisingParameters(parameters);
+ }
+ }
+
+ void setPeriodicAdvertisingData(int id, AdvertiseData data) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.setPeriodicAdvertisingData(data);
+ }
+ }
+
+ void onPeriodicAdvertiseEnabled(int id, boolean enable) {
+ synchronized (this) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(id);
+ if (stats == null) {
+ return;
+ }
+ stats.onPeriodicAdvertiseEnabled(enable);
+ }
+ }
+
/**
* Get the device addresses for all connected devices
*/
@@ -477,7 +665,9 @@ import java.util.UUID;
while (i.hasNext()) {
App entry = i.next();
entry.unlinkToDeath();
- entry.appScanStats.isRegistered = false;
+ if (entry.appScanStats != null) {
+ entry.appScanStats.isRegistered = false;
+ }
i.remove();
}
}
@@ -485,6 +675,11 @@ import java.util.UUID;
synchronized (mConnections) {
mConnections.clear();
}
+
+ synchronized (this) {
+ mAppAdvertiseStats.clear();
+ mLastAdvertises.clear();
+ }
}
/**
@@ -514,4 +709,31 @@ import java.util.UUID;
appScanStats.dumpToString(sb);
}
}
+
+ /**
+ * Logs advertiser debug information.
+ */
+ void dumpAdvertiser(StringBuilder sb) {
+ synchronized (this) {
+ if (!mLastAdvertises.isEmpty()) {
+ sb.append("\n last " + mLastAdvertises.size() + " advertising:");
+ for (AppAdvertiseStats stats : mLastAdvertises) {
+ AppAdvertiseStats.dumpToString(sb, stats);
+ }
+ sb.append("\n");
+ }
+
+ if (!mAppAdvertiseStats.isEmpty()) {
+ sb.append(" Total number of ongoing advertising : "
+ + mAppAdvertiseStats.size());
+ sb.append("\n Ongoing advertising:");
+ for (Integer key : mAppAdvertiseStats.keySet()) {
+ AppAdvertiseStats stats = mAppAdvertiseStats.get(key);
+ AppAdvertiseStats.dumpToString(sb, stats);
+ }
+ }
+ sb.append("\n");
+ }
+ Log.d(TAG, sb.toString());
+ }
}
diff --git a/android/app/src/com/android/bluetooth/gatt/GattDebugUtils.java b/android/app/src/com/android/bluetooth/gatt/GattDebugUtils.java
index 232477b9b163adedbde6c3ed6b7aaeda9de0962d..0a1bfb35812f6425506b7e1bfa5e1267fb12dea4 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattDebugUtils.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattDebugUtils.java
@@ -20,6 +20,9 @@ import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.UUID;
/**
@@ -29,17 +32,23 @@ import java.util.UUID;
private static final String TAG = GattServiceConfig.TAG_PREFIX + "DebugUtils";
private static final boolean DEBUG_ADMIN = GattServiceConfig.DEBUG_ADMIN;
- private static final String ACTION_GATT_PAIRING_CONFIG =
+ @VisibleForTesting
+ static final String ACTION_GATT_PAIRING_CONFIG =
"android.bluetooth.action.GATT_PAIRING_CONFIG";
- private static final String ACTION_GATT_TEST_USAGE = "android.bluetooth.action.GATT_TEST_USAGE";
- private static final String ACTION_GATT_TEST_ENABLE =
+ @VisibleForTesting
+ static final String ACTION_GATT_TEST_USAGE = "android.bluetooth.action.GATT_TEST_USAGE";
+ @VisibleForTesting
+ static final String ACTION_GATT_TEST_ENABLE =
"android.bluetooth.action.GATT_TEST_ENABLE";
- private static final String ACTION_GATT_TEST_CONNECT =
+ @VisibleForTesting
+ static final String ACTION_GATT_TEST_CONNECT =
"android.bluetooth.action.GATT_TEST_CONNECT";
- private static final String ACTION_GATT_TEST_DISCONNECT =
+ @VisibleForTesting
+ static final String ACTION_GATT_TEST_DISCONNECT =
"android.bluetooth.action.GATT_TEST_DISCONNECT";
- private static final String ACTION_GATT_TEST_DISCOVER =
+ @VisibleForTesting
+ static final String ACTION_GATT_TEST_DISCOVER =
"android.bluetooth.action.GATT_TEST_DISCOVER";
private static final String EXTRA_ENABLE = "enable";
@@ -69,7 +78,7 @@ import java.util.UUID;
* import com.android.bluetooth.gatt.GattService;
*/
static boolean handleDebugAction(GattService svc, Intent intent) {
- if (!DEBUG_ADMIN) {
+ if (!DEBUG_ADMIN && !Utils.isInstrumentationTestMode()) {
return false;
}
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index cec3bfd2fd90d78731d14d6aa85f6bc8dc9aab07..2bcedd72c95e4d0eda02aba24c8fa401cfa51c60 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -24,6 +24,7 @@ import android.annotation.SuppressLint;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.Service;
+import android.content.Context;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
@@ -52,6 +53,8 @@ import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.content.AttributionSource;
import android.content.Intent;
+import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.MacAddress;
import android.os.Binder;
import android.os.Build;
@@ -221,6 +224,13 @@ public class GattService extends ProfileService {
ScannerMap mScannerMap = new ScannerMap();
+ /**
+ * List of our registered advertisers.
+ */
+ static class AdvertiserMap extends ContextMap {}
+
+ private AdvertiserMap mAdvertiserMap = new AdvertiserMap();
+
/**
* List of our registered clients.
*/
@@ -324,7 +334,7 @@ public class GattService extends ProfileService {
mBluetoothAdapterProxy = BluetoothAdapterProxy.getInstance();
mCompanionManager = getSystemService(CompanionDeviceManager.class);
mAppOps = getSystemService(AppOpsManager.class);
- mAdvertiseManager = new AdvertiseManager(this, mAdapterService);
+ mAdvertiseManager = new AdvertiseManager(this, mAdapterService, mAdvertiserMap);
mAdvertiseManager.start();
mScanManager = new ScanManager(this, mAdapterService, mBluetoothAdapterProxy);
@@ -344,6 +354,7 @@ public class GattService extends ProfileService {
}
setGattService(null);
mScannerMap.clear();
+ mAdvertiserMap.clear();
mClientMap.clear();
mServerMap.clear();
mHandleMap.clear();
@@ -561,7 +572,8 @@ public class GattService extends ProfileService {
/**
* Handlers for incoming service calls
*/
- private static class BluetoothGattBinder extends IBluetoothGatt.Stub
+ @VisibleForTesting
+ static class BluetoothGattBinder extends IBluetoothGatt.Stub
implements IProfileServiceBinder {
private GattService mService;
@@ -3416,7 +3428,7 @@ public class GattService extends ProfileService {
Log.d(TAG, "clientConnect() - address=" + address + ", isDirect=" + isDirect
+ ", opportunistic=" + opportunistic + ", phy=" + phy);
}
- statsLogAppPackage(address, attributionSource.getPackageName());
+ statsLogAppPackage(address, attributionSource.getUid(), clientIf);
statsLogGattConnectionStateChange(
BluetoothProfile.GATT, address, clientIf,
BluetoothProtoEnums.CONNECTION_STATE_CONNECTING);
@@ -4007,8 +4019,17 @@ public class GattService extends ProfileService {
connectionState = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
}
+ int applicationUid = -1;
+
+ try {
+ applicationUid = this.getPackageManager().getPackageUid(app.name, PackageInfoFlags.of(0));
+
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "onClientConnected() uid_not_found=" + app.name);
+ }
+
app.callback.onServerConnectionState((byte) 0, serverIf, connected, address);
- statsLogAppPackage(address, app.name);
+ statsLogAppPackage(address, applicationUid, serverIf);
statsLogGattConnectionStateChange(
BluetoothProfile.GATT_SERVER, address, serverIf, connectionState);
}
@@ -4665,6 +4686,9 @@ public class GattService extends ProfileService {
sb.append("GATT Scanner Map\n");
mScannerMap.dump(sb);
+ sb.append("GATT Advertiser Map\n");
+ mAdvertiserMap.dumpAdvertiser(sb);
+
sb.append("GATT Client Map\n");
mClientMap.dump(sb);
@@ -4684,14 +4708,14 @@ public class GattService extends ProfileService {
}
}
- private void statsLogAppPackage(String address, String app) {
+ private void statsLogAppPackage(String address, int applicationUid, int sessionIndex) {
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
BluetoothStatsLog.write(
- BluetoothStatsLog.BLUETOOTH_DEVICE_NAME_REPORTED,
- mAdapterService.getMetricId(device), app);
+ BluetoothStatsLog.BLUETOOTH_GATT_APP_INFO,
+ sessionIndex, mAdapterService.getMetricId(device), applicationUid);
if (DBG) {
Log.d(TAG, "Gatt Logging: metric_id=" + mAdapterService.getMetricId(device)
- + ", app=" + app);
+ + ", app_uid=" + applicationUid);
}
}
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanManager.java b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
index 675b89983cd08aac7a8007aa79a5b95e706ccfe5..fe506cae5ac374f1d6997c9ea762e8587c3a5f8b 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
@@ -282,6 +282,9 @@ public class ScanManager {
if (client == null) {
client = mScanNative.getRegularScanClient(scannerId);
}
+ if (client == null) {
+ client = mScanNative.getSuspendedScanClient(scannerId);
+ }
sendMessage(MSG_STOP_BLE_SCAN, client);
}
@@ -1127,6 +1130,15 @@ public class ScanManager {
return null;
}
+ ScanClient getSuspendedScanClient(int scannerId) {
+ for (ScanClient client : mSuspendedScanClients) {
+ if (client.scannerId == scannerId) {
+ return client;
+ }
+ }
+ return null;
+ }
+
void stopBatchScan(ScanClient client) {
mBatchClients.remove(client);
removeScanFilters(client.scannerId);
@@ -1325,11 +1337,12 @@ public class ScanManager {
private void initFilterIndexStack() {
int maxFiltersSupported =
AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
- // Start from index 3 as:
+ // Start from index 4 as:
// index 0 is reserved for ALL_PASS filter in Settings app.
// index 1 is reserved for ALL_PASS filter for regular scan apps.
// index 2 is reserved for ALL_PASS filter for batch scan apps.
- for (int i = 3; i < maxFiltersSupported; ++i) {
+ // index 3 is reserved for BAP/CAP Announcements
+ for (int i = 4; i < maxFiltersSupported; ++i) {
mFilterIndexStack.add(i);
}
}
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index 52aaaaf5c357bfc3f1a08e53fce64214ae6b966f..2b06aa608e1709068c0e0795c6542e57dfe74951 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -100,7 +100,8 @@ public class HapClientService extends ProfileService {
return BluetoothProperties.isProfileHapClientEnabled().orElse(false);
}
- private static synchronized void setHapClient(HapClientService instance) {
+ @VisibleForTesting
+ static synchronized void setHapClient(HapClientService instance) {
if (DBG) {
Log.d(TAG, "setHapClient(): set to: " + instance);
}
@@ -267,6 +268,8 @@ public class HapClientService extends ProfileService {
return;
}
if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+ Log.i(TAG, "Disconnecting device because it was unbonded.");
+ disconnect(device);
return;
}
removeStateMachine(device);
@@ -1221,8 +1224,8 @@ public class HapClientService extends ProfileService {
if (mIsTesting) {
return mService;
}
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
Log.w(TAG, "Hearing Access call not allowed for non-active user");
return null;
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java
index 9ef2678c8b8a070fc1c8da73350032e23aef4d70..a2aabd05aacbe10856bbb4d36c90ebe95bf8c21c 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java
@@ -128,14 +128,16 @@ public class HearingAidNativeInterface {
return mAdapter.getRemoteDevice(address);
}
- private byte[] getByteAddress(BluetoothDevice device) {
+ @VisibleForTesting
+ byte[] getByteAddress(BluetoothDevice device) {
if (device == null) {
return Utils.getBytesFromAddress("00:00:00:00:00:00");
}
return Utils.getBytesFromAddress(device.getAddress());
}
- private void sendMessageToService(HearingAidStackEvent event) {
+ @VisibleForTesting
+ void sendMessageToService(HearingAidStackEvent event) {
HearingAidService service = HearingAidService.getHearingAidService();
if (service != null) {
service.messageFromNative(event);
@@ -148,7 +150,8 @@ public class HearingAidNativeInterface {
// All callbacks are routed via the Service which will disambiguate which
// state machine the message should be routed to.
- private void onConnectionStateChanged(int state, byte[] address) {
+ @VisibleForTesting
+ void onConnectionStateChanged(int state, byte[] address) {
HearingAidStackEvent event =
new HearingAidStackEvent(HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
event.device = getDevice(address);
@@ -160,7 +163,8 @@ public class HearingAidNativeInterface {
sendMessageToService(event);
}
- private void onDeviceAvailable(byte capabilities, long hiSyncId, byte[] address) {
+ @VisibleForTesting
+ void onDeviceAvailable(byte capabilities, long hiSyncId, byte[] address) {
HearingAidStackEvent event = new HearingAidStackEvent(
HearingAidStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
event.device = getDevice(address);
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
index 6da45bd84ec21fc22c65df4ac184ca93b953a57c..7f51e42d4b4f1e7cd2c9464aa688c1ec72abf898 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -29,9 +29,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.BluetoothProfileConnectionInfo;
+import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.ParcelUuid;
import android.sysprop.BluetoothProperties;
import android.util.Log;
@@ -73,6 +77,7 @@ public class HearingAidService extends ProfileService {
private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
private BluetoothDevice mPreviousAudioDevice;
+ private BluetoothDevice mActiveDevice;
@VisibleForTesting
HearingAidNativeInterface mHearingAidNativeInterface;
@@ -88,6 +93,12 @@ public class HearingAidService extends ProfileService {
private BroadcastReceiver mBondStateChangedReceiver;
private BroadcastReceiver mConnectionStateChangedReceiver;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private final AudioManagerOnAudioDevicesAddedCallback mAudioManagerOnAudioDevicesAddedCallback =
+ new AudioManagerOnAudioDevicesAddedCallback();
+ private final AudioManagerOnAudioDevicesRemovedCallback
+ mAudioManagerOnAudioDevicesRemovedCallback =
+ new AudioManagerOnAudioDevicesRemovedCallback();
private final ServiceFactory mFactory = new ServiceFactory();
@@ -206,6 +217,9 @@ public class HearingAidService extends ProfileService {
}
}
+ mAudioManager.unregisterAudioDeviceCallback(mAudioManagerOnAudioDevicesAddedCallback);
+ mAudioManager.unregisterAudioDeviceCallback(mAudioManagerOnAudioDevicesRemovedCallback);
+
// Clear AdapterService, HearingAidNativeInterface
mAudioManager = null;
mHearingAidNativeInterface = null;
@@ -238,7 +252,8 @@ public class HearingAidService extends ProfileService {
return sHearingAidService;
}
- private static synchronized void setHearingAidService(HearingAidService instance) {
+ @VisibleForTesting
+ static synchronized void setHearingAidService(HearingAidService instance) {
if (DBG) {
Log.d(TAG, "setHearingAidService(): set to: " + instance);
}
@@ -582,6 +597,12 @@ public class HearingAidService extends ProfileService {
}
return true;
}
+
+ /* No action needed since this is the same device as previousely activated */
+ if (device.equals(mActiveDevice)) {
+ return true;
+ }
+
if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
Log.e(TAG, "setActiveDevice(" + device + "): failed because device not connected");
return false;
@@ -671,6 +692,46 @@ public class HearingAidService extends ProfileService {
}
}
+ private void notifyActiveDeviceChanged() {
+ Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mActiveDevice);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ sendBroadcast(intent, BLUETOOTH_CONNECT);
+ }
+
+ /* Notifications of audio device disconnection events. */
+ private class AudioManagerOnAudioDevicesRemovedCallback extends AudioDeviceCallback {
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ for (AudioDeviceInfo deviceInfo : removedDevices) {
+ if (deviceInfo.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
+ notifyActiveDeviceChanged();
+ if (DBG) {
+ Log.d(TAG, " onAudioDevicesRemoved: device type: " + deviceInfo.getType());
+ }
+ mAudioManager.unregisterAudioDeviceCallback(this);
+ }
+ }
+ }
+ }
+
+ /* Notifications of audio device connection events. */
+ private class AudioManagerOnAudioDevicesAddedCallback extends AudioDeviceCallback {
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ for (AudioDeviceInfo deviceInfo : addedDevices) {
+ if (deviceInfo.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
+ notifyActiveDeviceChanged();
+ if (DBG) {
+ Log.d(TAG, " onAudioDevicesAdded: device type: " + deviceInfo.getType());
+ }
+ mAudioManager.unregisterAudioDeviceCallback(this);
+ }
+ }
+ }
+ }
+
private HearingAidStateMachine getOrCreateStateMachine(BluetoothDevice device) {
if (device == null) {
Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
@@ -706,6 +767,8 @@ public class HearingAidService extends ProfileService {
Log.d(TAG, "reportActiveDevice(" + device + ")");
}
+ mActiveDevice = device;
+
BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED,
BluetoothProfile.HEARING_AID, mAdapterService.obfuscateAddress(device),
mAdapterService.getMetricId(device));
@@ -722,6 +785,15 @@ public class HearingAidService extends ProfileService {
Log.d(TAG, "Hearing Aid audio: " + mPreviousAudioDevice + " -> " + device
+ ". Stop audio: " + stopAudio);
}
+
+ if (device != null) {
+ mAudioManager.registerAudioDeviceCallback(mAudioManagerOnAudioDevicesAddedCallback,
+ mHandler);
+ } else {
+ mAudioManager.registerAudioDeviceCallback(mAudioManagerOnAudioDevicesRemovedCallback,
+ mHandler);
+ }
+
mAudioManager.handleBluetoothActiveDeviceChanged(device, mPreviousAudioDevice,
BluetoothProfileConnectionInfo.createHearingAidInfo(!stopAudio));
mPreviousAudioDevice = device;
@@ -767,6 +839,8 @@ public class HearingAidService extends ProfileService {
return;
}
if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+ Log.i(TAG, "Disconnecting device because it was unbonded.");
+ disconnect(device);
return;
}
removeStateMachine(device);
@@ -818,7 +892,6 @@ public class HearingAidService extends ProfileService {
BluetoothMetricsProto.ProfileId.HEARING_AID);
}
if (!mHiSyncIdConnectedMap.getOrDefault(myHiSyncId, false)) {
- setActiveDevice(device);
mHiSyncIdConnectedMap.put(myHiSyncId, true);
}
}
@@ -867,8 +940,8 @@ public class HearingAidService extends ProfileService {
if (mIsTesting) {
return mService;
}
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
- || !Utils.checkServiceAvailable(mService, TAG)
+ if (!Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
return null;
}
diff --git a/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java b/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java
index 12f1c4c40ebe672ba42b8ad2219288a0c1d4eb06..8f273aa5389912fb28856b412adae834e9c40b57 100644
--- a/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java
+++ b/android/app/src/com/android/bluetooth/hfp/AtPhonebook.java
@@ -32,10 +32,12 @@ import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
+import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.R;
import com.android.bluetooth.Utils;
import com.android.bluetooth.util.DevicePolicyUtils;
import com.android.bluetooth.util.GsmAlphabet;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
@@ -70,7 +72,8 @@ public class AtPhonebook {
private static final String INCOMING_CALL_WHERE = Calls.TYPE + "=" + Calls.INCOMING_TYPE;
private static final String MISSED_CALL_WHERE = Calls.TYPE + "=" + Calls.MISSED_TYPE;
- private class PhonebookResult {
+ @VisibleForTesting
+ class PhonebookResult {
public Cursor cursor; // result set of last query
public int numberColumn;
public int numberPresentationColumn;
@@ -81,16 +84,20 @@ public class AtPhonebook {
private Context mContext;
private ContentResolver mContentResolver;
private HeadsetNativeInterface mNativeInterface;
- private String mCurrentPhonebook;
- private String mCharacterSet = "UTF-8";
+ @VisibleForTesting
+ String mCurrentPhonebook;
+ @VisibleForTesting
+ String mCharacterSet = "UTF-8";
- private int mCpbrIndex1, mCpbrIndex2;
+ @VisibleForTesting
+ int mCpbrIndex1, mCpbrIndex2;
private boolean mCheckingAccessPermission;
// package and class name to which we send intent to check phone book access permission
private final String mPairingPackage;
- private final HashMap mPhonebooks =
+ @VisibleForTesting
+ final HashMap mPhonebooks =
new HashMap(4);
static final int TYPE_UNKNOWN = -1;
@@ -387,7 +394,8 @@ public class AtPhonebook {
* If force then re-query that phonebook
* Returns null if the cursor is not ready
*/
- private synchronized PhonebookResult getPhonebookResult(String pb, boolean force) {
+ @VisibleForTesting
+ synchronized PhonebookResult getPhonebookResult(String pb, boolean force) {
if (pb == null) {
return null;
}
@@ -431,8 +439,8 @@ public class AtPhonebook {
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, where);
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, Calls.DEFAULT_SORT_ORDER);
queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, MAX_PHONEBOOK_SIZE);
- pbr.cursor = mContentResolver.query(Calls.CONTENT_URI, CALLS_PROJECTION,
- queryArgs, null);
+ pbr.cursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mContentResolver,
+ Calls.CONTENT_URI, CALLS_PROJECTION, queryArgs, null);
if (pbr.cursor == null) {
return false;
@@ -447,8 +455,8 @@ public class AtPhonebook {
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, where);
queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, MAX_PHONEBOOK_SIZE);
final Uri phoneContentUri = DevicePolicyUtils.getEnterprisePhoneUri(mContext);
- pbr.cursor = mContentResolver.query(phoneContentUri, PHONES_PROJECTION,
- queryArgs, null);
+ pbr.cursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mContentResolver,
+ phoneContentUri, PHONES_PROJECTION, queryArgs, null);
if (pbr.cursor == null) {
return false;
@@ -469,7 +477,8 @@ public class AtPhonebook {
mCheckingAccessPermission = false;
}
- private synchronized int getMaxPhoneBookSize(int currSize) {
+ @VisibleForTesting
+ synchronized int getMaxPhoneBookSize(int currSize) {
// some car kits ignore the current size and request max phone book
// size entries. Thus, it takes a long time to transfer all the
// entries. Use a heuristic to calculate the max phone book size
@@ -543,7 +552,7 @@ public class AtPhonebook {
// try caller id lookup
// TODO: This code is horribly inefficient. I saw it
// take 7 seconds to process 100 missed calls.
- Cursor c = mContentResolver.query(
+ Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mContentResolver,
Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, number),
new String[]{
PhoneLookup.DISPLAY_NAME, PhoneLookup.TYPE
@@ -632,7 +641,8 @@ public class AtPhonebook {
* @return {@link BluetoothDevice#ACCESS_UNKNOWN}, {@link BluetoothDevice#ACCESS_ALLOWED} or
* {@link BluetoothDevice#ACCESS_REJECTED}.
*/
- private int checkAccessPermission(BluetoothDevice remoteDevice) {
+ @VisibleForTesting
+ int checkAccessPermission(BluetoothDevice remoteDevice) {
log("checkAccessPermission");
int permission = remoteDevice.getPhonebookAccessPermission();
@@ -653,7 +663,8 @@ public class AtPhonebook {
return permission;
}
- private static String getPhoneType(int type) {
+ @VisibleForTesting
+ static String getPhoneType(int type) {
switch (type) {
case Phone.TYPE_HOME:
return "H";
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
index be6327ca0747682eeae03219d85d94290950ed97..59d3ecabe0664310e468cea0736b826313422f52 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
@@ -69,7 +69,7 @@ public class HeadsetNativeInterface {
} else {
// Service must call cleanup() when quiting and native stack shouldn't send any event
// after cleanup() -> cleanupNative() is called.
- Log.wtf(TAG, "FATAL: Stack sent event while service is not available: " + event);
+ Log.w(TAG, "Stack sent event while service is not available: " + event);
}
}
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index 275ae6397c8d7c3023250dbc0b4e48fc53ab3521..19bffa0c28628b96e07025e77fa0b4ebda332e13 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -23,6 +23,7 @@ import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
@@ -462,7 +463,8 @@ public class HeadsetService extends ProfileService {
/**
* Handlers for incoming service calls
*/
- private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
+ @VisibleForTesting
+ static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
implements IProfileServiceBinder {
private volatile HeadsetService mService;
@@ -477,7 +479,10 @@ public class HeadsetService extends ProfileService {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private HeadsetService getService(AttributionSource source) {
- if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
+ if (Utils.isInstrumentationTestMode()) {
+ return mService;
+ }
+ if (!Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
|| !Utils.checkServiceAvailable(mService, TAG)
|| !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
return null;
@@ -1927,7 +1932,12 @@ public class HeadsetService extends ProfileService {
return true;
}
- boolean isInbandRingingEnabled() {
+ /**
+ * Checks if headset devices are able to get inband ringing.
+ *
+ * @return True if inband ringing is enabled.
+ */
+ public boolean isInbandRingingEnabled() {
boolean isInbandRingingSupported = getResources().getBoolean(
com.android.bluetooth.R.bool.config_bluetooth_hfp_inband_ringing_support);
return isInbandRingingSupported && !SystemProperties.getBoolean(
@@ -2176,13 +2186,35 @@ public class HeadsetService extends ProfileService {
/**
* Retrieves the most recently connected device in the A2DP connected devices list.
*/
- private BluetoothDevice getFallbackDevice() {
+ public BluetoothDevice getFallbackDevice() {
DatabaseManager dbManager = mAdapterService.getDatabase();
return dbManager != null ? dbManager
- .getMostRecentlyConnectedDevicesInList(getConnectedDevices())
+ .getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager))
: null;
}
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ List getFallbackCandidates(DatabaseManager dbManager) {
+ List fallbackCandidates = getConnectedDevices();
+ List uninterestedCandidates = new ArrayList<>();
+ for (BluetoothDevice device : fallbackCandidates) {
+ byte[] deviceType = dbManager.getCustomMeta(device,
+ BluetoothDevice.METADATA_DEVICE_TYPE);
+ BluetoothClass deviceClass = device.getBluetoothClass();
+ if ((deviceClass != null
+ && deviceClass.getMajorDeviceClass()
+ == BluetoothClass.Device.WEARABLE_WRIST_WATCH)
+ || (deviceType != null
+ && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) {
+ uninterestedCandidates.add(device);
+ }
+ }
+ for (BluetoothDevice device : uninterestedCandidates) {
+ fallbackCandidates.remove(device);
+ }
+ return fallbackCandidates;
+ }
+
@Override
public void dump(StringBuilder sb) {
boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn();
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index e431c43bde04082a69d5fcfc2766c1f24f40a43c..b237e7d6cc647b77df87ee2bb6550c9af3188682 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -136,8 +136,10 @@ public class HeadsetStateMachine extends StateMachine {
private final HeadsetSystemInterface mSystemInterface;
// Runtime states
- private int mSpeakerVolume;
- private int mMicVolume;
+ @VisibleForTesting
+ int mSpeakerVolume;
+ @VisibleForTesting
+ int mMicVolume;
private boolean mDeviceSilenced;
private HeadsetAgIndicatorEnableState mAgIndicatorEnableState;
// The timestamp when the device entered connecting/connected state
@@ -146,7 +148,8 @@ public class HeadsetStateMachine extends StateMachine {
private boolean mHasNrecEnabled = false;
private boolean mHasWbsEnabled = false;
// AT Phone book keeps a group of states used by AT+CPBR commands
- private final AtPhonebook mPhonebook;
+ @VisibleForTesting
+ final AtPhonebook mPhonebook;
// HSP specific
private boolean mNeedDialingOutReply;
// Audio disconnect timeout retry count
@@ -1516,7 +1519,8 @@ public class HeadsetStateMachine extends StateMachine {
/*
* Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
*/
- private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
+ @VisibleForTesting
+ void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
Object[] arguments, BluetoothDevice device) {
log("broadcastVendorSpecificEventIntent(" + command + ")");
Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
@@ -1540,7 +1544,8 @@ public class HeadsetStateMachine extends StateMachine {
am.setBluetoothHeadsetProperties(getCurrentDeviceName(), mHasNrecEnabled, mHasWbsEnabled);
}
- private String parseUnknownAt(String atString) {
+ @VisibleForTesting
+ String parseUnknownAt(String atString) {
StringBuilder atCommand = new StringBuilder(atString.length());
for (int i = 0; i < atString.length(); i++) {
@@ -1561,7 +1566,8 @@ public class HeadsetStateMachine extends StateMachine {
return atCommand.toString();
}
- private int getAtCommandType(String atCommand) {
+ @VisibleForTesting
+ int getAtCommandType(String atCommand) {
int commandType = AtPhonebook.TYPE_UNKNOWN;
String atString = null;
atCommand = atCommand.trim();
@@ -1642,7 +1648,8 @@ public class HeadsetStateMachine extends StateMachine {
}
}
- private void processVolumeEvent(int volumeType, int volume) {
+ @VisibleForTesting
+ void processVolumeEvent(int volumeType, int volume) {
// Only current active device can change SCO volume
if (!mDevice.equals(mHeadsetService.getActiveDevice())) {
Log.w(TAG, "processVolumeEvent, ignored because " + mDevice + " is not active");
@@ -1691,7 +1698,8 @@ public class HeadsetStateMachine extends StateMachine {
}
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- private void processAtChld(int chld, BluetoothDevice device) {
+ @VisibleForTesting
+ void processAtChld(int chld, BluetoothDevice device) {
if (mSystemInterface.processChld(chld)) {
mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
} else {
@@ -1700,7 +1708,8 @@ public class HeadsetStateMachine extends StateMachine {
}
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- private void processSubscriberNumberRequest(BluetoothDevice device) {
+ @VisibleForTesting
+ void processSubscriberNumberRequest(BluetoothDevice device) {
String number = mSystemInterface.getSubscriberNumber();
if (number != null) {
mNativeInterface.atResponseString(device,
@@ -1735,7 +1744,8 @@ public class HeadsetStateMachine extends StateMachine {
}
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- private void processAtCops(BluetoothDevice device) {
+ @VisibleForTesting
+ void processAtCops(BluetoothDevice device) {
// Get operator name suggested by Telephony
String operatorName = null;
ServiceState serviceState = mSystemInterface.getHeadsetPhoneState().getServiceState();
@@ -1756,7 +1766,8 @@ public class HeadsetStateMachine extends StateMachine {
}
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- private void processAtClcc(BluetoothDevice device) {
+ @VisibleForTesting
+ void processAtClcc(BluetoothDevice device) {
if (mHeadsetService.isVirtualCallStarted()) {
// In virtual call, send our phone number instead of remote phone number
String phoneNumber = mSystemInterface.getSubscriberNumber();
@@ -1777,7 +1788,8 @@ public class HeadsetStateMachine extends StateMachine {
}
}
- private void processAtCscs(String atString, int type, BluetoothDevice device) {
+ @VisibleForTesting
+ void processAtCscs(String atString, int type, BluetoothDevice device) {
log("processAtCscs - atString = " + atString);
if (mPhonebook != null) {
mPhonebook.handleCscsCommand(atString, type, device);
@@ -1787,7 +1799,8 @@ public class HeadsetStateMachine extends StateMachine {
}
}
- private void processAtCpbs(String atString, int type, BluetoothDevice device) {
+ @VisibleForTesting
+ void processAtCpbs(String atString, int type, BluetoothDevice device) {
log("processAtCpbs - atString = " + atString);
if (mPhonebook != null) {
mPhonebook.handleCpbsCommand(atString, type, device);
@@ -1797,7 +1810,8 @@ public class HeadsetStateMachine extends StateMachine {
}
}
- private void processAtCpbr(String atString, int type, BluetoothDevice device) {
+ @VisibleForTesting
+ void processAtCpbr(String atString, int type, BluetoothDevice device) {
log("processAtCpbr - atString = " + atString);
if (mPhonebook != null) {
mPhonebook.handleCpbrCommand(atString, type, device);
@@ -1811,7 +1825,8 @@ public class HeadsetStateMachine extends StateMachine {
* Find a character ch, ignoring quoted sections.
* Return input.length() if not found.
*/
- private static int findChar(char ch, String input, int fromIndex) {
+ @VisibleForTesting
+ static int findChar(char ch, String input, int fromIndex) {
for (int i = fromIndex; i < input.length(); i++) {
char c = input.charAt(i);
if (c == '"') {
@@ -1831,7 +1846,8 @@ public class HeadsetStateMachine extends StateMachine {
* Integer arguments are turned into Integer objects. Otherwise a String
* object is used.
*/
- private static Object[] generateArgs(String input) {
+ @VisibleForTesting
+ static Object[] generateArgs(String input) {
int i = 0;
int j;
ArrayList