diff --git a/android/app/.clang-format b/android/app/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..85098f2a60c122814316bf6b34ebf23851ba373a --- /dev/null +++ b/android/app/.clang-format @@ -0,0 +1,30 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Below are some minor deviations from the default Google style to +# accommodate for handling of the large legacy code base. +# + +BasedOnStyle: Google +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false + +--- +Language: Java +# Java format is handled by check_style hook +DisableFormat: true +SortIncludes: false diff --git a/android/app/Android.bp b/android/app/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..347b22b03964f2cc89621bcc162e98d5e63e2e4b --- /dev/null +++ b/android/app/Android.bp @@ -0,0 +1,142 @@ +// MAP API module + +package { + default_applicable_licenses: ["packages_apps_Bluetooth_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "packages_apps_Bluetooth_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + // large-scale-change unable to identify any license_text files +} + +java_library { + name: "bluetooth.mapsapi", + + srcs: ["lib/mapapi/**/*.java"], +} + +java_library { + name: "mmslib", + + srcs: [":framework-mms-shared-srcs"], + libs: ["unsupportedappusage"], +} + +// Bluetooth JNI + +cc_library_shared { + name: "libbluetooth_jni", + srcs: ["jni/**/*.cpp"], + header_libs: [ + "jni_headers", + "libbluetooth_headers", + ], + include_dirs: [ + "packages/modules/Bluetooth/system/types", + ], + shared_libs: [ + "libbase", + "libchrome", + "liblog", + "libnativehelper", + ], + static_libs: [ + "libbluetooth-types", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + "-Wno-unused-parameter", + ], + sanitize: { + scs: true, + }, +} + +// Bluetooth APK + +android_app { + name: "Bluetooth", + defaults: ["platform_app_defaults"], + + srcs: [ + "src/**/*.java", + ":statslog-bluetooth-java-gen", + ], + platform_apis: true, + certificate: "platform", + + jni_libs: ["libbluetooth_jni"], + libs: [ + "javax.obex", + "services.net", + ], + static_libs: [ + "com.android.vcard", + "bluetooth.mapsapi", + "sap-api-java-static", + "services.net", + "libprotobuf-java-lite", + "bluetooth-protos-lite", + "androidx.core_core", + "androidx.legacy_legacy-support-v4", + "androidx.lifecycle_lifecycle-livedata", + "modules-utils-statemachine", + "androidx.room_room-runtime", + "guava", + "android.hardware.radio-V1.0-java", + "mmslib", + "modules-utils-backgroundthread", + "modules-utils-bytesmatcher", + ], + + plugins: [ + "androidx.room_room-compiler-plugin", + ], + + // Add in path to Bluetooth directory because local path does not exist + javacflags: ["-Aroom.schemaLocation=packages/apps/Bluetooth/tests/unit/src/com/android/bluetooth/btservice/storage/schemas"], + + optimize: { + enabled: false, + }, + required: ["libbluetooth"], + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth.updatable", + ], + errorprone: { + javacflags: [ + // "-Xep:AndroidFrameworkRequiresPermission:ERROR", + ], + }, +} + +genrule { + name: "statslog-bluetooth-java-gen", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --java $(out) --module bluetooth" + + " --javaPackage com.android.bluetooth --javaClass BluetoothStatsLog" + + " --minApiLevel 32", + out: ["com/android/bluetooth/BluetoothStatsLog.java"], +} diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..c4cd0511621f4cfcb2a952aac5c864ba49fb1c2d --- /dev/null +++ b/android/app/AndroidManifest.xml @@ -0,0 +1,475 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/OWNERS b/android/app/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..4e4e11988b6239917e8a8a01aef13b84a39a5f8e --- /dev/null +++ b/android/app/OWNERS @@ -0,0 +1 @@ +baligh@google.com diff --git a/android/app/PREUPLOAD.cfg b/android/app/PREUPLOAD.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d0c92d327c3a7d82a9b06805fa46810aa8d7f108 --- /dev/null +++ b/android/app/PREUPLOAD.cfg @@ -0,0 +1,12 @@ +[Options] +ignore_merged_commits = true + +[Builtin Hooks] +clang_format = true + +[Hook Scripts] +checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} + -fw src/com/android/bluetooth/ + lib/mapapi/com/android/bluetooth/mapapi/ + tests/src/com/android/bluetooth/ +aosp_first = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES} diff --git a/android/app/jni/com_android_bluetooth.h b/android/app/jni/com_android_bluetooth.h new file mode 100644 index 0000000000000000000000000000000000000000..5c16f042f2ad46472b1152d80f87dd0736727379 --- /dev/null +++ b/android/app/jni/com_android_bluetooth.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2012 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. + */ + +#ifndef COM_ANDROID_BLUETOOTH_H +#define COM_ANDROID_BLUETOOTH_H + +#include "hardware/bluetooth.h" +#include "hardware/hardware.h" +#include "jni.h" +#include "jni_logging.h" +#include "nativehelper/ScopedLocalRef.h" +#include "utils/Log.h" + +namespace android { + +JNIEnv* getCallbackEnv(); +bool isCallbackThread(); + +class CallbackEnv { +public: + CallbackEnv(const char *methodName) : mName(methodName) { + mCallbackEnv = getCallbackEnv(); + } + + ~CallbackEnv() { + if (mCallbackEnv && mCallbackEnv->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", mName); + LOGE_EX(mCallbackEnv); + mCallbackEnv->ExceptionClear(); + } + } + + bool valid() const { + if (!mCallbackEnv || !isCallbackThread()) { + ALOGE("%s: Callback env fail", mName); + return false; + } + return true; + } + + // stolen from art/runtime/jni/check_jni.cc + bool isValidUtf(const char* bytes) const { + while (*bytes != '\0') { + const uint8_t* utf8 = reinterpret_cast(bytes++); + // Switch on the high four bits. + switch (*utf8 >> 4) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + // Bit pattern 0xxx. No need for any extra bytes. + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + // Bit patterns 10xx, which are illegal start bytes. + return false; + case 0x0f: + // Bit pattern 1111, which might be the start of a 4 byte sequence. + if ((*utf8 & 0x08) == 0) { + // Bit pattern 1111 0xxx, which is the start of a 4 byte sequence. + // We consume one continuation byte here, and fall through to + // consume two more. + utf8 = reinterpret_cast(bytes++); + if ((*utf8 & 0xc0) != 0x80) { + return false; + } + } else { + return false; + } + // Fall through to the cases below to consume two more continuation + // bytes. + FALLTHROUGH_INTENDED; + case 0x0e: + // Bit pattern 1110, so there are two additional bytes. + utf8 = reinterpret_cast(bytes++); + if ((*utf8 & 0xc0) != 0x80) { + return false; + } + // Fall through to consume one more continuation byte. + FALLTHROUGH_INTENDED; + case 0x0c: + case 0x0d: + // Bit pattern 110x, so there is one additional byte. + utf8 = reinterpret_cast(bytes++); + if ((*utf8 & 0xc0) != 0x80) { + return false; + } + break; + } + } + return true; + } + + JNIEnv *operator-> () const { + return mCallbackEnv; + } + + JNIEnv *get() const { + return mCallbackEnv; + } + +private: + JNIEnv *mCallbackEnv; + const char *mName; + + DISALLOW_COPY_AND_ASSIGN(CallbackEnv); +}; + +const bt_interface_t* getBluetoothInterface(); + +int register_com_android_bluetooth_hfp(JNIEnv* env); + +int register_com_android_bluetooth_hfpclient(JNIEnv* env); + +int register_com_android_bluetooth_a2dp(JNIEnv* env); + +int register_com_android_bluetooth_a2dp_sink(JNIEnv* env); + +int register_com_android_bluetooth_avrcp(JNIEnv* env); + +int register_com_android_bluetooth_avrcp_target(JNIEnv* env); + +int register_com_android_bluetooth_avrcp_controller(JNIEnv* env); + +int register_com_android_bluetooth_hid_host(JNIEnv* env); + +int register_com_android_bluetooth_hid_device(JNIEnv* env); + +int register_com_android_bluetooth_pan(JNIEnv* env); + +int register_com_android_bluetooth_gatt(JNIEnv* env); + +int register_com_android_bluetooth_sdp(JNIEnv* env); + +int register_com_android_bluetooth_hearing_aid(JNIEnv* env); + +int register_com_android_bluetooth_btservice_BluetoothKeystore(JNIEnv* env); + +int register_com_android_bluetooth_btservice_activity_attribution(JNIEnv* env); + +int register_com_android_bluetooth_le_audio(JNIEnv* env); + +int register_com_android_bluetooth_vc(JNIEnv* env); + +int register_com_android_bluetooth_csip_set_coordinator(JNIEnv* env); +} // namespace android + +#endif /* COM_ANDROID_BLUETOOTH_H */ diff --git a/android/app/jni/com_android_bluetooth_a2dp.cpp b/android/app/jni/com_android_bluetooth_a2dp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abc8830b518198eb0132c6e4a12c1fdbe7b5983b --- /dev/null +++ b/android/app/jni/com_android_bluetooth_a2dp.cpp @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothA2dpServiceJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_av.h" +#include "utils/Log.h" + +#include +#include + +namespace android { +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onAudioStateChanged; +static jmethodID method_onCodecConfigChanged; +static jmethodID method_isMandatoryCodecPreferred; + +static struct { + jclass clazz; + jmethodID constructor; + jmethodID getCodecType; + jmethodID getCodecPriority; + jmethodID getSampleRate; + jmethodID getBitsPerSample; + jmethodID getChannelMode; + jmethodID getCodecSpecific1; + jmethodID getCodecSpecific2; + jmethodID getCodecSpecific3; + jmethodID getCodecSpecific4; +} android_bluetooth_BluetoothCodecConfig; + +static const btav_source_interface_t* sBluetoothA2dpInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +static void bta2dp_connection_state_callback(const RawAddress& bd_addr, + btav_connection_state_t state) { + ALOGI("%s", __func__); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Fail to new jbyteArray bd addr", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion( + addr.get(), 0, sizeof(RawAddress), + reinterpret_cast(bd_addr.address)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + addr.get(), (jint)state); +} + +static void bta2dp_audio_state_callback(const RawAddress& bd_addr, + btav_audio_state_t state) { + ALOGI("%s", __func__); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Fail to new jbyteArray bd addr", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion( + addr.get(), 0, sizeof(RawAddress), + reinterpret_cast(bd_addr.address)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, + addr.get(), (jint)state); +} + +static void bta2dp_audio_config_callback( + const RawAddress& bd_addr, btav_a2dp_codec_config_t codec_config, + std::vector codecs_local_capabilities, + std::vector codecs_selectable_capabilities) { + ALOGI("%s", __func__); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + jobject codecConfigObj = sCallbackEnv->NewObject( + android_bluetooth_BluetoothCodecConfig.clazz, + android_bluetooth_BluetoothCodecConfig.constructor, + (jint)codec_config.codec_type, (jint)codec_config.codec_priority, + (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample, + (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1, + (jlong)codec_config.codec_specific_2, + (jlong)codec_config.codec_specific_3, + (jlong)codec_config.codec_specific_4); + + jsize i = 0; + jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray( + (jsize)codecs_local_capabilities.size(), + android_bluetooth_BluetoothCodecConfig.clazz, nullptr); + for (auto const& cap : codecs_local_capabilities) { + jobject capObj = sCallbackEnv->NewObject( + android_bluetooth_BluetoothCodecConfig.clazz, + android_bluetooth_BluetoothCodecConfig.constructor, + (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate, + (jint)cap.bits_per_sample, (jint)cap.channel_mode, + (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2, + (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4); + sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj); + sCallbackEnv->DeleteLocalRef(capObj); + } + + i = 0; + jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray( + (jsize)codecs_selectable_capabilities.size(), + android_bluetooth_BluetoothCodecConfig.clazz, nullptr); + for (auto const& cap : codecs_selectable_capabilities) { + jobject capObj = sCallbackEnv->NewObject( + android_bluetooth_BluetoothCodecConfig.clazz, + android_bluetooth_BluetoothCodecConfig.constructor, + (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate, + (jint)cap.bits_per_sample, (jint)cap.channel_mode, + (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2, + (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4); + sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++, + capObj); + sCallbackEnv->DeleteLocalRef(capObj); + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength)); + if (!addr.get()) { + ALOGE("%s: Fail to new jbyteArray bd addr", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion( + addr.get(), 0, RawAddress::kLength, + reinterpret_cast(bd_addr.address)); + + sCallbackEnv->CallVoidMethod( + mCallbacksObj, method_onCodecConfigChanged, addr.get(), codecConfigObj, + local_capabilities_array, selectable_capabilities_array); +} + +static bool bta2dp_mandatory_codec_preferred_callback( + const RawAddress& bd_addr) { + ALOGI("%s", __func__); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return false; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength)); + if (!addr.get()) { + ALOGE("%s: Fail to new jbyteArray bd addr", __func__); + return false; + } + sCallbackEnv->SetByteArrayRegion( + addr.get(), 0, RawAddress::kLength, + reinterpret_cast(bd_addr.address)); + return sCallbackEnv->CallBooleanMethod( + mCallbacksObj, method_isMandatoryCodecPreferred, addr.get()); +} + +static btav_source_callbacks_t sBluetoothA2dpCallbacks = { + sizeof(sBluetoothA2dpCallbacks), + bta2dp_connection_state_callback, + bta2dp_audio_state_callback, + bta2dp_audio_config_callback, + bta2dp_mandatory_codec_preferred_callback, +}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + jclass jniBluetoothCodecConfigClass = + env->FindClass("android/bluetooth/BluetoothCodecConfig"); + android_bluetooth_BluetoothCodecConfig.constructor = + env->GetMethodID(jniBluetoothCodecConfigClass, "", "(IIIIIJJJJ)V"); + android_bluetooth_BluetoothCodecConfig.getCodecType = + env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I"); + android_bluetooth_BluetoothCodecConfig.getCodecPriority = + env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I"); + android_bluetooth_BluetoothCodecConfig.getSampleRate = + env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I"); + android_bluetooth_BluetoothCodecConfig.getBitsPerSample = + env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I"); + android_bluetooth_BluetoothCodecConfig.getChannelMode = + env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I"); + android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID( + jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J"); + android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID( + jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J"); + android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID( + jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J"); + android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID( + jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J"); + + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); + + method_onAudioStateChanged = + env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V"); + + method_onCodecConfigChanged = + env->GetMethodID(clazz, "onCodecConfigChanged", + "([BLandroid/bluetooth/BluetoothCodecConfig;" + "[Landroid/bluetooth/BluetoothCodecConfig;" + "[Landroid/bluetooth/BluetoothCodecConfig;)V"); + + method_isMandatoryCodecPreferred = + env->GetMethodID(clazz, "isMandatoryCodecPreferred", "([B)Z"); + + ALOGI("%s: succeeds", __func__); +} + +static std::vector prepareCodecPreferences( + JNIEnv* env, jobject object, jobjectArray codecConfigArray) { + std::vector codec_preferences; + + int numConfigs = env->GetArrayLength(codecConfigArray); + for (int i = 0; i < numConfigs; i++) { + jobject jcodecConfig = env->GetObjectArrayElement(codecConfigArray, i); + if (jcodecConfig == nullptr) continue; + if (!env->IsInstanceOf(jcodecConfig, + android_bluetooth_BluetoothCodecConfig.clazz)) { + ALOGE("%s: Invalid BluetoothCodecConfig instance", __func__); + continue; + } + jint codecType = env->CallIntMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType); + jint codecPriority = env->CallIntMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority); + jint sampleRate = env->CallIntMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate); + jint bitsPerSample = env->CallIntMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample); + jint channelMode = env->CallIntMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode); + jlong codecSpecific1 = env->CallLongMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1); + jlong codecSpecific2 = env->CallLongMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2); + jlong codecSpecific3 = env->CallLongMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3); + jlong codecSpecific4 = env->CallLongMethod( + jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4); + + btav_a2dp_codec_config_t codec_config = { + .codec_type = static_cast(codecType), + .codec_priority = + static_cast(codecPriority), + .sample_rate = static_cast(sampleRate), + .bits_per_sample = + static_cast(bitsPerSample), + .channel_mode = + static_cast(channelMode), + .codec_specific_1 = codecSpecific1, + .codec_specific_2 = codecSpecific2, + .codec_specific_3 = codecSpecific3, + .codec_specific_4 = codecSpecific4}; + + codec_preferences.push_back(codec_config); + } + return codec_preferences; +} + +static void initNative(JNIEnv* env, jobject object, + jint maxConnectedAudioDevices, + jobjectArray codecConfigArray, + jobjectArray codecOffloadingArray) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + ALOGE("%s: Bluetooth module is not loaded", __func__); + return; + } + + if (sBluetoothA2dpInterface != nullptr) { + ALOGW("%s: Cleaning up A2DP Interface before initializing...", __func__); + sBluetoothA2dpInterface->cleanup(); + sBluetoothA2dpInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + ALOGW("%s: Cleaning up A2DP callback object", __func__); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + ALOGE("%s: Failed to allocate Global Ref for A2DP Callbacks", __func__); + return; + } + + android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef( + env->FindClass("android/bluetooth/BluetoothCodecConfig")); + if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) { + ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class", + __func__); + return; + } + + sBluetoothA2dpInterface = + (btav_source_interface_t*)btInf->get_profile_interface( + BT_PROFILE_ADVANCED_AUDIO_ID); + if (sBluetoothA2dpInterface == nullptr) { + ALOGE("%s: Failed to get Bluetooth A2DP Interface", __func__); + return; + } + + std::vector codec_priorities = + prepareCodecPreferences(env, object, codecConfigArray); + + std::vector codec_offloading = + prepareCodecPreferences(env, object, codecOffloadingArray); + + bt_status_t status = sBluetoothA2dpInterface->init( + &sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities, + codec_offloading); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed to initialize Bluetooth A2DP, status: %d", __func__, + status); + sBluetoothA2dpInterface = nullptr; + return; + } +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + ALOGE("%s: Bluetooth module is not loaded", __func__); + return; + } + + if (sBluetoothA2dpInterface != nullptr) { + sBluetoothA2dpInterface->cleanup(); + sBluetoothA2dpInterface = nullptr; + } + + env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz); + android_bluetooth_BluetoothCodecConfig.clazz = nullptr; + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static jboolean connectA2dpNative(JNIEnv* env, jobject object, + jbyteArray address) { + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + std::shared_lock lock(interface_mutex); + if (!sBluetoothA2dpInterface) { + ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress bd_addr; + bd_addr.FromOctets(reinterpret_cast(addr)); + bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed A2DP connection, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean disconnectA2dpNative(JNIEnv* env, jobject object, + jbyteArray address) { + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + std::shared_lock lock(interface_mutex); + if (!sBluetoothA2dpInterface) { + ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress bd_addr; + bd_addr.FromOctets(reinterpret_cast(addr)); + bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed A2DP disconnection, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setSilenceDeviceNative(JNIEnv* env, jobject object, + jbyteArray address, jboolean silence) { + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + std::shared_lock lock(interface_mutex); + if (!sBluetoothA2dpInterface) { + ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + + RawAddress bd_addr = RawAddress::kEmpty; + if (addr) { + bd_addr.FromOctets(reinterpret_cast(addr)); + } + if (bd_addr == RawAddress::kEmpty) { + return JNI_FALSE; + } + bt_status_t status = + sBluetoothA2dpInterface->set_silence_device(bd_addr, silence); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed A2DP set_silence_device, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setActiveDeviceNative(JNIEnv* env, jobject object, + jbyteArray address) { + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + std::shared_lock lock(interface_mutex); + if (!sBluetoothA2dpInterface) { + ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + + RawAddress bd_addr = RawAddress::kEmpty; + if (addr) { + bd_addr.FromOctets(reinterpret_cast(addr)); + } + bt_status_t status = sBluetoothA2dpInterface->set_active_device(bd_addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed A2DP set_active_device, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object, + jbyteArray address, + jobjectArray codecConfigArray) { + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + std::shared_lock lock(interface_mutex); + if (!sBluetoothA2dpInterface) { + ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress bd_addr; + bd_addr.FromOctets(reinterpret_cast(addr)); + std::vector codec_preferences = + prepareCodecPreferences(env, object, codecConfigArray); + + bt_status_t status = + sBluetoothA2dpInterface->config_codec(bd_addr, codec_preferences); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed codec configuration, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", + "(I[Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V", + (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative}, + {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative}, + {"setSilenceDeviceNative", "([BZ)Z", (void*)setSilenceDeviceNative}, + {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative}, + {"setCodecConfigPreferenceNative", + "([B[Landroid/bluetooth/BluetoothCodecConfig;)Z", + (void*)setCodecConfigPreferenceNative}, +}; + +int register_com_android_bluetooth_a2dp(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/a2dp/A2dpNativeInterface", sMethods, + NELEM(sMethods)); +} +} diff --git a/android/app/jni/com_android_bluetooth_a2dp_sink.cpp b/android/app/jni/com_android_bluetooth_a2dp_sink.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a74b18d6fbab4ba81fbe57ca148002f9856b66ee --- /dev/null +++ b/android/app/jni/com_android_bluetooth_a2dp_sink.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothA2dpSinkServiceJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_av.h" +#include "utils/Log.h" + +#include + +namespace android { +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onAudioStateChanged; +static jmethodID method_onAudioConfigChanged; + +static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL; +static jobject mCallbacksObj = NULL; + +static void bta2dp_connection_state_callback(const RawAddress& bd_addr, + btav_connection_state_t state) { + ALOGI("%s", __func__); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for connection state"); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (const jbyte*)bd_addr.address); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + addr.get(), (jint)state); +} + +static void bta2dp_audio_state_callback(const RawAddress& bd_addr, + btav_audio_state_t state) { + ALOGI("%s", __func__); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for connection state"); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (const jbyte*)bd_addr.address); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, + addr.get(), (jint)state); +} + +static void bta2dp_audio_config_callback(const RawAddress& bd_addr, + uint32_t sample_rate, + uint8_t channel_count) { + ALOGI("%s", __func__); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for connection state"); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (const jbyte*)bd_addr.address); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConfigChanged, + addr.get(), (jint)sample_rate, + (jint)channel_count); +} + +static btav_sink_callbacks_t sBluetoothA2dpCallbacks = { + sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback, + bta2dp_audio_state_callback, bta2dp_audio_config_callback, +}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); + + method_onAudioStateChanged = + env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V"); + + method_onAudioConfigChanged = + env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V"); + + ALOGI("%s: succeeds", __func__); +} + +static void initNative(JNIEnv* env, jobject object, + jint maxConnectedAudioDevices) { + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothA2dpInterface != NULL) { + ALOGW("Cleaning up A2DP Interface before initializing..."); + sBluetoothA2dpInterface->cleanup(); + sBluetoothA2dpInterface = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up A2DP callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + sBluetoothA2dpInterface = + (btav_sink_interface_t*)btInf->get_profile_interface( + BT_PROFILE_ADVANCED_AUDIO_SINK_ID); + if (sBluetoothA2dpInterface == NULL) { + ALOGE("Failed to get Bluetooth A2DP Sink Interface"); + return; + } + + bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, + maxConnectedAudioDevices); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status); + sBluetoothA2dpInterface = NULL; + return; + } + + mCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + const bt_interface_t* btInf = getBluetoothInterface(); + + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothA2dpInterface != NULL) { + sBluetoothA2dpInterface->cleanup(); + sBluetoothA2dpInterface = NULL; + } + + if (mCallbacksObj != NULL) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } +} + +static jboolean connectA2dpNative(JNIEnv* env, jobject object, + jbyteArray address) { + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + if (!sBluetoothA2dpInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress bd_addr; + bd_addr.FromOctets(reinterpret_cast(addr)); + bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF connection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean disconnectA2dpNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothA2dpInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress bd_addr; + bd_addr.FromOctets(reinterpret_cast(addr)); + bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF disconnection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static void informAudioFocusStateNative(JNIEnv* env, jobject object, + jint focus_state) { + if (!sBluetoothA2dpInterface) return; + sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state); +} + +static void informAudioTrackGainNative(JNIEnv* env, jobject object, + jfloat gain) { + if (!sBluetoothA2dpInterface) return; + sBluetoothA2dpInterface->set_audio_track_gain((float)gain); +} + +static jboolean setActiveDeviceNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothA2dpInterface) return JNI_FALSE; + + ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + bt_status_t status = sBluetoothA2dpInterface->set_active_device(rawAddress); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending passthru command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "(I)V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative}, + {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative}, + {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative}, + {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative}, + {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative}, +}; + +int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface", sMethods, + NELEM(sMethods)); +} +} diff --git a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp new file mode 100755 index 0000000000000000000000000000000000000000..86bf0687913e240dc2d33de8985bb739eec585c7 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp @@ -0,0 +1,1316 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BluetoothAvrcpControllerJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_rc.h" +#include "utils/Log.h" + +#include +#include + +namespace android { +static jmethodID method_handlePassthroughRsp; +static jmethodID method_onConnectionStateChanged; +static jmethodID method_getRcFeatures; +static jmethodID method_setplayerappsettingrsp; +static jmethodID method_handleplayerappsetting; +static jmethodID method_handleplayerappsettingchanged; +static jmethodID method_handleSetAbsVolume; +static jmethodID method_handleRegisterNotificationAbsVol; +static jmethodID method_handletrackchanged; +static jmethodID method_handleplaypositionchanged; +static jmethodID method_handleplaystatuschanged; +static jmethodID method_handleGetFolderItemsRsp; +static jmethodID method_handleGetPlayerItemsRsp; +static jmethodID method_handleGroupNavigationRsp; +static jmethodID method_createFromNativeMediaItem; +static jmethodID method_createFromNativeFolderItem; +static jmethodID method_createFromNativePlayerItem; +static jmethodID method_handleChangeFolderRsp; +static jmethodID method_handleSetBrowsedPlayerRsp; +static jmethodID method_handleSetAddressedPlayerRsp; +static jmethodID method_handleAddressedPlayerChanged; +static jmethodID method_handleNowPlayingContentChanged; +static jmethodID method_onAvailablePlayerChanged; +static jmethodID method_getRcPsm; + +static jclass class_AvrcpItem; +static jclass class_AvrcpPlayer; + +static const btrc_ctrl_interface_t* sBluetoothAvrcpInterface = NULL; +static jobject sCallbacksObj = NULL; +static std::shared_timed_mutex sCallbacks_mutex; + +static void btavrcp_passthrough_response_callback(const RawAddress& bd_addr, + int id, int pressed) { + ALOGI("%s: id: %d, pressed: %d", __func__, id, pressed); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handlePassthroughRsp, + (jint)id, (jint)pressed, addr.get()); +} + +static void btavrcp_groupnavigation_response_callback(int id, int pressed) { + ALOGV("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGroupNavigationRsp, + (jint)id, (jint)pressed); +} + +static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect, + const RawAddress& bd_addr) { + ALOGI("%s: conn state: rc: %d br: %d", __func__, rc_connect, br_connect); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_onConnectionStateChanged, + (jboolean)rc_connect, (jboolean)br_connect, + addr.get()); +} + +static void btavrcp_get_rcfeatures_callback(const RawAddress& bd_addr, + int features) { + ALOGV("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcFeatures, addr.get(), + (jint)features); +} + +static void btavrcp_setplayerapplicationsetting_rsp_callback( + const RawAddress& bd_addr, uint8_t accepted) { + ALOGV("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_setplayerappsettingrsp, + addr.get(), (jint)accepted); +} + +static void btavrcp_playerapplicationsetting_callback( + const RawAddress& bd_addr, uint8_t num_attr, + btrc_player_app_attr_t* app_attrs, uint8_t num_ext_attr, + btrc_player_app_ext_attr_t* ext_attrs) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + /* TODO ext attrs + * Flattening defined attributes: + */ + jint arraylen = 0; + for (int i = 0; i < num_attr; i++) { + /*2 bytes for id and num */ + arraylen += 2 + app_attrs[i].num_val; + } + ALOGV(" arraylen %d", arraylen); + + ScopedLocalRef playerattribs( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen)); + if (!playerattribs.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + for (int i = 0, k = 0; (i < num_attr) && (k < arraylen); i++) { + sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, + (jbyte*)&(app_attrs[i].attr_id)); + k++; + sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, + (jbyte*)&(app_attrs[i].num_val)); + k++; + sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, + app_attrs[i].num_val, + (jbyte*)(app_attrs[i].attr_val)); + k = k + app_attrs[i].num_val; + } + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplayerappsetting, + addr.get(), playerattribs.get(), (jint)arraylen); +} + +static void btavrcp_playerapplicationsetting_changed_callback( + const RawAddress& bd_addr, const btrc_player_settings_t& vals) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + int arraylen = vals.num_attr * 2; + ScopedLocalRef playerattribs( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen)); + if (!playerattribs.get()) { + ALOGE("Fail to new jbyteArray playerattribs "); + return; + } + /* + * Flatening format: + */ + for (int i = 0, k = 0; (i < vals.num_attr) && (k < arraylen); i++) { + sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, + (jbyte*)&(vals.attr_ids[i])); + k++; + sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, + (jbyte*)&(vals.attr_values[i])); + k++; + } + sCallbackEnv->CallVoidMethod(sCallbacksObj, + method_handleplayerappsettingchanged, addr.get(), + playerattribs.get(), (jint)arraylen); +} + +static void btavrcp_set_abs_vol_cmd_callback(const RawAddress& bd_addr, + uint8_t abs_vol, uint8_t label) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAbsVolume, + addr.get(), (jbyte)abs_vol, (jbyte)label); +} + +static void btavrcp_register_notification_absvol_callback( + const RawAddress& bd_addr, uint8_t label) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, + method_handleRegisterNotificationAbsVol, + addr.get(), (jbyte)label); +} + +static void btavrcp_track_changed_callback(const RawAddress& bd_addr, + uint8_t num_attr, + btrc_element_attr_val_t* p_attrs) { + /* + * byteArray will be formatted like this: id,len,string + * Assuming text feild to be null terminated. + */ + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + ScopedLocalRef attribIds(sCallbackEnv.get(), + sCallbackEnv->NewIntArray(num_attr)); + if (!attribIds.get()) { + ALOGE(" failed to set new array for attribIds"); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + jclass strclazz = sCallbackEnv->FindClass("java/lang/String"); + ScopedLocalRef stringArray( + sCallbackEnv.get(), + sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0)); + if (!stringArray.get()) { + ALOGE(" failed to get String array"); + return; + } + + for (jint i = 0; i < num_attr; i++) { + ScopedLocalRef str( + sCallbackEnv.get(), + sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text))); + if (!str.get()) { + ALOGE("Unable to get str"); + return; + } + sCallbackEnv->SetIntArrayRegion(attribIds.get(), i, 1, + (jint*)&(p_attrs[i].attr_id)); + sCallbackEnv->SetObjectArrayElement(stringArray.get(), i, str.get()); + } + + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handletrackchanged, + addr.get(), (jbyte)(num_attr), attribIds.get(), + stringArray.get()); +} + +static void btavrcp_play_position_changed_callback(const RawAddress& bd_addr, + uint32_t song_len, + uint32_t song_pos) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaypositionchanged, + addr.get(), (jint)(song_len), (jint)song_pos); +} + +static void btavrcp_play_status_changed_callback( + const RawAddress& bd_addr, btrc_play_status_t play_status) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaystatuschanged, + addr.get(), (jbyte)play_status); +} + +static void btavrcp_get_folder_items_callback( + const RawAddress& bd_addr, btrc_status_t status, + const btrc_folder_items_t* folder_items, uint8_t count) { + /* Folder items are list of items that can be either BTRC_ITEM_PLAYER + * BTRC_ITEM_MEDIA, BTRC_ITEM_FOLDER. Here we translate them to their java + * counterparts by calling the java constructor for each of the items. + */ + ALOGV("%s count %d", __func__, count); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + // Inspect if the first element is a folder/item or player listing. They are + // always exclusive. + bool isPlayerListing = + count > 0 && (folder_items[0].item_type == BTRC_ITEM_PLAYER); + + // Initialize arrays for Folder OR Player listing. + ScopedLocalRef itemArray(sCallbackEnv.get(), NULL); + if (isPlayerListing) { + itemArray.reset( + sCallbackEnv->NewObjectArray((jint)count, class_AvrcpPlayer, 0)); + } else { + itemArray.reset(sCallbackEnv->NewObjectArray( + (jint)count, class_AvrcpItem, 0)); + } + if (!itemArray.get()) { + ALOGE("%s itemArray allocation failed.", __func__); + return; + } + for (int i = 0; i < count; i++) { + const btrc_folder_items_t* item = &(folder_items[i]); + ALOGV("%s item type %d", __func__, item->item_type); + switch (item->item_type) { + case BTRC_ITEM_MEDIA: { + // Parse name + ScopedLocalRef mediaName( + sCallbackEnv.get(), + sCallbackEnv->NewStringUTF((const char*)item->media.name)); + if (!mediaName.get()) { + ALOGE("%s can't allocate media name string!", __func__); + return; + } + // Parse UID + long long uid = *(long long*)item->media.uid; + // Parse Attrs + ScopedLocalRef attrIdArray( + sCallbackEnv.get(), + sCallbackEnv->NewIntArray(item->media.num_attrs)); + if (!attrIdArray.get()) { + ALOGE("%s can't allocate attr id array!", __func__); + return; + } + ScopedLocalRef attrValArray( + sCallbackEnv.get(), + sCallbackEnv->NewObjectArray( + item->media.num_attrs, + sCallbackEnv->FindClass("java/lang/String"), 0)); + if (!attrValArray.get()) { + ALOGE("%s can't allocate attr val array!", __func__); + return; + } + + for (int j = 0; j < item->media.num_attrs; j++) { + sCallbackEnv->SetIntArrayRegion( + attrIdArray.get(), j, 1, + (jint*)&(item->media.p_attrs[j].attr_id)); + ScopedLocalRef attrValStr( + sCallbackEnv.get(), + sCallbackEnv->NewStringUTF((char*)(item->media.p_attrs[j].text))); + sCallbackEnv->SetObjectArrayElement(attrValArray.get(), j, + attrValStr.get()); + } + + ScopedLocalRef mediaObj( + sCallbackEnv.get(), + (jobject)sCallbackEnv->CallObjectMethod( + sCallbacksObj, method_createFromNativeMediaItem, addr.get(), + uid, (jint)item->media.type, mediaName.get(), + attrIdArray.get(), attrValArray.get())); + if (!mediaObj.get()) { + ALOGE("%s failed to create AvrcpItem for type ITEM_MEDIA", __func__); + return; + } + sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, mediaObj.get()); + break; + } + + case BTRC_ITEM_FOLDER: { + // Parse name + ScopedLocalRef folderName( + sCallbackEnv.get(), + sCallbackEnv->NewStringUTF((const char*)item->folder.name)); + if (!folderName.get()) { + ALOGE("%s can't allocate folder name string!", __func__); + return; + } + // Parse UID + long long uid = *(long long*)item->folder.uid; + ScopedLocalRef folderObj( + sCallbackEnv.get(), + (jobject)sCallbackEnv->CallObjectMethod( + sCallbacksObj, method_createFromNativeFolderItem, addr.get(), + uid, (jint)item->folder.type, folderName.get(), + (jint)item->folder.playable)); + if (!folderObj.get()) { + ALOGE("%s failed to create AvrcpItem for type ITEM_FOLDER", __func__); + return; + } + sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, + folderObj.get()); + break; + } + + case BTRC_ITEM_PLAYER: { + // Parse name + isPlayerListing = true; + jint id = (jint)item->player.player_id; + jint playerType = (jint)item->player.major_type; + jint playStatus = (jint)item->player.play_status; + ScopedLocalRef featureBitArray( + sCallbackEnv.get(), + sCallbackEnv->NewByteArray(BTRC_FEATURE_BIT_MASK_SIZE * + sizeof(uint8_t))); + if (!featureBitArray.get()) { + ALOGE("%s failed to allocate featureBitArray", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion( + featureBitArray.get(), 0, + sizeof(uint8_t) * BTRC_FEATURE_BIT_MASK_SIZE, + (jbyte*)item->player.features); + ScopedLocalRef playerName( + sCallbackEnv.get(), + sCallbackEnv->NewStringUTF((const char*)item->player.name)); + if (!playerName.get()) { + ALOGE("%s can't allocate player name string!", __func__); + return; + } + ScopedLocalRef playerObj( + sCallbackEnv.get(), + (jobject)sCallbackEnv->CallObjectMethod( + sCallbacksObj, method_createFromNativePlayerItem, addr.get(), + id, playerName.get(), featureBitArray.get(), playStatus, + playerType)); + if (!playerObj.get()) { + ALOGE("%s failed to create AvrcpPlayer from ITEM_PLAYER", __func__); + return; + } + sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, + playerObj.get()); + break; + } + + default: + ALOGE("%s cannot understand type %d", __func__, item->item_type); + } + } + + if (isPlayerListing) { + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetPlayerItemsRsp, + addr.get(), itemArray.get()); + } else { + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetFolderItemsRsp, + addr.get(), status, itemArray.get()); + } +} + +static void btavrcp_change_path_callback(const RawAddress& bd_addr, + uint32_t count) { + ALOGI("%s count %d", __func__, count); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleChangeFolderRsp, + addr.get(), (jint)count); +} + +static void btavrcp_set_browsed_player_callback(const RawAddress& bd_addr, + uint8_t num_items, + uint8_t depth) { + ALOGI("%s items %d depth %d", __func__, num_items, depth); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetBrowsedPlayerRsp, + addr.get(), (jint)num_items, (jint)depth); +} + +static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr, + uint8_t status) { + ALOGI("%s status %d", __func__, status); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + sCallbackEnv->CallVoidMethod(sCallbacksObj, + method_handleSetAddressedPlayerRsp, addr.get(), + (jint)status); +} + +static void btavrcp_addressed_player_changed_callback(const RawAddress& bd_addr, + uint16_t id) { + ALOGI("%s status %d", __func__, id); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_handleAddressedPlayerChanged, addr.get(), (jint)id); +} + +static void btavrcp_now_playing_content_changed_callback( + const RawAddress& bd_addr) { + ALOGI("%s", __func__); + + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_handleNowPlayingContentChanged, addr.get()); +} + +static void btavrcp_available_player_changed_callback ( + const RawAddress& bd_addr) { + ALOGI("%s", __func__); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_onAvailablePlayerChanged, addr.get()); +} + +static void btavrcp_get_rcpsm_callback(const RawAddress& bd_addr, + uint16_t psm) { + ALOGE("%s -> psm received of %d", __func__, psm); + std::shared_lock lock(sCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbacksObj) { + ALOGE("%s: sCallbacksObj is null", __func__); + return; + } + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("%s: Failed to allocate a new byte array", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr.address); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcPsm, addr.get(), + (jint)psm); +} + +static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { + sizeof(sBluetoothAvrcpCallbacks), + btavrcp_passthrough_response_callback, + btavrcp_groupnavigation_response_callback, + btavrcp_connection_state_callback, + btavrcp_get_rcfeatures_callback, + btavrcp_setplayerapplicationsetting_rsp_callback, + btavrcp_playerapplicationsetting_callback, + btavrcp_playerapplicationsetting_changed_callback, + btavrcp_set_abs_vol_cmd_callback, + btavrcp_register_notification_absvol_callback, + btavrcp_track_changed_callback, + btavrcp_play_position_changed_callback, + btavrcp_play_status_changed_callback, + btavrcp_get_folder_items_callback, + btavrcp_change_path_callback, + btavrcp_set_browsed_player_callback, + btavrcp_set_addressed_player_callback, + btavrcp_addressed_player_changed_callback, + btavrcp_now_playing_content_changed_callback, + btavrcp_available_player_changed_callback, + btavrcp_get_rcpsm_callback}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_handlePassthroughRsp = + env->GetMethodID(clazz, "handlePassthroughRsp", "(II[B)V"); + + method_handleGroupNavigationRsp = + env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V"); + + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "(ZZ[B)V"); + + method_getRcFeatures = env->GetMethodID(clazz, "getRcFeatures", "([BI)V"); + + method_getRcPsm = env->GetMethodID(clazz, "getRcPsm", "([BI)V"); + + method_setplayerappsettingrsp = + env->GetMethodID(clazz, "setPlayerAppSettingRsp", "([BB)V"); + + method_handleplayerappsetting = + env->GetMethodID(clazz, "handlePlayerAppSetting", "([B[BI)V"); + + method_handleplayerappsettingchanged = + env->GetMethodID(clazz, "onPlayerAppSettingChanged", "([B[BI)V"); + + method_handleSetAbsVolume = + env->GetMethodID(clazz, "handleSetAbsVolume", "([BBB)V"); + + method_handleRegisterNotificationAbsVol = + env->GetMethodID(clazz, "handleRegisterNotificationAbsVol", "([BB)V"); + + method_handletrackchanged = + env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V"); + + method_handleplaypositionchanged = + env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V"); + + method_handleplaystatuschanged = + env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V"); + + method_handleGetFolderItemsRsp = + env->GetMethodID(clazz, "handleGetFolderItemsRsp", + "([BI[Lcom/android/bluetooth/avrcpcontroller/" + "AvrcpItem;)V"); + method_handleGetPlayerItemsRsp = env->GetMethodID( + clazz, "handleGetPlayerItemsRsp", + "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V"); + + method_createFromNativeMediaItem = + env->GetMethodID(clazz, "createFromNativeMediaItem", + "([BJILjava/lang/String;[I[Ljava/lang/String;)Lcom/" + "android/bluetooth/avrcpcontroller/AvrcpItem;"); + method_createFromNativeFolderItem = env->GetMethodID( + clazz, "createFromNativeFolderItem", + "([BJILjava/lang/String;I)Lcom/android/bluetooth/avrcpcontroller/" + "AvrcpItem;"); + method_createFromNativePlayerItem = + env->GetMethodID(clazz, "createFromNativePlayerItem", + "([BILjava/lang/String;[BII)Lcom/android/bluetooth/" + "avrcpcontroller/AvrcpPlayer;"); + method_handleChangeFolderRsp = + env->GetMethodID(clazz, "handleChangeFolderRsp", "([BI)V"); + method_handleSetBrowsedPlayerRsp = + env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "([BII)V"); + method_handleSetAddressedPlayerRsp = + env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "([BI)V"); + method_handleAddressedPlayerChanged = + env->GetMethodID(clazz, "handleAddressedPlayerChanged", "([BI)V"); + method_handleNowPlayingContentChanged = + env->GetMethodID(clazz, "handleNowPlayingContentChanged", "([B)V"); + method_onAvailablePlayerChanged = + env->GetMethodID(clazz, "onAvailablePlayerChanged", "([B)V"); + + ALOGI("%s: succeeds", __func__); +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock lock(sCallbacks_mutex); + + jclass tmpAvrcpItem = + env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpItem"); + class_AvrcpItem = (jclass)env->NewGlobalRef(tmpAvrcpItem); + + jclass tmpBtPlayer = + env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpPlayer"); + class_AvrcpPlayer = (jclass)env->NewGlobalRef(tmpBtPlayer); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothAvrcpInterface != NULL) { + ALOGW("Cleaning up Avrcp Interface before initializing..."); + sBluetoothAvrcpInterface->cleanup(); + sBluetoothAvrcpInterface = NULL; + } + + if (sCallbacksObj != NULL) { + ALOGW("Cleaning up Avrcp callback object"); + env->DeleteGlobalRef(sCallbacksObj); + sCallbacksObj = NULL; + } + + sBluetoothAvrcpInterface = + (btrc_ctrl_interface_t*)btInf->get_profile_interface( + BT_PROFILE_AV_RC_CTRL_ID); + if (sBluetoothAvrcpInterface == NULL) { + ALOGE("Failed to get Bluetooth Avrcp Controller Interface"); + return; + } + + bt_status_t status = + sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to initialize Bluetooth Avrcp Controller, status: %d", + status); + sBluetoothAvrcpInterface = NULL; + return; + } + + sCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock lock(sCallbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothAvrcpInterface != NULL) { + sBluetoothAvrcpInterface->cleanup(); + sBluetoothAvrcpInterface = NULL; + } + + if (sCallbacksObj != NULL) { + env->DeleteGlobalRef(sCallbacksObj); + sCallbacksObj = NULL; + } +} + +static jboolean sendPassThroughCommandNative(JNIEnv* env, jobject object, + jbyteArray address, jint key_code, + jint key_state) { + if (!sBluetoothAvrcpInterface) return JNI_FALSE; + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + + ALOGI("key_code: %d, key_state: %d", key_code, key_state); + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + bt_status_t status = sBluetoothAvrcpInterface->send_pass_through_cmd( + rawAddress, (uint8_t)key_code, (uint8_t)key_state); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending passthru command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean sendGroupNavigationCommandNative(JNIEnv* env, jobject object, + jbyteArray address, + jint key_code, + jint key_state) { + if (!sBluetoothAvrcpInterface) return JNI_FALSE; + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + + ALOGI("key_code: %d, key_state: %d", key_code, key_state); + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = sBluetoothAvrcpInterface->send_group_navigation_cmd( + rawAddress, (uint8_t)key_code, (uint8_t)key_state); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending Grp Navigation command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static void setPlayerApplicationSettingValuesNative(JNIEnv* env, jobject object, + jbyteArray address, + jbyte num_attrib, + jbyteArray attrib_ids, + jbyteArray attrib_val) { + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + if (!sBluetoothAvrcpInterface) return; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + + uint8_t* pAttrs = new uint8_t[num_attrib]; + uint8_t* pAttrsVal = new uint8_t[num_attrib]; + if ((!pAttrs) || (!pAttrsVal)) { + delete[] pAttrs; + ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory"); + return; + } + + jbyte* attr = env->GetByteArrayElements(attrib_ids, NULL); + jbyte* attr_val = env->GetByteArrayElements(attrib_val, NULL); + if ((!attr) || (!attr_val)) { + delete[] pAttrs; + delete[] pAttrsVal; + jniThrowIOException(env, EINVAL); + return; + } + + int i; + for (i = 0; i < num_attrib; ++i) { + pAttrs[i] = (uint8_t)attr[i]; + pAttrsVal[i] = (uint8_t)attr_val[i]; + } + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = sBluetoothAvrcpInterface->set_player_app_setting_cmd( + rawAddress, (uint8_t)num_attrib, pAttrs, pAttrsVal); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending setPlAppSettValNative command, status: %d", status); + } + delete[] pAttrs; + delete[] pAttrsVal; + env->ReleaseByteArrayElements(attrib_ids, attr, 0); + env->ReleaseByteArrayElements(attrib_val, attr_val, 0); + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void sendAbsVolRspNative(JNIEnv* env, jobject object, jbyteArray address, + jint abs_vol, jint label) { + if (!sBluetoothAvrcpInterface) return; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = sBluetoothAvrcpInterface->set_volume_rsp( + rawAddress, (uint8_t)abs_vol, (uint8_t)label); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending sendAbsVolRspNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void sendRegisterAbsVolRspNative(JNIEnv* env, jobject object, + jbyteArray address, jbyte rsp_type, + jint abs_vol, jint label) { + if (!sBluetoothAvrcpInterface) return; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = sBluetoothAvrcpInterface->register_abs_vol_rsp( + rawAddress, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol, + (uint8_t)label); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d", + status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void getCurrentMetadataNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothAvrcpInterface) return; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = + sBluetoothAvrcpInterface->get_current_metadata_cmd(rawAddress); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending getCurrentMetadataNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void getPlaybackStateNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothAvrcpInterface) return; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = + sBluetoothAvrcpInterface->get_playback_state_cmd(rawAddress); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending getPlaybackStateNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void getNowPlayingListNative(JNIEnv* env, jobject object, + jbyteArray address, jint start, jint end) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = sBluetoothAvrcpInterface->get_now_playing_list_cmd( + rawAddress, start, end); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending getNowPlayingListNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void getFolderListNative(JNIEnv* env, jobject object, jbyteArray address, + jint start, jint end) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = + sBluetoothAvrcpInterface->get_folder_list_cmd(rawAddress, start, end); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending getFolderListNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void getPlayerListNative(JNIEnv* env, jobject object, jbyteArray address, + jint start, jint end) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = + sBluetoothAvrcpInterface->get_player_list_cmd(rawAddress, start, end); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending getPlayerListNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void changeFolderPathNative(JNIEnv* env, jobject object, + jbyteArray address, jbyte direction, + jlong uid) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + + // jbyte* uid = env->GetByteArrayElements(uidarr, NULL); + // if (!uid) { + // jniThrowIOException(env, EINVAL); + // return; + //} + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + bt_status_t status = sBluetoothAvrcpInterface->change_folder_path_cmd( + rawAddress, (uint8_t)direction, (uint8_t*)&uid); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending changeFolderPathNative command, status: %d", status); + } + // env->ReleaseByteArrayElements(address, addr, 0); +} + +static void setBrowsedPlayerNative(JNIEnv* env, jobject object, + jbyteArray address, jint id) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_cmd( + rawAddress, (uint16_t)id); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void setAddressedPlayerNative(JNIEnv* env, jobject object, + jbyteArray address, jint id) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_cmd( + rawAddress, (uint16_t)id); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending setAddressedPlayerNative command, status: %d", + status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void playItemNative(JNIEnv* env, jobject object, jbyteArray address, + jbyte scope, jlong uid, jint uidCounter) { + if (!sBluetoothAvrcpInterface) return; + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + + // jbyte* uid = env->GetByteArrayElements(uidArr, NULL); + // if (!uid) { + // jniThrowIOException(env, EINVAL); + // return; + // } + RawAddress rawAddress; + rawAddress.FromOctets((uint8_t*)addr); + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); + bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd( + rawAddress, (uint8_t)scope, (uint8_t*)&uid, (uint16_t)uidCounter); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending playItemNative command, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"sendPassThroughCommandNative", "([BII)Z", + (void*)sendPassThroughCommandNative}, + {"sendGroupNavigationCommandNative", "([BII)Z", + (void*)sendGroupNavigationCommandNative}, + {"setPlayerApplicationSettingValuesNative", "([BB[B[B)V", + (void*)setPlayerApplicationSettingValuesNative}, + {"sendAbsVolRspNative", "([BII)V", (void*)sendAbsVolRspNative}, + {"sendRegisterAbsVolRspNative", "([BBII)V", + (void*)sendRegisterAbsVolRspNative}, + {"getCurrentMetadataNative", "([B)V", (void*)getCurrentMetadataNative}, + {"getPlaybackStateNative", "([B)V", (void*)getPlaybackStateNative}, + {"getNowPlayingListNative", "([BII)V", (void*)getNowPlayingListNative}, + {"getFolderListNative", "([BII)V", (void*)getFolderListNative}, + {"getPlayerListNative", "([BII)V", (void*)getPlayerListNative}, + {"changeFolderPathNative", "([BBJ)V", (void*)changeFolderPathNative}, + {"playItemNative", "([BBJI)V", (void*)playItemNative}, + {"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative}, + {"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative}, +}; + +int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/avrcpcontroller/AvrcpControllerService", + sMethods, NELEM(sMethods)); +} +} diff --git a/android/app/jni/com_android_bluetooth_avrcp_target.cpp b/android/app/jni/com_android_bluetooth_avrcp_target.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb8c56086d0316b4000ca46da3d59216dc97367e --- /dev/null +++ b/android/app/jni/com_android_bluetooth_avrcp_target.cpp @@ -0,0 +1,931 @@ +/* + * Copyright (C) 2018 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. + */ + +#define LOG_TAG "AvrcpTargetJni" + +#include +#include +#include +#include +#include +#include + +#include "avrcp.h" +#include "com_android_bluetooth.h" +#include "utils/Log.h" + +using namespace bluetooth::avrcp; + +namespace android { + +// Static Variables +static MediaCallbacks* mServiceCallbacks; +static ServiceInterface* sServiceInterface; +static jobject mJavaInterface; +static std::shared_timed_mutex interface_mutex; +static std::shared_timed_mutex callbacks_mutex; + +// Forward Declarations +static void sendMediaKeyEvent(int, KeyState); +static std::string getCurrentMediaId(); +static SongInfo getSongInfo(); +static PlayStatus getCurrentPlayStatus(); +static std::vector getNowPlayingList(); +static uint16_t getCurrentPlayerId(); +static std::vector getMediaPlayerList(); +using SetBrowsedPlayerCb = MediaInterface::SetBrowsedPlayerCallback; +static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb); +using GetFolderItemsCb = MediaInterface::FolderItemsCallback; +static void getFolderItems(uint16_t player_id, std::string media_id, + GetFolderItemsCb cb); +static void playItem(uint16_t player_id, bool now_playing, + std::string media_id); +static void setActiveDevice(const RawAddress& address); + +static void volumeDeviceConnected(const RawAddress& address); +static void volumeDeviceConnected( + const RawAddress& address, + ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb); +static void volumeDeviceDisconnected(const RawAddress& address); +static void setVolume(int8_t volume); + +// Local Variables +// TODO (apanicke): Use a map here to store the callback in order to +// support multi-browsing +SetBrowsedPlayerCb set_browsed_player_cb; +using map_entry = std::pair; +std::map get_folder_items_cb_map; +std::map + volumeCallbackMap; + +// TODO (apanicke): In the future, this interface should guarantee that +// all calls happen on the JNI Thread. Right now this is very difficult +// as it is hard to get a handle on the JNI thread from here. +class AvrcpMediaInterfaceImpl : public MediaInterface { + public: + void SendKeyEvent(uint8_t key, KeyState state) { + sendMediaKeyEvent(key, state); + } + + void GetSongInfo(SongInfoCallback cb) override { + auto info = getSongInfo(); + cb.Run(info); + } + + void GetPlayStatus(PlayStatusCallback cb) override { + auto status = getCurrentPlayStatus(); + cb.Run(status); + } + + void GetNowPlayingList(NowPlayingCallback cb) override { + auto curr_song_id = getCurrentMediaId(); + auto now_playing_list = getNowPlayingList(); + cb.Run(curr_song_id, std::move(now_playing_list)); + } + + void GetMediaPlayerList(MediaListCallback cb) override { + uint16_t current_player = getCurrentPlayerId(); + auto player_list = getMediaPlayerList(); + cb.Run(current_player, std::move(player_list)); + } + + void GetFolderItems(uint16_t player_id, std::string media_id, + FolderItemsCallback folder_cb) override { + getFolderItems(player_id, media_id, folder_cb); + } + + void SetBrowsedPlayer(uint16_t player_id, + SetBrowsedPlayerCallback browse_cb) override { + setBrowsedPlayer(player_id, browse_cb); + } + + void RegisterUpdateCallback(MediaCallbacks* callback) override { + // TODO (apanicke): Allow multiple registrations in the future + mServiceCallbacks = callback; + } + + void UnregisterUpdateCallback(MediaCallbacks* callback) override { + mServiceCallbacks = nullptr; + } + + void PlayItem(uint16_t player_id, bool now_playing, + std::string media_id) override { + playItem(player_id, now_playing, media_id); + } + + void SetActiveDevice(const RawAddress& address) override { + setActiveDevice(address); + } +}; +static AvrcpMediaInterfaceImpl mAvrcpInterface; + +class VolumeInterfaceImpl : public VolumeInterface { + public: + void DeviceConnected(const RawAddress& bdaddr) override { + volumeDeviceConnected(bdaddr); + } + + void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) override { + volumeDeviceConnected(bdaddr, cb); + } + + void DeviceDisconnected(const RawAddress& bdaddr) override { + volumeDeviceDisconnected(bdaddr); + } + + void SetVolume(int8_t volume) override { setVolume(volume); } +}; +static VolumeInterfaceImpl mVolumeInterface; + +static jmethodID method_getCurrentSongInfo; +static jmethodID method_getPlaybackStatus; +static jmethodID method_sendMediaKeyEvent; + +static jmethodID method_getCurrentMediaId; +static jmethodID method_getNowPlayingList; + +static jmethodID method_setBrowsedPlayer; +static jmethodID method_getCurrentPlayerId; +static jmethodID method_getMediaPlayerList; +static jmethodID method_getFolderItemsRequest; +static jmethodID method_playItem; + +static jmethodID method_setActiveDevice; + +static jmethodID method_volumeDeviceConnected; +static jmethodID method_volumeDeviceDisconnected; + +static jmethodID method_setVolume; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_getCurrentSongInfo = env->GetMethodID( + clazz, "getCurrentSongInfo", "()Lcom/android/bluetooth/audio_util/Metadata;"); + + method_getPlaybackStatus = env->GetMethodID( + clazz, "getPlayStatus", "()Lcom/android/bluetooth/audio_util/PlayStatus;"); + + method_sendMediaKeyEvent = + env->GetMethodID(clazz, "sendMediaKeyEvent", "(IZ)V"); + + method_getCurrentMediaId = + env->GetMethodID(clazz, "getCurrentMediaId", "()Ljava/lang/String;"); + + method_getNowPlayingList = + env->GetMethodID(clazz, "getNowPlayingList", "()Ljava/util/List;"); + + method_getCurrentPlayerId = + env->GetMethodID(clazz, "getCurrentPlayerId", "()I"); + + method_getMediaPlayerList = + env->GetMethodID(clazz, "getMediaPlayerList", "()Ljava/util/List;"); + + method_setBrowsedPlayer = env->GetMethodID(clazz, "setBrowsedPlayer", "(I)V"); + + method_getFolderItemsRequest = env->GetMethodID( + clazz, "getFolderItemsRequest", "(ILjava/lang/String;)V"); + + method_playItem = + env->GetMethodID(clazz, "playItem", "(IZLjava/lang/String;)V"); + + method_setActiveDevice = + env->GetMethodID(clazz, "setActiveDevice", "(Ljava/lang/String;)V"); + + // Volume Management functions + method_volumeDeviceConnected = + env->GetMethodID(clazz, "deviceConnected", "(Ljava/lang/String;Z)V"); + + method_volumeDeviceDisconnected = + env->GetMethodID(clazz, "deviceDisconnected", "(Ljava/lang/String;)V"); + + method_setVolume = env->GetMethodID(clazz, "setVolume", "(I)V"); + + ALOGI("%s: AvrcpTargetJni initialized!", __func__); +} + +static void initNative(JNIEnv* env, jobject object) { + ALOGD("%s", __func__); + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + mJavaInterface = env->NewGlobalRef(object); + + sServiceInterface = getBluetoothInterface()->get_avrcp_service(); + sServiceInterface->Init(&mAvrcpInterface, &mVolumeInterface); +} + +static void registerBipServerNative(JNIEnv* env, jobject object, + jint l2cap_psm) { + ALOGD("%s: l2cap_psm=%d", __func__, (int)l2cap_psm); + std::unique_lock interface_lock(interface_mutex); + if (sServiceInterface == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return; + } + sServiceInterface->RegisterBipServer((int)l2cap_psm); +} + +static void unregisterBipServerNative(JNIEnv* env, jobject object) { + ALOGD("%s", __func__); + std::unique_lock interface_lock(interface_mutex); + if (sServiceInterface == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return; + } + sServiceInterface->UnregisterBipServer(); +} + +static void sendMediaUpdateNative(JNIEnv* env, jobject object, + jboolean metadata, jboolean state, + jboolean queue) { + ALOGD("%s", __func__); + std::unique_lock interface_lock(interface_mutex); + if (mServiceCallbacks == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return; + } + + mServiceCallbacks->SendMediaUpdate(metadata == JNI_TRUE, state == JNI_TRUE, + queue == JNI_TRUE); +} + +static void sendFolderUpdateNative(JNIEnv* env, jobject object, + jboolean available_players, + jboolean addressed_player, jboolean uids) { + ALOGD("%s", __func__); + std::unique_lock interface_lock(interface_mutex); + if (mServiceCallbacks == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return; + } + + mServiceCallbacks->SendFolderUpdate(available_players == JNI_TRUE, + addressed_player == JNI_TRUE, + uids == JNI_TRUE); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + get_folder_items_cb_map.clear(); + volumeCallbackMap.clear(); + + sServiceInterface->Cleanup(); + env->DeleteGlobalRef(mJavaInterface); + mJavaInterface = nullptr; + mServiceCallbacks = nullptr; + sServiceInterface = nullptr; +} + +jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) { + ALOGD("%s", __func__); + std::unique_lock interface_lock(interface_mutex); + if (mServiceCallbacks == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return JNI_FALSE; + } + + const char* tmp_addr = env->GetStringUTFChars(address, 0); + RawAddress bdaddr; + bool success = RawAddress::FromString(tmp_addr, bdaddr); + env->ReleaseStringUTFChars(address, tmp_addr); + + if (!success) return JNI_FALSE; + + return sServiceInterface->ConnectDevice(bdaddr) == true ? JNI_TRUE + : JNI_FALSE; +} + +jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) { + ALOGD("%s", __func__); + std::unique_lock interface_lock(interface_mutex); + if (mServiceCallbacks == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return JNI_FALSE; + } + + const char* tmp_addr = env->GetStringUTFChars(address, 0); + RawAddress bdaddr; + bool success = RawAddress::FromString(tmp_addr, bdaddr); + env->ReleaseStringUTFChars(address, tmp_addr); + + if (!success) return JNI_FALSE; + + return sServiceInterface->DisconnectDevice(bdaddr) == true ? JNI_TRUE + : JNI_FALSE; +} + +static void sendMediaKeyEvent(int key, KeyState state) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + sCallbackEnv->CallVoidMethod( + mJavaInterface, method_sendMediaKeyEvent, key, + state == KeyState::PUSHED ? JNI_TRUE : JNI_FALSE); +} + +static std::string getImageHandleFromJavaObj(JNIEnv* env, jobject image) { + std::string handle; + + if (image == nullptr) return handle; + + jclass class_image = env->GetObjectClass(image); + jmethodID method_getImageHandle = + env->GetMethodID(class_image, "getImageHandle", "()Ljava/lang/String;"); + jstring imageHandle = (jstring) env->CallObjectMethod( + image, method_getImageHandle); + if (imageHandle == nullptr) { + return handle; + } + + const char* value = env->GetStringUTFChars(imageHandle, nullptr); + handle = std::string(value); + env->ReleaseStringUTFChars(imageHandle, value); + env->DeleteLocalRef(imageHandle); + return handle; +} + +static SongInfo getSongInfoFromJavaObj(JNIEnv* env, jobject metadata) { + SongInfo info; + + if (metadata == nullptr) return info; + + jclass class_metadata = env->GetObjectClass(metadata); + jfieldID field_mediaId = + env->GetFieldID(class_metadata, "mediaId", "Ljava/lang/String;"); + jfieldID field_title = + env->GetFieldID(class_metadata, "title", "Ljava/lang/String;"); + jfieldID field_artist = + env->GetFieldID(class_metadata, "artist", "Ljava/lang/String;"); + jfieldID field_album = + env->GetFieldID(class_metadata, "album", "Ljava/lang/String;"); + jfieldID field_trackNum = + env->GetFieldID(class_metadata, "trackNum", "Ljava/lang/String;"); + jfieldID field_numTracks = + env->GetFieldID(class_metadata, "numTracks", "Ljava/lang/String;"); + jfieldID field_genre = + env->GetFieldID(class_metadata, "genre", "Ljava/lang/String;"); + jfieldID field_playingTime = + env->GetFieldID(class_metadata, "duration", "Ljava/lang/String;"); + jfieldID field_image = + env->GetFieldID(class_metadata, "image", "Lcom/android/bluetooth/audio_util/Image;"); + + jstring jstr = (jstring)env->GetObjectField(metadata, field_mediaId); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.media_id = std::string(value); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_title); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::TITLE, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_artist); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::ARTIST_NAME, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_album); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::ALBUM_NAME, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_trackNum); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::TRACK_NUMBER, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_numTracks); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::TOTAL_NUMBER_OF_TRACKS, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_genre); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::GENRE, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jstr = (jstring)env->GetObjectField(metadata, field_playingTime); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.attributes.insert( + AttributeEntry(Attribute::PLAYING_TIME, std::string(value))); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + jobject object_image = env->GetObjectField(metadata, field_image); + if (object_image != nullptr) { + std::string imageHandle = getImageHandleFromJavaObj(env, object_image); + if (!imageHandle.empty()) { + info.attributes.insert( + AttributeEntry(Attribute::DEFAULT_COVER_ART, imageHandle)); + } + env->DeleteLocalRef(object_image); + } + + return info; +} + +static FolderInfo getFolderInfoFromJavaObj(JNIEnv* env, jobject folder) { + FolderInfo info; + + jclass class_folder = env->GetObjectClass(folder); + jfieldID field_mediaId = + env->GetFieldID(class_folder, "mediaId", "Ljava/lang/String;"); + jfieldID field_isPlayable = env->GetFieldID(class_folder, "isPlayable", "Z"); + jfieldID field_name = + env->GetFieldID(class_folder, "title", "Ljava/lang/String;"); + + jstring jstr = (jstring)env->GetObjectField(folder, field_mediaId); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.media_id = std::string(value); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + info.is_playable = env->GetBooleanField(folder, field_isPlayable) == JNI_TRUE; + + jstr = (jstring)env->GetObjectField(folder, field_name); + if (jstr != nullptr) { + const char* value = env->GetStringUTFChars(jstr, nullptr); + info.name = std::string(value); + env->ReleaseStringUTFChars(jstr, value); + env->DeleteLocalRef(jstr); + } + + return info; +} + +static SongInfo getSongInfo() { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return SongInfo(); + + jobject metadata = + sCallbackEnv->CallObjectMethod(mJavaInterface, method_getCurrentSongInfo); + SongInfo info = getSongInfoFromJavaObj(sCallbackEnv.get(), metadata); + sCallbackEnv->DeleteLocalRef(metadata); + return info; +} + +static PlayStatus getCurrentPlayStatus() { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return PlayStatus(); + + PlayStatus status; + jobject playStatus = + sCallbackEnv->CallObjectMethod(mJavaInterface, method_getPlaybackStatus); + + if (playStatus == nullptr) { + ALOGE("%s: Got a null play status", __func__); + return status; + } + + jclass class_playStatus = sCallbackEnv->GetObjectClass(playStatus); + jfieldID field_position = + sCallbackEnv->GetFieldID(class_playStatus, "position", "J"); + jfieldID field_duration = + sCallbackEnv->GetFieldID(class_playStatus, "duration", "J"); + jfieldID field_state = + sCallbackEnv->GetFieldID(class_playStatus, "state", "B"); + + status.position = sCallbackEnv->GetLongField(playStatus, field_position); + status.duration = sCallbackEnv->GetLongField(playStatus, field_duration); + status.state = (PlayState)sCallbackEnv->GetByteField(playStatus, field_state); + + sCallbackEnv->DeleteLocalRef(playStatus); + + return status; +} + +static std::string getCurrentMediaId() { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return ""; + + jstring media_id = (jstring)sCallbackEnv->CallObjectMethod( + mJavaInterface, method_getCurrentMediaId); + if (media_id == nullptr) { + ALOGE("%s: Got a null media ID", __func__); + return ""; + } + + const char* value = sCallbackEnv->GetStringUTFChars(media_id, nullptr); + std::string ret(value); + sCallbackEnv->ReleaseStringUTFChars(media_id, value); + sCallbackEnv->DeleteLocalRef(media_id); + return ret; +} + +static std::vector getNowPlayingList() { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector(); + + jobject song_list = + sCallbackEnv->CallObjectMethod(mJavaInterface, method_getNowPlayingList); + if (song_list == nullptr) { + ALOGE("%s: Got a null now playing list", __func__); + return std::vector(); + } + + jclass class_list = sCallbackEnv->GetObjectClass(song_list); + jmethodID method_get = + sCallbackEnv->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;"); + jmethodID method_size = sCallbackEnv->GetMethodID(class_list, "size", "()I"); + + auto size = sCallbackEnv->CallIntMethod(song_list, method_size); + if (size == 0) { + sCallbackEnv->DeleteLocalRef(song_list); + return std::vector(); + } + std::vector ret; + for (int i = 0; i < size; i++) { + jobject song = sCallbackEnv->CallObjectMethod(song_list, method_get, i); + ret.push_back(getSongInfoFromJavaObj(sCallbackEnv.get(), song)); + sCallbackEnv->DeleteLocalRef(song); + } + + sCallbackEnv->DeleteLocalRef(song_list); + + return ret; +} + +static uint16_t getCurrentPlayerId() { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return 0u; + + jint id = + sCallbackEnv->CallIntMethod(mJavaInterface, method_getCurrentPlayerId); + + return (static_cast(id) & 0xFFFF); +} + +static std::vector getMediaPlayerList() { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) + return std::vector(); + + jobject player_list = (jobject)sCallbackEnv->CallObjectMethod( + mJavaInterface, method_getMediaPlayerList); + + if (player_list == nullptr) { + ALOGE("%s: Got a null media player list", __func__); + return std::vector(); + } + + jclass class_list = sCallbackEnv->GetObjectClass(player_list); + jmethodID method_get = + sCallbackEnv->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;"); + jmethodID method_size = sCallbackEnv->GetMethodID(class_list, "size", "()I"); + + jint list_size = sCallbackEnv->CallIntMethod(player_list, method_size); + if (list_size == 0) { + sCallbackEnv->DeleteLocalRef(player_list); + return std::vector(); + } + + jobject player_info = + sCallbackEnv->CallObjectMethod(player_list, method_get, 0); + jclass class_playerInfo = sCallbackEnv->GetObjectClass(player_info); + jfieldID field_playerId = + sCallbackEnv->GetFieldID(class_playerInfo, "id", "I"); + jfieldID field_name = + sCallbackEnv->GetFieldID(class_playerInfo, "name", "Ljava/lang/String;"); + jfieldID field_browsable = + sCallbackEnv->GetFieldID(class_playerInfo, "browsable", "Z"); + + std::vector ret_list; + for (jsize i = 0; i < list_size; i++) { + jobject player = sCallbackEnv->CallObjectMethod(player_list, method_get, i); + + MediaPlayerInfo temp; + temp.id = sCallbackEnv->GetIntField(player, field_playerId); + + jstring jstr = (jstring)sCallbackEnv->GetObjectField(player, field_name); + if (jstr != nullptr) { + const char* value = sCallbackEnv->GetStringUTFChars(jstr, nullptr); + temp.name = std::string(value); + sCallbackEnv->ReleaseStringUTFChars(jstr, value); + sCallbackEnv->DeleteLocalRef(jstr); + } + + temp.browsing_supported = + sCallbackEnv->GetBooleanField(player, field_browsable) == JNI_TRUE + ? true + : false; + + ret_list.push_back(std::move(temp)); + sCallbackEnv->DeleteLocalRef(player); + } + + sCallbackEnv->DeleteLocalRef(player_info); + sCallbackEnv->DeleteLocalRef(player_list); + + return ret_list; +} + +static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb cb) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + set_browsed_player_cb = cb; + sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer, + player_id); +} + +static void setBrowsedPlayerResponseNative(JNIEnv* env, jobject object, + jint player_id, jboolean success, + jstring root_id, jint num_items) { + ALOGD("%s", __func__); + + std::string root; + if (root_id != nullptr) { + const char* value = env->GetStringUTFChars(root_id, nullptr); + root = std::string(value); + env->ReleaseStringUTFChars(root_id, value); + } + + set_browsed_player_cb.Run(success == JNI_TRUE, root, num_items); +} + +static void getFolderItemsResponseNative(JNIEnv* env, jobject object, + jstring parent_id, jobject list) { + ALOGD("%s", __func__); + + std::string id; + if (parent_id != nullptr) { + const char* value = env->GetStringUTFChars(parent_id, nullptr); + id = std::string(value); + env->ReleaseStringUTFChars(parent_id, value); + } + + // TODO (apanicke): Right now browsing will fail on a second device if two + // devices browse the same folder. Use a MultiMap to fix this behavior so + // that both callbacks can be handled with one lookup if a request comes + // for a folder that is already trying to be looked at. + if (get_folder_items_cb_map.find(id) == get_folder_items_cb_map.end()) { + ALOGE("Could not find response callback for the request of \"%s\"", + id.c_str()); + return; + } + + auto callback = get_folder_items_cb_map.find(id)->second; + get_folder_items_cb_map.erase(id); + + if (list == nullptr) { + ALOGE("%s: Got a null get folder items response list", __func__); + callback.Run(std::vector()); + return; + } + + jclass class_list = env->GetObjectClass(list); + jmethodID method_get = + env->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;"); + jmethodID method_size = env->GetMethodID(class_list, "size", "()I"); + + jint list_size = env->CallIntMethod(list, method_size); + if (list_size == 0) { + callback.Run(std::vector()); + return; + } + + jobject list_item = env->CallObjectMethod(list, method_get, 0); + jclass class_listItem = env->GetObjectClass(list_item); + jfieldID field_isFolder = env->GetFieldID(class_listItem, "isFolder", "Z"); + jfieldID field_folder = env->GetFieldID( + class_listItem, "folder", "Lcom/android/bluetooth/audio_util/Folder;"); + jfieldID field_song = env->GetFieldID( + class_listItem, "song", "Lcom/android/bluetooth/audio_util/Metadata;"); + + std::vector ret_list; + for (jsize i = 0; i < list_size; i++) { + jobject item = env->CallObjectMethod(list, method_get, i); + + bool is_folder = env->GetBooleanField(item, field_isFolder) == JNI_TRUE; + + if (is_folder) { + jobject folder = env->GetObjectField(item, field_folder); + ListItem temp = {ListItem::FOLDER, + getFolderInfoFromJavaObj(env, folder), + SongInfo()}; + ret_list.push_back(temp); + env->DeleteLocalRef(folder); + } else { + jobject song = env->GetObjectField(item, field_song); + ListItem temp = {ListItem::SONG, FolderInfo(), + getSongInfoFromJavaObj(env, song)}; + ret_list.push_back(temp); + env->DeleteLocalRef(song); + } + env->DeleteLocalRef(item); + } + + env->DeleteLocalRef(list_item); + + callback.Run(std::move(ret_list)); +} + +static void getFolderItems(uint16_t player_id, std::string media_id, + GetFolderItemsCb cb) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + // TODO (apanicke): Fix a potential media_id collision if two media players + // use the same media_id scheme or two devices browse the same content. + get_folder_items_cb_map.insert(map_entry(media_id, cb)); + + jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str()); + sCallbackEnv->CallVoidMethod(mJavaInterface, method_getFolderItemsRequest, + player_id, j_media_id); +} + +static void playItem(uint16_t player_id, bool now_playing, + std::string media_id) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str()); + sCallbackEnv->CallVoidMethod(mJavaInterface, method_playItem, player_id, + now_playing ? JNI_TRUE : JNI_FALSE, j_media_id); +} + +static void setActiveDevice(const RawAddress& address) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); + sCallbackEnv->CallVoidMethod(mJavaInterface, method_setActiveDevice, + j_bdaddr); +} + +static void volumeDeviceConnected(const RawAddress& address) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); + sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected, + j_bdaddr, JNI_FALSE); +} + +static void volumeDeviceConnected( + const RawAddress& address, + ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + volumeCallbackMap.emplace(address, cb); + + jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); + sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected, + j_bdaddr, JNI_TRUE); +} + +static void volumeDeviceDisconnected(const RawAddress& address) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + volumeCallbackMap.erase(address); + + jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); + sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceDisconnected, + j_bdaddr); +} + +static void sendVolumeChangedNative(JNIEnv* env, jobject object, + jstring address, jint volume) { + const char* tmp_addr = env->GetStringUTFChars(address, 0); + RawAddress bdaddr; + bool success = RawAddress::FromString(tmp_addr, bdaddr); + env->ReleaseStringUTFChars(address, tmp_addr); + + if (!success) return; + + ALOGD("%s", __func__); + if (volumeCallbackMap.find(bdaddr) != volumeCallbackMap.end()) { + volumeCallbackMap.find(bdaddr)->second.Run(volume & 0x7F); + } +} + +static void setVolume(int8_t volume) { + ALOGD("%s", __func__); + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mJavaInterface) return; + + sCallbackEnv->CallVoidMethod(mJavaInterface, method_setVolume, volume); +} + +static void setBipClientStatusNative(JNIEnv* env, jobject object, + jstring address, jboolean connected) { + std::unique_lock interface_lock(interface_mutex); + if (mServiceCallbacks == nullptr) { + ALOGW("%s: Service not loaded.", __func__); + return; + } + + const char* tmp_addr = env->GetStringUTFChars(address, 0); + RawAddress bdaddr; + bool success = RawAddress::FromString(tmp_addr, bdaddr); + env->ReleaseStringUTFChars(address, tmp_addr); + + if (!success) return; + + bool status = (connected == JNI_TRUE); + sServiceInterface->SetBipClientStatus(bdaddr, status); +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"registerBipServerNative", "(I)V", (void*)registerBipServerNative}, + {"unregisterBipServerNative", "()V", (void*)unregisterBipServerNative}, + {"sendMediaUpdateNative", "(ZZZ)V", (void*)sendMediaUpdateNative}, + {"sendFolderUpdateNative", "(ZZZ)V", (void*)sendFolderUpdateNative}, + {"setBrowsedPlayerResponseNative", "(IZLjava/lang/String;I)V", + (void*)setBrowsedPlayerResponseNative}, + {"getFolderItemsResponseNative", "(Ljava/lang/String;Ljava/util/List;)V", + (void*)getFolderItemsResponseNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectDeviceNative", "(Ljava/lang/String;)Z", + (void*)connectDeviceNative}, + {"disconnectDeviceNative", "(Ljava/lang/String;)Z", + (void*)disconnectDeviceNative}, + {"sendVolumeChangedNative", "(Ljava/lang/String;I)V", + (void*)sendVolumeChangedNative}, + {"setBipClientStatusNative", "(Ljava/lang/String;Z)V", + (void*)setBipClientStatusNative}, +}; + +int register_com_android_bluetooth_avrcp_target(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/avrcp/AvrcpNativeInterface", sMethods, + NELEM(sMethods)); +} + +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp b/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp new file mode 100644 index 0000000000000000000000000000000000000000..791e42a0ea48df9617e24a12616b679a8bf981f9 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2020 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. + */ + +#define LOG_TAG "BluetoothActivityAttributionJni" + +#include + +#include + +#include "base/logging.h" +#include "com_android_bluetooth.h" +#include "hardware/bt_activity_attribution.h" + +using bluetooth::activity_attribution::ActivityAttributionCallbacks; +using bluetooth::activity_attribution::ActivityAttributionInterface; + +namespace android { +static jmethodID method_onWakeup; +static jmethodID method_onActivityLogsReady; + +static ActivityAttributionInterface* sActivityAttributionInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +class ActivityAttributionCallbacksImpl : public ActivityAttributionCallbacks { + public: + ~ActivityAttributionCallbacksImpl() = default; + + void OnWakeup(const Activity activity, const RawAddress& bd_addr) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) + << "Failed to allocate jbyteArray for bd_addr of wakeup callback"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWakeup, (jint)activity, + addr.get()); + } + + void OnActivityLogsReady( + const std::vector logs) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + jsize logs_size = logs.size() * sizeof(BtaaAggregationEntry); + ScopedLocalRef addr(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(logs_size)); + if (!addr.get()) { + LOG(ERROR) << "Failed to allocate jbyteArray for logs from activity " + "logging callback"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, logs_size, + (jbyte*)logs.data()); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onActivityLogsReady, + addr.get()); + } +}; + +static ActivityAttributionCallbacksImpl sActivityAttributionCallbacks; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onWakeup = env->GetMethodID(clazz, "onWakeup", "(I[B)V"); + method_onActivityLogsReady = + env->GetMethodID(clazz, "onActivityLogsReady", "([B)V"); + + LOG(INFO) << __func__ << ": succeeds"; +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sActivityAttributionInterface != nullptr) { + LOG(INFO) + << "Cleaning up ActivityAttribution Interface before initializing..."; + sActivityAttributionInterface->Cleanup(); + sActivityAttributionInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + LOG(INFO) << "Cleaning up ActivityAttribution callback object"; + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + LOG(ERROR) + << "Failed to allocate Global Ref for ActivityAttribution Callbacks"; + return; + } + + sActivityAttributionInterface = + (ActivityAttributionInterface*)btInf->get_profile_interface( + BT_ACTIVITY_ATTRIBUTION_ID); + if (sActivityAttributionInterface == nullptr) { + LOG(ERROR) << "Failed to get ActivityAttribution Interface"; + return; + } + + sActivityAttributionInterface->RegisterCallbacks( + &sActivityAttributionCallbacks); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sActivityAttributionInterface != nullptr) { + sActivityAttributionInterface->Cleanup(); + sActivityAttributionInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, +}; + +int register_com_android_bluetooth_btservice_activity_attribution(JNIEnv* env) { + return jniRegisterNativeMethods( + env, + "com/android/bluetooth/btservice/activityattribution/" + "ActivityAttributionNativeInterface", + sMethods, NELEM(sMethods)); +} + +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbf46bb7dfad7459414e9450b31d1369c9a49890 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp @@ -0,0 +1,1891 @@ +/* + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothServiceJni" +#include "com_android_bluetooth.h" +#include "hardware/bt_sock.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +using bluetooth::Uuid; + +namespace android { +// Both +// OOB_ADDRESS_SIZE is 6 bytes address + 1 byte address type +#define OOB_ADDRESS_SIZE 7 +#define OOB_C_SIZE 16 +#define OOB_R_SIZE 16 +#define OOB_NAME_MAX_SIZE 256 +// Classic +#define OOB_DATA_LEN_SIZE 2 +#define OOB_COD_SIZE 3 +// LE +#define OOB_TK_SIZE 16 +#define OOB_LE_FLAG_SIZE 1 +#define OOB_LE_ROLE_SIZE 1 +#define OOB_LE_APPEARANCE_SIZE 2 + +#define TRANSPORT_AUTO 0 +#define TRANSPORT_BREDR 1 +#define TRANSPORT_LE 2 + +const jint INVALID_FD = -1; + +static jmethodID method_oobDataReceivedCallback; +static jmethodID method_stateChangeCallback; +static jmethodID method_adapterPropertyChangedCallback; +static jmethodID method_devicePropertyChangedCallback; +static jmethodID method_deviceFoundCallback; +static jmethodID method_pinRequestCallback; +static jmethodID method_sspRequestCallback; +static jmethodID method_bondStateChangeCallback; +static jmethodID method_addressConsolidateCallback; +static jmethodID method_aclStateChangeCallback; +static jmethodID method_discoveryStateChangeCallback; +static jmethodID method_linkQualityReportCallback; +static jmethodID method_setWakeAlarm; +static jmethodID method_acquireWakeLock; +static jmethodID method_releaseWakeLock; +static jmethodID method_energyInfo; + +static struct { + jclass clazz; + jmethodID constructor; +} android_bluetooth_UidTraffic; + +static const bt_interface_t* sBluetoothInterface = NULL; +static const btsock_interface_t* sBluetoothSocketInterface = NULL; +static JavaVM* vm = NULL; +static JNIEnv* callbackEnv = NULL; +static pthread_t sCallbackThread; +static bool sHaveCallbackThread; + +static jobject sJniAdapterServiceObj; +static jobject sJniCallbacksObj; +static jfieldID sJniCallbacksField; + +const bt_interface_t* getBluetoothInterface() { return sBluetoothInterface; } + +JNIEnv* getCallbackEnv() { return callbackEnv; } + +bool isCallbackThread() { + return sHaveCallbackThread && pthread_equal(sCallbackThread, pthread_self()); +} + +static void adapter_state_change_callback(bt_state_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + ALOGV("%s: Status is: %d", __func__, status); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, + (jint)status); +} + +static int get_properties(int num_properties, bt_property_t* properties, + jintArray* types, jobjectArray* props) { + for (int i = 0; i < num_properties; i++) { + ScopedLocalRef propVal( + callbackEnv, callbackEnv->NewByteArray(properties[i].len)); + if (!propVal.get()) { + ALOGE("Error while allocation of array in %s", __func__); + return -1; + } + + callbackEnv->SetByteArrayRegion(propVal.get(), 0, properties[i].len, + (jbyte*)properties[i].val); + callbackEnv->SetObjectArrayElement(*props, i, propVal.get()); + callbackEnv->SetIntArrayRegion(*types, i, 1, (jint*)&properties[i].type); + } + return 0; +} + +static void adapter_properties_callback(bt_status_t status, int num_properties, + bt_property_t* properties) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ALOGV("%s: Status is: %d, Properties: %d", __func__, status, num_properties); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Status %d is incorrect", __func__, status); + return; + } + + ScopedLocalRef val( + sCallbackEnv.get(), + (jbyteArray)sCallbackEnv->NewByteArray(num_properties)); + if (!val.get()) { + ALOGE("%s: Error allocating byteArray", __func__); + return; + } + + ScopedLocalRef mclass(sCallbackEnv.get(), + sCallbackEnv->GetObjectClass(val.get())); + + /* (BT) Initialize the jobjectArray and jintArray here itself and send the + initialized array pointers alone to get_properties */ + + ScopedLocalRef props( + sCallbackEnv.get(), + sCallbackEnv->NewObjectArray(num_properties, mclass.get(), NULL)); + if (!props.get()) { + ALOGE("%s: Error allocating object Array for properties", __func__); + return; + } + + ScopedLocalRef types( + sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_properties)); + if (!types.get()) { + ALOGE("%s: Error allocating int Array for values", __func__); + return; + } + + jintArray typesPtr = types.get(); + jobjectArray propsPtr = props.get(); + if (get_properties(num_properties, properties, &typesPtr, &propsPtr) < 0) { + return; + } + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, + method_adapterPropertyChangedCallback, + types.get(), props.get()); +} + +static void remote_device_properties_callback(bt_status_t status, + RawAddress* bd_addr, + int num_properties, + bt_property_t* properties) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ALOGV("%s: Status is: %d, Properties: %d", __func__, status, num_properties); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Status %d is incorrect", __func__, status); + return; + } + + ScopedLocalRef val( + sCallbackEnv.get(), + (jbyteArray)sCallbackEnv->NewByteArray(num_properties)); + if (!val.get()) { + ALOGE("%s: Error allocating byteArray", __func__); + return; + } + + ScopedLocalRef mclass(sCallbackEnv.get(), + sCallbackEnv->GetObjectClass(val.get())); + + /* Initialize the jobjectArray and jintArray here itself and send the + initialized array pointers alone to get_properties */ + + ScopedLocalRef props( + sCallbackEnv.get(), + sCallbackEnv->NewObjectArray(num_properties, mclass.get(), NULL)); + if (!props.get()) { + ALOGE("%s: Error allocating object Array for properties", __func__); + return; + } + + ScopedLocalRef types( + sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_properties)); + if (!types.get()) { + ALOGE("%s: Error allocating int Array for values", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Error while allocation byte array in %s", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr); + + jintArray typesPtr = types.get(); + jobjectArray propsPtr = props.get(); + if (get_properties(num_properties, properties, &typesPtr, &propsPtr) < 0) { + return; + } + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, + method_devicePropertyChangedCallback, addr.get(), + types.get(), props.get()); +} + +static void device_found_callback(int num_properties, + bt_property_t* properties) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr(sCallbackEnv.get(), NULL); + int addr_index; + for (int i = 0; i < num_properties; i++) { + if (properties[i].type == BT_PROPERTY_BDADDR) { + addr.reset(sCallbackEnv->NewByteArray(properties[i].len)); + if (!addr.get()) { + ALOGE("Address is NULL (unable to allocate) in %s", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, properties[i].len, + (jbyte*)properties[i].val); + addr_index = i; + } + } + if (!addr.get()) { + ALOGE("Address is NULL in %s", __func__); + return; + } + + ALOGV("%s: Properties: %d, Address: %s", __func__, num_properties, + (const char*)properties[addr_index].val); + + remote_device_properties_callback(BT_STATUS_SUCCESS, + (RawAddress*)properties[addr_index].val, + num_properties, properties); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, + addr.get()); +} + +static void bond_state_changed_callback(bt_status_t status, RawAddress* bd_addr, + bt_bond_state_t state, + int fail_reason) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + if (!bd_addr) { + ALOGE("Address is null in %s", __func__); + return; + } + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Address allocation failed in %s", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback, + (jint)status, addr.get(), (jint)state, + (jint)fail_reason); +} + +static void address_consolidate_callback(RawAddress* main_bd_addr, + RawAddress* secondary_bd_addr) { + CallbackEnv sCallbackEnv(__func__); + + ScopedLocalRef main_addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!main_addr.get()) { + ALOGE("Address allocation failed in %s", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(main_addr.get(), 0, sizeof(RawAddress), + (jbyte*)main_bd_addr); + + ScopedLocalRef secondary_addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!secondary_addr.get()) { + ALOGE("Address allocation failed in %s", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(secondary_addr.get(), 0, sizeof(RawAddress), + (jbyte*)secondary_bd_addr); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, + method_addressConsolidateCallback, + main_addr.get(), secondary_addr.get()); +} + +static void acl_state_changed_callback(bt_status_t status, RawAddress* bd_addr, + bt_acl_state_t state, + int transport_link_type, + bt_hci_error_code_t hci_reason) { + if (!bd_addr) { + ALOGE("Address is null in %s", __func__); + return; + } + + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Address allocation failed in %s", __func__); + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_aclStateChangeCallback, + (jint)status, addr.get(), (jint)state, + (jint)transport_link_type, (jint)hci_reason); +} + +static void discovery_state_changed_callback(bt_discovery_state_t state) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ALOGV("%s: DiscoveryState:%d ", __func__, state); + + sCallbackEnv->CallVoidMethod( + sJniCallbacksObj, method_discoveryStateChangeCallback, (jint)state); +} + +static void pin_request_callback(RawAddress* bd_addr, bt_bdname_t* bdname, + uint32_t cod, bool min_16_digits) { + if (!bd_addr) { + ALOGE("Address is null in %s", __func__); + return; + } + + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Error while allocating in: %s", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr); + + ScopedLocalRef devname( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdname_t))); + if (!devname.get()) { + ALOGE("Error while allocating in: %s", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(devname.get(), 0, sizeof(bt_bdname_t), + (jbyte*)bdname); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_pinRequestCallback, + addr.get(), devname.get(), cod, min_16_digits); +} + +static void ssp_request_callback(RawAddress* bd_addr, bt_bdname_t* bdname, + uint32_t cod, bt_ssp_variant_t pairing_variant, + uint32_t pass_key) { + if (!bd_addr) { + ALOGE("Address is null in %s", __func__); + return; + } + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Error while allocating in: %s", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr); + + ScopedLocalRef devname( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdname_t))); + if (!devname.get()) { + ALOGE("Error while allocating in: %s", __func__); + return; + } + + sCallbackEnv->SetByteArrayRegion(devname.get(), 0, sizeof(bt_bdname_t), + (jbyte*)bdname); + + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_sspRequestCallback, + addr.get(), devname.get(), cod, + (jint)pairing_variant, pass_key); +} + +static jobject createClassicOobDataObject(JNIEnv* env, bt_oob_data_t oob_data) { + ALOGV("%s", __func__); + jclass classicBuilderClass = + env->FindClass("android/bluetooth/OobData$ClassicBuilder"); + + jbyteArray confirmationHash = env->NewByteArray(OOB_C_SIZE); + env->SetByteArrayRegion(confirmationHash, 0, OOB_C_SIZE, + reinterpret_cast(oob_data.c)); + + jbyteArray oobDataLength = env->NewByteArray(OOB_DATA_LEN_SIZE); + env->SetByteArrayRegion(oobDataLength, 0, OOB_DATA_LEN_SIZE, + reinterpret_cast(oob_data.oob_data_length)); + + jbyteArray address = env->NewByteArray(OOB_ADDRESS_SIZE); + env->SetByteArrayRegion(address, 0, OOB_ADDRESS_SIZE, + reinterpret_cast(oob_data.address)); + + jmethodID classicBuilderConstructor = + env->GetMethodID(classicBuilderClass, "", "([B[B[B)V"); + + jobject oobDataClassicBuilder = + env->NewObject(classicBuilderClass, classicBuilderConstructor, + confirmationHash, oobDataLength, address); + + jmethodID setRMethod = + env->GetMethodID(classicBuilderClass, "setRandomizerHash", + "([B)Landroid/bluetooth/OobData$ClassicBuilder;"); + jbyteArray randomizerHash = env->NewByteArray(OOB_R_SIZE); + env->SetByteArrayRegion(randomizerHash, 0, OOB_R_SIZE, + reinterpret_cast(oob_data.r)); + + oobDataClassicBuilder = + env->CallObjectMethod(oobDataClassicBuilder, setRMethod, randomizerHash); + + jmethodID setNameMethod = + env->GetMethodID(classicBuilderClass, "setDeviceName", + "([B)Landroid/bluetooth/OobData$ClassicBuilder;"); + + int name_char_count = 0; + for (int i = 0; i < OOB_NAME_MAX_SIZE; i++) { + if (oob_data.device_name[i] == 0) { + name_char_count = i; + break; + } + } + + jbyteArray deviceName = env->NewByteArray(name_char_count); + env->SetByteArrayRegion(deviceName, 0, name_char_count, + reinterpret_cast(oob_data.device_name)); + + oobDataClassicBuilder = + env->CallObjectMethod(oobDataClassicBuilder, setNameMethod, deviceName); + + jmethodID buildMethod = env->GetMethodID(classicBuilderClass, "build", + "()Landroid/bluetooth/OobData;"); + + return env->CallObjectMethod(oobDataClassicBuilder, buildMethod); +} + +static jobject createLeOobDataObject(JNIEnv* env, bt_oob_data_t oob_data) { + ALOGV("%s", __func__); + + jclass leBuilderClass = env->FindClass("android/bluetooth/OobData$LeBuilder"); + + jbyteArray confirmationHash = env->NewByteArray(OOB_C_SIZE); + env->SetByteArrayRegion(confirmationHash, 0, OOB_C_SIZE, + reinterpret_cast(oob_data.c)); + + jbyteArray address = env->NewByteArray(OOB_ADDRESS_SIZE); + env->SetByteArrayRegion(address, 0, OOB_ADDRESS_SIZE, + reinterpret_cast(oob_data.address)); + + jint le_role = (jint)oob_data.le_device_role; + + jmethodID leBuilderConstructor = + env->GetMethodID(leBuilderClass, "", "([B[BI)V"); + + jobject oobDataLeBuilder = env->NewObject( + leBuilderClass, leBuilderConstructor, confirmationHash, address, le_role); + + jmethodID setRMethod = + env->GetMethodID(leBuilderClass, "setRandomizerHash", + "([B)Landroid/bluetooth/OobData$LeBuilder;"); + jbyteArray randomizerHash = env->NewByteArray(OOB_R_SIZE); + env->SetByteArrayRegion(randomizerHash, 0, OOB_R_SIZE, + reinterpret_cast(oob_data.r)); + + oobDataLeBuilder = + env->CallObjectMethod(oobDataLeBuilder, setRMethod, randomizerHash); + + jmethodID setNameMethod = + env->GetMethodID(leBuilderClass, "setDeviceName", + "([B)Landroid/bluetooth/OobData$LeBuilder;"); + + int name_char_count = 0; + for (int i = 0; i < OOB_NAME_MAX_SIZE; i++) { + if (oob_data.device_name[i] == 0) { + name_char_count = i; + break; + } + } + + jbyteArray deviceName = env->NewByteArray(name_char_count); + env->SetByteArrayRegion(deviceName, 0, name_char_count, + reinterpret_cast(oob_data.device_name)); + + oobDataLeBuilder = + env->CallObjectMethod(oobDataLeBuilder, setNameMethod, deviceName); + + jmethodID buildMethod = env->GetMethodID(leBuilderClass, "build", + "()Landroid/bluetooth/OobData;"); + + return env->CallObjectMethod(oobDataLeBuilder, buildMethod); +} + +static void generate_local_oob_data_callback(tBT_TRANSPORT transport, + bt_oob_data_t oob_data) { + ALOGV("%s", __func__); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + if (transport == TRANSPORT_BREDR) { + sCallbackEnv->CallVoidMethod( + sJniCallbacksObj, method_oobDataReceivedCallback, (jint)transport, + ((oob_data.is_valid) + ? createClassicOobDataObject(sCallbackEnv.get(), oob_data) + : nullptr)); + } else if (transport == TRANSPORT_LE) { + sCallbackEnv->CallVoidMethod( + sJniCallbacksObj, method_oobDataReceivedCallback, (jint)transport, + ((oob_data.is_valid) + ? createLeOobDataObject(sCallbackEnv.get(), oob_data) + : nullptr)); + } else { + // TRANSPORT_AUTO is a concept, however, the host stack doesn't fully + // implement it So passing it from the java layer is currently useless until + // the implementation and concept of TRANSPORT_AUTO is fleshed out. + ALOGE("TRANSPORT: %d not implemented", transport); + sCallbackEnv->CallVoidMethod(sJniCallbacksObj, + method_oobDataReceivedCallback, + (jint)transport, nullptr); + } +} + +static void link_quality_report_callback( + uint64_t timestamp, int report_id, int rssi, int snr, + int retransmission_count, int packets_not_receive_count, + int negative_acknowledgement_count) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ALOGV("%s: LinkQualityReportCallback: %d %d %d %d %d %d", __func__, + report_id, rssi, snr, retransmission_count, packets_not_receive_count, + negative_acknowledgement_count); + + sCallbackEnv->CallVoidMethod( + sJniCallbacksObj, method_linkQualityReportCallback, + (jlong)timestamp, (jint)report_id, (jint)rssi, (jint)snr, + (jint)retransmission_count, (jint)packets_not_receive_count, + (jint)negative_acknowledgement_count); +} + +static void callback_thread_event(bt_cb_thread_evt event) { + if (event == ASSOCIATE_JVM) { + JavaVMAttachArgs args; + char name[] = "BT Service Callback Thread"; + args.version = JNI_VERSION_1_6; + args.name = name; + args.group = NULL; + vm->AttachCurrentThread(&callbackEnv, &args); + sHaveCallbackThread = true; + sCallbackThread = pthread_self(); + ALOGV("Callback thread attached: %p", callbackEnv); + } else if (event == DISASSOCIATE_JVM) { + if (!isCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __func__); + return; + } + vm->DetachCurrentThread(); + sHaveCallbackThread = false; + } +} + +static void dut_mode_recv_callback(uint16_t opcode, uint8_t* buf, uint8_t len) { + +} + +static void le_test_mode_recv_callback(bt_status_t status, + uint16_t packet_count) { + ALOGV("%s: status:%d packet_count:%d ", __func__, status, packet_count); +} + +static void energy_info_recv_callback(bt_activity_energy_info* p_energy_info, + bt_uid_traffic_t* uid_data) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + jsize len = 0; + for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) { + len++; + } + + ScopedLocalRef array( + sCallbackEnv.get(), sCallbackEnv->NewObjectArray( + len, android_bluetooth_UidTraffic.clazz, NULL)); + jsize i = 0; + for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) { + ScopedLocalRef uidObj( + sCallbackEnv.get(), + sCallbackEnv->NewObject(android_bluetooth_UidTraffic.clazz, + android_bluetooth_UidTraffic.constructor, + (jint)data->app_uid, (jlong)data->rx_bytes, + (jlong)data->tx_bytes)); + sCallbackEnv->SetObjectArrayElement(array.get(), i++, uidObj.get()); + } + + sCallbackEnv->CallVoidMethod( + sJniAdapterServiceObj, method_energyInfo, p_energy_info->status, + p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time, + p_energy_info->idle_time, p_energy_info->energy_used, array.get()); +} + +static bt_callbacks_t sBluetoothCallbacks = {sizeof(sBluetoothCallbacks), + adapter_state_change_callback, + adapter_properties_callback, + remote_device_properties_callback, + device_found_callback, + discovery_state_changed_callback, + pin_request_callback, + ssp_request_callback, + bond_state_changed_callback, + address_consolidate_callback, + acl_state_changed_callback, + callback_thread_event, + dut_mode_recv_callback, + le_test_mode_recv_callback, + energy_info_recv_callback, + link_quality_report_callback, + generate_local_oob_data_callback}; + +// The callback to call when the wake alarm fires. +static alarm_cb sAlarmCallback; + +// The data to pass to the wake alarm callback. +static void* sAlarmCallbackData; + +class JNIThreadAttacher { + public: + JNIThreadAttacher(JavaVM* vm) : vm_(vm), env_(nullptr) { + status_ = vm_->GetEnv((void**)&env_, JNI_VERSION_1_6); + + if (status_ != JNI_OK && status_ != JNI_EDETACHED) { + ALOGE( + "JNIThreadAttacher: unable to get environment for JNI CALL, " + "status: %d", + status_); + env_ = nullptr; + return; + } + + if (status_ == JNI_EDETACHED) { + char name[17] = {0}; + if (prctl(PR_GET_NAME, (unsigned long)name) != 0) { + ALOGE( + "JNIThreadAttacher: unable to grab previous thread name, error: %s", + strerror(errno)); + env_ = nullptr; + return; + } + + JavaVMAttachArgs args = { + .version = JNI_VERSION_1_6, .name = name, .group = nullptr}; + if (vm_->AttachCurrentThread(&env_, &args) != 0) { + ALOGE("JNIThreadAttacher: unable to attach thread to VM"); + env_ = nullptr; + return; + } + } + } + + ~JNIThreadAttacher() { + if (status_ == JNI_EDETACHED) vm_->DetachCurrentThread(); + } + + JNIEnv* getEnv() { return env_; } + + private: + JavaVM* vm_; + JNIEnv* env_; + jint status_; +}; + +static bool set_wake_alarm_callout(uint64_t delay_millis, bool should_wake, + alarm_cb cb, void* data) { + JNIThreadAttacher attacher(vm); + JNIEnv* env = attacher.getEnv(); + + if (env == nullptr) { + ALOGE("%s: Unable to get JNI Env", __func__); + return false; + } + + sAlarmCallback = cb; + sAlarmCallbackData = data; + + jboolean jshould_wake = should_wake ? JNI_TRUE : JNI_FALSE; + jboolean ret = + env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm, + (jlong)delay_millis, jshould_wake); + if (!ret) { + sAlarmCallback = NULL; + sAlarmCallbackData = NULL; + } + + return (ret == JNI_TRUE); +} + +static int acquire_wake_lock_callout(const char* lock_name) { + JNIThreadAttacher attacher(vm); + JNIEnv* env = attacher.getEnv(); + + if (env == nullptr) { + ALOGE("%s: Unable to get JNI Env", __func__); + return BT_STATUS_JNI_THREAD_ATTACH_ERROR; + } + + jint ret = BT_STATUS_SUCCESS; + { + ScopedLocalRef lock_name_jni(env, env->NewStringUTF(lock_name)); + if (lock_name_jni.get()) { + bool acquired = env->CallBooleanMethod( + sJniAdapterServiceObj, method_acquireWakeLock, lock_name_jni.get()); + if (!acquired) ret = BT_STATUS_WAKELOCK_ERROR; + } else { + ALOGE("%s unable to allocate string: %s", __func__, lock_name); + ret = BT_STATUS_NOMEM; + } + } + + return ret; +} + +static int release_wake_lock_callout(const char* lock_name) { + JNIThreadAttacher attacher(vm); + JNIEnv* env = attacher.getEnv(); + + if (env == nullptr) { + ALOGE("%s: Unable to get JNI Env", __func__); + return BT_STATUS_JNI_THREAD_ATTACH_ERROR; + } + + jint ret = BT_STATUS_SUCCESS; + { + ScopedLocalRef lock_name_jni(env, env->NewStringUTF(lock_name)); + if (lock_name_jni.get()) { + bool released = env->CallBooleanMethod( + sJniAdapterServiceObj, method_releaseWakeLock, lock_name_jni.get()); + if (!released) ret = BT_STATUS_WAKELOCK_ERROR; + } else { + ALOGE("%s unable to allocate string: %s", __func__, lock_name); + ret = BT_STATUS_NOMEM; + } + } + + return ret; +} + +// Called by Java code when alarm is fired. A wake lock is held by the caller +// over the duration of this callback. +static void alarmFiredNative(JNIEnv* env, jobject obj) { + if (sAlarmCallback) { + sAlarmCallback(sAlarmCallbackData); + } else { + ALOGE("%s() - Alarm fired with callback not set!", __func__); + } +} + +static bt_os_callouts_t sBluetoothOsCallouts = { + sizeof(sBluetoothOsCallouts), set_wake_alarm_callout, + acquire_wake_lock_callout, release_wake_lock_callout, +}; + +int hal_util_load_bt_library(const bt_interface_t** interface) { + const char* sym = BLUETOOTH_INTERFACE_STRING; + bt_interface_t* itf = nullptr; + + // The library name is not set by default, so the preset library name is used. + void* handle = dlopen("libbluetooth.so", RTLD_NOW); + if (!handle) { + const char* err_str = dlerror(); + ALOGE("%s: failed to load Bluetooth library, error=%s", __func__, + err_str ? err_str : "error unknown"); + goto error; + } + + // Get the address of the bt_interface_t. + itf = (bt_interface_t*)dlsym(handle, sym); + if (!itf) { + ALOGE("%s: failed to load symbol from Bluetooth library %s", __func__, sym); + goto error; + } + + // Success. + ALOGI("%s: loaded Bluetooth library successfully", __func__); + *interface = itf; + return 0; + +error: + *interface = NULL; + if (handle) dlclose(handle); + + return -EINVAL; +} + +static void classInitNative(JNIEnv* env, jclass clazz) { + jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic"); + android_bluetooth_UidTraffic.constructor = + env->GetMethodID(jniUidTrafficClass, "", "(IJJ)V"); + + jclass jniCallbackClass = + env->FindClass("com/android/bluetooth/btservice/JniCallbacks"); + sJniCallbacksField = env->GetFieldID( + clazz, "mJniCallbacks", "Lcom/android/bluetooth/btservice/JniCallbacks;"); + + method_oobDataReceivedCallback = + env->GetMethodID(jniCallbackClass, "oobDataReceivedCallback", + "(ILandroid/bluetooth/OobData;)V"); + + method_stateChangeCallback = + env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V"); + + method_adapterPropertyChangedCallback = env->GetMethodID( + jniCallbackClass, "adapterPropertyChangedCallback", "([I[[B)V"); + method_discoveryStateChangeCallback = env->GetMethodID( + jniCallbackClass, "discoveryStateChangeCallback", "(I)V"); + + method_devicePropertyChangedCallback = env->GetMethodID( + jniCallbackClass, "devicePropertyChangedCallback", "([B[I[[B)V"); + method_deviceFoundCallback = + env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V"); + method_pinRequestCallback = + env->GetMethodID(jniCallbackClass, "pinRequestCallback", "([B[BIZ)V"); + method_sspRequestCallback = + env->GetMethodID(jniCallbackClass, "sspRequestCallback", "([B[BIII)V"); + + method_bondStateChangeCallback = + env->GetMethodID(jniCallbackClass, "bondStateChangeCallback", "(I[BII)V"); + + method_addressConsolidateCallback = env->GetMethodID( + jniCallbackClass, "addressConsolidateCallback", "([B[B)V"); + + method_aclStateChangeCallback = + env->GetMethodID(jniCallbackClass, "aclStateChangeCallback", "(I[BIII)V"); + + method_linkQualityReportCallback = env->GetMethodID( + jniCallbackClass, "linkQualityReportCallback", "(JIIIIII)V"); + + method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z"); + method_acquireWakeLock = + env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z"); + method_releaseWakeLock = + env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z"); + method_energyInfo = env->GetMethodID( + clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V"); + + if (env->GetJavaVM(&vm) != JNI_OK) { + ALOGE("Could not get JavaVM"); + } + + if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) { + ALOGE("No Bluetooth Library found"); + } +} + +static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest, + jboolean isCommonCriteriaMode, int configCompareResult, + jobjectArray initFlags, jboolean isAtvDevice) { + ALOGV("%s", __func__); + + android_bluetooth_UidTraffic.clazz = + (jclass)env->NewGlobalRef(env->FindClass("android/bluetooth/UidTraffic")); + + sJniAdapterServiceObj = env->NewGlobalRef(obj); + sJniCallbacksObj = + env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField)); + + if (!sBluetoothInterface) { + return JNI_FALSE; + } + + int flagCount = env->GetArrayLength(initFlags); + jstring* flagObjs = new jstring[flagCount]; + const char** flags = nullptr; + if (flagCount > 0) { + flags = new const char*[flagCount + 1]; + flags[flagCount] = nullptr; + } + + for (int i = 0; i < flagCount; i++) { + flagObjs[i] = (jstring)env->GetObjectArrayElement(initFlags, i); + flags[i] = env->GetStringUTFChars(flagObjs[i], NULL); + } + + int ret = sBluetoothInterface->init( + &sBluetoothCallbacks, isGuest == JNI_TRUE ? 1 : 0, + isCommonCriteriaMode == JNI_TRUE ? 1 : 0, configCompareResult, flags, + isAtvDevice == JNI_TRUE ? 1 : 0); + + for (int i = 0; i < flagCount; i++) { + env->ReleaseStringUTFChars(flagObjs[i], flags[i]); + } + + delete[] flags; + delete[] flagObjs; + + if (ret != BT_STATUS_SUCCESS) { + ALOGE("Error while setting the callbacks: %d\n", ret); + sBluetoothInterface = NULL; + return JNI_FALSE; + } + ret = sBluetoothInterface->set_os_callouts(&sBluetoothOsCallouts); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("Error while setting Bluetooth callouts: %d\n", ret); + sBluetoothInterface->cleanup(); + sBluetoothInterface = NULL; + return JNI_FALSE; + } + + sBluetoothSocketInterface = + (btsock_interface_t*)sBluetoothInterface->get_profile_interface( + BT_PROFILE_SOCKETS_ID); + if (sBluetoothSocketInterface == NULL) { + ALOGE("Error getting socket interface"); + } + + return JNI_TRUE; +} + +static bool cleanupNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + sBluetoothInterface->cleanup(); + ALOGI("%s: return from cleanup", __func__); + + if (sJniCallbacksObj) { + env->DeleteGlobalRef(sJniCallbacksObj); + sJniCallbacksObj = NULL; + } + + if (sJniAdapterServiceObj) { + env->DeleteGlobalRef(sJniAdapterServiceObj); + sJniAdapterServiceObj = NULL; + } + + if (android_bluetooth_UidTraffic.clazz) { + env->DeleteGlobalRef(android_bluetooth_UidTraffic.clazz); + android_bluetooth_UidTraffic.clazz = NULL; + } + return JNI_TRUE; +} + +static jboolean enableNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + int ret = sBluetoothInterface->enable(); + return (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE + : JNI_FALSE; +} + +static jboolean disableNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + int ret = sBluetoothInterface->disable(); + /* Retrun JNI_FALSE only when BTIF explicitly reports + BT_STATUS_FAIL. It is fine for the BT_STATUS_NOT_READY + case which indicates that stack had not been enabled. + */ + return (ret == BT_STATUS_FAIL) ? JNI_FALSE : JNI_TRUE; +} + +static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + int ret = sBluetoothInterface->start_discovery(); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean cancelDiscoveryNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + int ret = sBluetoothInterface->cancel_discovery(); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean createBondNative(JNIEnv* env, jobject obj, jbyteArray address, + jint transport) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = sBluetoothInterface->create_bond((RawAddress*)addr, transport); + env->ReleaseByteArrayElements(address, addr, 0); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jbyteArray callByteArrayGetter(JNIEnv* env, jobject object, + const char* className, + const char* methodName) { + jclass myClass = env->FindClass(className); + jmethodID myMethod = env->GetMethodID(myClass, methodName, "()[B"); + return (jbyteArray)env->CallObjectMethod(object, myMethod); +} + +static jint callIntGetter(JNIEnv* env, jobject object, const char* className, + const char* methodName) { + jclass myClass = env->FindClass(className); + jmethodID myMethod = env->GetMethodID(myClass, methodName, "()I"); + return env->CallIntMethod(object, myMethod); +} + +static jboolean set_data(JNIEnv* env, bt_oob_data_t& oob_data, jobject oobData, + jint transport) { + // Need both arguments to be non NULL + if (oobData == NULL) { + ALOGE("%s: oobData is null! Nothing to do.", __func__); + return JNI_FALSE; + } + + memset(&oob_data, 0, sizeof(oob_data)); + + jbyteArray address = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getDeviceAddressWithType"); + + // Check the data + int len = env->GetArrayLength(address); + if (len != OOB_ADDRESS_SIZE) { + ALOGE("%s: addressBytes must be 7 bytes in length (address plus type) 6+1!", + __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + // Convert the address from byte[] + jbyte* addressBytes = env->GetByteArrayElements(address, NULL); + if (addressBytes == NULL) { + ALOGE("%s: addressBytes cannot be null!", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + memcpy(oob_data.address, addressBytes, len); + + // Get the device name byte[] java object + jbyteArray deviceName = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getDeviceName"); + + // Optional + // Convert it to a jbyte* and copy it to the struct + jbyte* deviceNameBytes = NULL; + if (deviceName != NULL) { + deviceNameBytes = env->GetByteArrayElements(deviceName, NULL); + int len = env->GetArrayLength(deviceName); + if (len > OOB_NAME_MAX_SIZE) { + ALOGI( + "%s: wrong length of deviceName, should be empty or less than or " + "equal to %d bytes.", + __func__, OOB_NAME_MAX_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0); + return JNI_FALSE; + } + memcpy(oob_data.device_name, deviceNameBytes, len); + env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0); + } + // Used by both classic and LE + jbyteArray confirmation = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getConfirmationHash"); + if (confirmation == NULL) { + ALOGE("%s: confirmation cannot be null!", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + // Confirmation is mandatory + jbyte* confirmationBytes = NULL; + confirmationBytes = env->GetByteArrayElements(confirmation, NULL); + len = env->GetArrayLength(confirmation); + if (confirmationBytes == NULL || len != OOB_C_SIZE) { + ALOGI( + "%s: wrong length of Confirmation, should be empty or %d " + "bytes.", + __func__, OOB_C_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0); + return JNI_FALSE; + } + memcpy(oob_data.c, confirmationBytes, len); + env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0); + + // Random is supposedly optional according to the specification + jbyteArray randomizer = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getRandomizerHash"); + jbyte* randomizerBytes = NULL; + if (randomizer != NULL) { + randomizerBytes = env->GetByteArrayElements(randomizer, NULL); + int len = env->GetArrayLength(randomizer); + if (randomizerBytes == NULL || len != OOB_R_SIZE) { + ALOGI("%s: wrong length of Random, should be empty or %d bytes.", + __func__, OOB_R_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0); + return JNI_FALSE; + } + memcpy(oob_data.r, randomizerBytes, len); + env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0); + } + + // Transport specific data fetching/setting + if (transport == TRANSPORT_BREDR) { + // Classic + // Not optional + jbyteArray oobDataLength = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getClassicLength"); + jbyte* oobDataLengthBytes = NULL; + if (oobDataLength == NULL || + env->GetArrayLength(oobDataLength) != OOB_DATA_LEN_SIZE) { + ALOGI("%s: wrong length of oobDataLength, should be empty or %d bytes.", + __func__, OOB_DATA_LEN_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0); + return JNI_FALSE; + } + + oobDataLengthBytes = env->GetByteArrayElements(oobDataLength, NULL); + memcpy(oob_data.oob_data_length, oobDataLengthBytes, len); + env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0); + + // Optional + jbyteArray classOfDevice = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getClassOfDevice"); + jbyte* classOfDeviceBytes = NULL; + if (classOfDevice != NULL) { + classOfDeviceBytes = env->GetByteArrayElements(classOfDevice, NULL); + int len = env->GetArrayLength(classOfDevice); + if (len != OOB_COD_SIZE) { + ALOGI("%s: wrong length of classOfDevice, should be empty or %d bytes.", + __func__, OOB_COD_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0); + return JNI_FALSE; + } + memcpy(oob_data.class_of_device, classOfDeviceBytes, len); + env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0); + } + } else if (transport == TRANSPORT_LE) { + // LE + jbyteArray temporaryKey = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getLeTemporaryKey"); + jbyte* temporaryKeyBytes = NULL; + if (temporaryKey != NULL) { + temporaryKeyBytes = env->GetByteArrayElements(temporaryKey, NULL); + int len = env->GetArrayLength(temporaryKey); + if (len != OOB_TK_SIZE) { + ALOGI("%s: wrong length of temporaryKey, should be empty or %d bytes.", + __func__, OOB_TK_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0); + return JNI_FALSE; + } + memcpy(oob_data.sm_tk, temporaryKeyBytes, len); + env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0); + } + + jbyteArray leAppearance = callByteArrayGetter( + env, oobData, "android/bluetooth/OobData", "getLeAppearance"); + jbyte* leAppearanceBytes = NULL; + if (leAppearance != NULL) { + leAppearanceBytes = env->GetByteArrayElements(leAppearance, NULL); + int len = env->GetArrayLength(leAppearance); + if (len != OOB_LE_APPEARANCE_SIZE) { + ALOGI("%s: wrong length of leAppearance, should be empty or %d bytes.", + __func__, OOB_LE_APPEARANCE_SIZE); + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0); + return JNI_FALSE; + } + memcpy(oob_data.le_appearance, leAppearanceBytes, len); + env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0); + } + + jint leRole = callIntGetter(env, oobData, "android/bluetooth/OobData", + "getLeDeviceRole"); + oob_data.le_device_role = leRole; + + jint leFlag = + callIntGetter(env, oobData, "android/bluetooth/OobData", "getLeFlags"); + oob_data.le_flags = leFlag; + } + return JNI_TRUE; +} + +static void generateLocalOobDataNative(JNIEnv* env, jobject obj, + jint transport) { + // No BT interface? Can't do anything. + if (!sBluetoothInterface) return; + + if (sBluetoothInterface->generate_local_oob_data(transport) != + BT_STATUS_SUCCESS) { + ALOGE("%s: Call to generate_local_oob_data failed!", __func__); + bt_oob_data_t oob_data; + oob_data.is_valid = false; + generate_local_oob_data_callback(transport, oob_data); + } +} + +static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj, + jbyteArray address, jint transport, + jobject p192Data, jobject p256Data) { + // No BT interface? Can't do anything. + if (!sBluetoothInterface) return JNI_FALSE; + + // No data? Can't do anything + if (p192Data == NULL && p256Data == NULL) { + ALOGE("%s: All OOB Data are null! Nothing to do.", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + // This address is already reversed which is why its being passed... + // In the future we want to remove this and just reverse the address + // for the oobdata in the host stack. + if (address == NULL) { + ALOGE("%s: Address cannot be null! Nothing to do.", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + // Check the data + int len = env->GetArrayLength(address); + if (len != 6) { + ALOGE("%s: addressBytes must be 6 bytes in length (address plus type) 6+1!", + __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + // Convert P192 data from Java POJO to C Struct + bt_oob_data_t p192_data; + if (p192Data != NULL) { + if (set_data(env, p192_data, p192Data, transport) == JNI_FALSE) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + } + + // Convert P256 data from Java POJO to C Struct + bt_oob_data_t p256_data; + if (p256Data != NULL) { + if (set_data(env, p256_data, p256Data, transport) == JNI_FALSE) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + } + + return ((sBluetoothInterface->create_bond_out_of_band( + (RawAddress*)addr, transport, &p192_data, &p256_data)) == + BT_STATUS_SUCCESS) + ? JNI_TRUE + : JNI_FALSE; +} + +static jboolean removeBondNative(JNIEnv* env, jobject obj, jbyteArray address) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = sBluetoothInterface->remove_bond((RawAddress*)addr); + env->ReleaseByteArrayElements(address, addr, 0); + + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean cancelBondNative(JNIEnv* env, jobject obj, jbyteArray address) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = sBluetoothInterface->cancel_bond((RawAddress*)addr); + env->ReleaseByteArrayElements(address, addr, 0); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static int getConnectionStateNative(JNIEnv* env, jobject obj, + jbyteArray address) { + ALOGV("%s", __func__); + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = sBluetoothInterface->get_connection_state((RawAddress*)addr); + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean pinReplyNative(JNIEnv* env, jobject obj, jbyteArray address, + jboolean accept, jint len, jbyteArray pinArray) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + jbyte* pinPtr = NULL; + if (accept) { + pinPtr = env->GetByteArrayElements(pinArray, NULL); + if (pinPtr == NULL) { + jniThrowIOException(env, EINVAL); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_FALSE; + } + } + + int ret = sBluetoothInterface->pin_reply((RawAddress*)addr, accept, len, + (bt_pin_code_t*)pinPtr); + env->ReleaseByteArrayElements(address, addr, 0); + env->ReleaseByteArrayElements(pinArray, pinPtr, 0); + + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean sspReplyNative(JNIEnv* env, jobject obj, jbyteArray address, + jint type, jboolean accept, jint passkey) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = sBluetoothInterface->ssp_reply( + (RawAddress*)addr, (bt_ssp_variant_t)type, accept, passkey); + env->ReleaseByteArrayElements(address, addr, 0); + + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setAdapterPropertyNative(JNIEnv* env, jobject obj, jint type, + jbyteArray value) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* val = env->GetByteArrayElements(value, NULL); + bt_property_t prop; + prop.type = (bt_property_type_t)type; + prop.len = env->GetArrayLength(value); + prop.val = val; + + int ret = sBluetoothInterface->set_adapter_property(&prop); + env->ReleaseByteArrayElements(value, val, 0); + + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean getAdapterPropertiesNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + int ret = sBluetoothInterface->get_adapter_properties(); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean getAdapterPropertyNative(JNIEnv* env, jobject obj, jint type) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + int ret = sBluetoothInterface->get_adapter_property((bt_property_type_t)type); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean getDevicePropertyNative(JNIEnv* env, jobject obj, + jbyteArray address, jint type) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = sBluetoothInterface->get_remote_device_property( + (RawAddress*)addr, (bt_property_type_t)type); + env->ReleaseByteArrayElements(address, addr, 0); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setDevicePropertyNative(JNIEnv* env, jobject obj, + jbyteArray address, jint type, + jbyteArray value) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* val = env->GetByteArrayElements(value, NULL); + if (val == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + env->ReleaseByteArrayElements(value, val, 0); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_property_t prop; + prop.type = (bt_property_type_t)type; + prop.len = env->GetArrayLength(value); + prop.val = val; + + int ret = + sBluetoothInterface->set_remote_device_property((RawAddress*)addr, &prop); + env->ReleaseByteArrayElements(value, val, 0); + env->ReleaseByteArrayElements(address, addr, 0); + + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean getRemoteServicesNative(JNIEnv* env, jobject obj, + jbyteArray address, jint transport) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + jbyte* addr = addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + int ret = + sBluetoothInterface->get_remote_services((RawAddress*)addr, transport); + env->ReleaseByteArrayElements(address, addr, 0); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static int readEnergyInfo() { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + int ret = sBluetoothInterface->read_energy_info(); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static void dumpNative(JNIEnv* env, jobject obj, jobject fdObj, + jobjectArray argArray) { + ALOGV("%s", __func__); + if (!sBluetoothInterface) return; + + int fd = jniGetFDFromFileDescriptor(env, fdObj); + if (fd < 0) return; + + int numArgs = env->GetArrayLength(argArray); + + jstring* argObjs = new jstring[numArgs]; + const char** args = nullptr; + if (numArgs > 0) { + args = new const char*[numArgs + 1]; + args[numArgs] = nullptr; + } + + for (int i = 0; i < numArgs; i++) { + argObjs[i] = (jstring)env->GetObjectArrayElement(argArray, i); + args[i] = env->GetStringUTFChars(argObjs[i], NULL); + } + + sBluetoothInterface->dump(fd, args); + + for (int i = 0; i < numArgs; i++) { + env->ReleaseStringUTFChars(argObjs[i], args[i]); + } + + delete[] args; + delete[] argObjs; +} + +static jbyteArray dumpMetricsNative(JNIEnv* env, jobject obj) { + ALOGI("%s", __func__); + if (!sBluetoothInterface) return env->NewByteArray(0); + + std::string output; + sBluetoothInterface->dumpMetrics(&output); + jsize output_size = output.size() * sizeof(char); + jbyteArray output_bytes = env->NewByteArray(output_size); + env->SetByteArrayRegion(output_bytes, 0, output_size, + (const jbyte*)output.data()); + return output_bytes; +} + +static jboolean factoryResetNative(JNIEnv* env, jobject obj) { + ALOGV("%s", __func__); + if (!sBluetoothInterface) return JNI_FALSE; + int ret = sBluetoothInterface->config_clear(); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jbyteArray obfuscateAddressNative(JNIEnv* env, jobject obj, + jbyteArray address) { + ALOGV("%s", __func__); + if (!sBluetoothInterface) return env->NewByteArray(0); + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (addr == nullptr) { + jniThrowIOException(env, EINVAL); + return env->NewByteArray(0); + } + RawAddress addr_obj = {}; + addr_obj.FromOctets((uint8_t*)addr); + std::string output = sBluetoothInterface->obfuscate_address(addr_obj); + jsize output_size = output.size() * sizeof(char); + jbyteArray output_bytes = env->NewByteArray(output_size); + env->SetByteArrayRegion(output_bytes, 0, output_size, + (const jbyte*)output.data()); + return output_bytes; +} + +static jboolean setBufferLengthMillisNative(JNIEnv* env, jobject obj, + jint codec, jint size) { + ALOGV("%s", __func__); + + if (!sBluetoothInterface) return JNI_FALSE; + + int ret = sBluetoothInterface->set_dynamic_audio_buffer_size(codec, size); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jint connectSocketNative(JNIEnv* env, jobject obj, jbyteArray address, + jint type, jbyteArray uuid, jint port, + jint flag, jint callingUid) { + int socket_fd = INVALID_FD; + jbyte* addr = nullptr; + jbyte* uuidBytes = nullptr; + Uuid btUuid; + + if (!sBluetoothSocketInterface) { + goto done; + } + addr = env->GetByteArrayElements(address, nullptr); + uuidBytes = env->GetByteArrayElements(uuid, nullptr); + if (addr == nullptr || uuidBytes == nullptr) { + jniThrowIOException(env, EINVAL); + goto done; + } + + btUuid = Uuid::From128BitBE((uint8_t*)uuidBytes); + if (sBluetoothSocketInterface->connect((RawAddress*)addr, (btsock_type_t)type, + &btUuid, port, &socket_fd, flag, + callingUid) != BT_STATUS_SUCCESS) { + socket_fd = INVALID_FD; + } + +done: + if (addr) env->ReleaseByteArrayElements(address, addr, 0); + if (uuidBytes) env->ReleaseByteArrayElements(uuid, uuidBytes, 0); + return socket_fd; +} + +static jint createSocketChannelNative(JNIEnv* env, jobject obj, jint type, + jstring serviceName, jbyteArray uuid, + jint port, jint flag, jint callingUid) { + int socket_fd = INVALID_FD; + jbyte* uuidBytes = nullptr; + Uuid btUuid; + const char* nativeServiceName = nullptr; + + if (!sBluetoothSocketInterface) { + goto done; + } + uuidBytes = env->GetByteArrayElements(uuid, nullptr); + if (serviceName != nullptr) { + nativeServiceName = env->GetStringUTFChars(serviceName, nullptr); + } + if (uuidBytes == nullptr) { + jniThrowIOException(env, EINVAL); + goto done; + } + btUuid = Uuid::From128BitBE((uint8_t*)uuidBytes); + + if (sBluetoothSocketInterface->listen((btsock_type_t)type, nativeServiceName, + &btUuid, port, &socket_fd, flag, + callingUid) != BT_STATUS_SUCCESS) { + socket_fd = INVALID_FD; + } + +done: + if (uuidBytes) env->ReleaseByteArrayElements(uuid, uuidBytes, 0); + if (nativeServiceName) + env->ReleaseStringUTFChars(serviceName, nativeServiceName); + return socket_fd; +} + +static void requestMaximumTxDataLengthNative(JNIEnv* env, jobject obj, + jbyteArray address) { + if (!sBluetoothSocketInterface) { + return; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (addr == nullptr) { + jniThrowIOException(env, EINVAL); + return; + } + + RawAddress addressVar = *(RawAddress*)addr; + sBluetoothSocketInterface->request_max_tx_data_length(addressVar); + env->ReleaseByteArrayElements(address, addr, 1); +} + +static int getMetricIdNative(JNIEnv* env, jobject obj, jbyteArray address) { + ALOGV("%s", __func__); + if (!sBluetoothInterface) return 0; // 0 is invalid id + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (addr == nullptr) { + jniThrowIOException(env, EINVAL); + return 0; + } + RawAddress addr_obj = {}; + addr_obj.FromOctets((uint8_t*)addr); + return sBluetoothInterface->get_metric_id(addr_obj); +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "(ZZI[Ljava/lang/String;Z)Z", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"enableNative", "()Z", (void*)enableNative}, + {"disableNative", "()Z", (void*)disableNative}, + {"setAdapterPropertyNative", "(I[B)Z", (void*)setAdapterPropertyNative}, + {"getAdapterPropertiesNative", "()Z", (void*)getAdapterPropertiesNative}, + {"getAdapterPropertyNative", "(I)Z", (void*)getAdapterPropertyNative}, + {"getDevicePropertyNative", "([BI)Z", (void*)getDevicePropertyNative}, + {"setDevicePropertyNative", "([BI[B)Z", (void*)setDevicePropertyNative}, + {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative}, + {"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative}, + {"createBondNative", "([BI)Z", (void*)createBondNative}, + {"createBondOutOfBandNative", + "([BILandroid/bluetooth/OobData;Landroid/bluetooth/OobData;)Z", + (void*)createBondOutOfBandNative}, + {"removeBondNative", "([B)Z", (void*)removeBondNative}, + {"cancelBondNative", "([B)Z", (void*)cancelBondNative}, + {"generateLocalOobDataNative", "(I)V", (void*)generateLocalOobDataNative}, + {"getConnectionStateNative", "([B)I", (void*)getConnectionStateNative}, + {"pinReplyNative", "([BZI[B)Z", (void*)pinReplyNative}, + {"sspReplyNative", "([BIZI)Z", (void*)sspReplyNative}, + {"getRemoteServicesNative", "([BI)Z", (void*)getRemoteServicesNative}, + {"alarmFiredNative", "()V", (void*)alarmFiredNative}, + {"readEnergyInfo", "()I", (void*)readEnergyInfo}, + {"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V", + (void*)dumpNative}, + {"dumpMetricsNative", "()[B", (void*)dumpMetricsNative}, + {"factoryResetNative", "()Z", (void*)factoryResetNative}, + {"obfuscateAddressNative", "([B)[B", (void*)obfuscateAddressNative}, + {"setBufferLengthMillisNative", "(II)Z", + (void*)setBufferLengthMillisNative}, + {"getMetricIdNative", "([B)I", (void*)getMetricIdNative}, + {"connectSocketNative", "([BI[BIII)I", (void*)connectSocketNative}, + {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I", + (void*)createSocketChannelNative}, + {"requestMaximumTxDataLengthNative", "([B)V", + (void*)requestMaximumTxDataLengthNative}}; + +int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/btservice/AdapterService", sMethods, + NELEM(sMethods)); +} + +} /* namespace android */ + +/* + * JNI Initialization + */ +jint JNI_OnLoad(JavaVM* jvm, void* reserved) { + JNIEnv* e; + int status; + + ALOGV("Bluetooth Adapter Service : loading JNI\n"); + + // Check JNI version + if (jvm->GetEnv((void**)&e, JNI_VERSION_1_6)) { + ALOGE("JNI version mismatch error"); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_btservice_AdapterService(e); + if (status < 0) { + ALOGE("jni adapter service registration failure, status: %d", status); + return JNI_ERR; + } + + status = + android::register_com_android_bluetooth_btservice_activity_attribution(e); + if (status < 0) { + ALOGE("jni activity attribution registration failure: %d", status); + return JNI_ERR; + } + + status = + android::register_com_android_bluetooth_btservice_BluetoothKeystore(e); + if (status < 0) { + ALOGE("jni BluetoothKeyStore registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_hfp(e); + if (status < 0) { + ALOGE("jni hfp registration failure, status: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_hfpclient(e); + if (status < 0) { + ALOGE("jni hfp client registration failure, status: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_a2dp(e); + if (status < 0) { + ALOGE("jni a2dp source registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_a2dp_sink(e); + if (status < 0) { + ALOGE("jni a2dp sink registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_avrcp_target(e); + if (status < 0) { + ALOGE("jni new avrcp target registration failure: %d", status); + } + + status = android::register_com_android_bluetooth_avrcp_controller(e); + if (status < 0) { + ALOGE("jni avrcp controller registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_hid_host(e); + if (status < 0) { + ALOGE("jni hid registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_hid_device(e); + if (status < 0) { + ALOGE("jni hidd registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_pan(e); + if (status < 0) { + ALOGE("jni pan registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_gatt(e); + if (status < 0) { + ALOGE("jni gatt registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_sdp(e); + if (status < 0) { + ALOGE("jni sdp registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_hearing_aid(e); + if (status < 0) { + ALOGE("jni hearing aid registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_le_audio(e); + if (status < 0) { + ALOGE("jni le_audio registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_vc(e); + if (status < 0) { + ALOGE("jni vc registration failure: %d", status); + return JNI_ERR; + } + + status = android::register_com_android_bluetooth_csip_set_coordinator(e); + if (status < 0) { + ALOGE("jni csis client registration failure: %d", status); + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} diff --git a/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp b/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bceea1d735b7e7654fa05b87718f6e9cef51c0a8 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2020 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. + */ + +#define LOG_TAG "BluetoothKeystoreServiceJni" + +#include "base/logging.h" +#include "com_android_bluetooth.h" +#include "hardware/bt_keystore.h" + +#include +#include + +using bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks; +using bluetooth::bluetooth_keystore::BluetoothKeystoreInterface; + +namespace android { +static jmethodID method_setEncryptKeyOrRemoveKeyCallback; +static jmethodID method_getKeyCallback; + +static BluetoothKeystoreInterface* sBluetoothKeystoreInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +class BluetoothKeystoreCallbacksImpl + : public bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks { + public: + ~BluetoothKeystoreCallbacksImpl() = default; + + void set_encrypt_key_or_remove_key( + const std::string prefixString, + const std::string decryptedString) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + jstring j_prefixString = sCallbackEnv->NewStringUTF(prefixString.c_str()); + jstring j_decryptedString = + sCallbackEnv->NewStringUTF(decryptedString.c_str()); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, + method_setEncryptKeyOrRemoveKeyCallback, + j_prefixString, j_decryptedString); + } + + std::string get_key(const std::string prefixString) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return ""; + + jstring j_prefixString = sCallbackEnv->NewStringUTF(prefixString.c_str()); + + jstring j_decrypt_str = (jstring)sCallbackEnv->CallObjectMethod( + mCallbacksObj, method_getKeyCallback, j_prefixString); + + if (j_decrypt_str == nullptr) { + ALOGE("%s: Got a null decrypt_str", __func__); + return ""; + } + + const char* value = sCallbackEnv->GetStringUTFChars(j_decrypt_str, nullptr); + std::string ret(value); + sCallbackEnv->ReleaseStringUTFChars(j_decrypt_str, value); + + return ret; + } +}; + +static BluetoothKeystoreCallbacksImpl sBluetoothKeystoreCallbacks; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_setEncryptKeyOrRemoveKeyCallback = + env->GetMethodID(clazz, "setEncryptKeyOrRemoveKeyCallback", + "(Ljava/lang/String;Ljava/lang/String;)V"); + + method_getKeyCallback = env->GetMethodID( + clazz, "getKeyCallback", "(Ljava/lang/String;)Ljava/lang/String;"); + + LOG(INFO) << __func__ << ": succeeds"; +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sBluetoothKeystoreInterface != nullptr) { + LOG(INFO) + << "Cleaning up BluetoothKeystore Interface before initializing..."; + sBluetoothKeystoreInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + LOG(INFO) << "Cleaning up BluetoothKeystore callback object"; + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + LOG(ERROR) + << "Failed to allocate Global Ref for BluetoothKeystore Callbacks"; + return; + } + + sBluetoothKeystoreInterface = + (BluetoothKeystoreInterface*)btInf->get_profile_interface(BT_KEYSTORE_ID); + if (sBluetoothKeystoreInterface == nullptr) { + LOG(ERROR) << "Failed to get BluetoothKeystore Interface"; + return; + } + + sBluetoothKeystoreInterface->init(&sBluetoothKeystoreCallbacks); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sBluetoothKeystoreInterface != nullptr) { + sBluetoothKeystoreInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, +}; + +int register_com_android_bluetooth_btservice_BluetoothKeystore(JNIEnv* env) { + return jniRegisterNativeMethods( + env, + "com/android/bluetooth/btservice/bluetoothkeystore/" + "BluetoothKeystoreNativeInterface", + sMethods, NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp b/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45ebe6e807ef273ead02c062fa80ca43f93fefa7 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp @@ -0,0 +1,296 @@ + +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ +#define LOG_TAG "BluetoothCsipSetCoordinatorJni" +#define LOG_NDEBUG 0 +#include + +#include + +#include "base/logging.h" +#include "com_android_bluetooth.h" +#include "hardware/bt_csis.h" + +using bluetooth::csis::ConnectionState; +using bluetooth::csis::CsisClientCallbacks; +using bluetooth::csis::CsisClientInterface; +using bluetooth::csis::CsisGroupLockStatus; + +namespace android { +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onDeviceAvailable; +static jmethodID method_onSetMemberAvailable; +static jmethodID method_onGroupLockChanged; + +static CsisClientInterface* sCsisClientInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +using bluetooth::Uuid; + +#define UUID_PARAMS(uuid) uuid_lsb(uuid), uuid_msb(uuid) + +static uint64_t uuid_lsb(const Uuid& uuid) { + uint64_t lsb = 0; + + auto uu = uuid.To128BitBE(); + for (int i = 8; i <= 15; i++) { + lsb <<= 8; + lsb |= uu[i]; + } + + return lsb; +} + +static uint64_t uuid_msb(const Uuid& uuid) { + uint64_t msb = 0; + + auto uu = uuid.To128BitBE(); + for (int i = 0; i <= 7; i++) { + msb <<= 8; + msb |= uu[i]; + } + + return msb; +} + +class CsisClientCallbacksImpl : public CsisClientCallbacks { + public: + ~CsisClientCallbacksImpl() = default; + + void OnConnectionState(const RawAddress& bd_addr, + ConnectionState state) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new bd addr jbyteArray for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + addr.get(), (jint)state); + } + + void OnDeviceAvailable(const RawAddress& bd_addr, int group_id, + int group_size, const bluetooth::Uuid& uuid) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new bd addr jbyteArray for device available"; + return; + } + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable, + addr.get(), (jint)group_id, (jint)group_size, + UUID_PARAMS(uuid)); + } + + void OnSetMemberAvailable(const RawAddress& bd_addr, int group_id) override { + LOG(INFO) << __func__ << ", group id:" << group_id; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetMemberAvailable, + addr.get(), (jint)group_id); + } + + void OnGroupLockChanged(int group_id, bool locked, + CsisGroupLockStatus status) override { + LOG(INFO) << __func__ << ", group_id: " << int(group_id) + << ", locked: " << locked << ", status: " << (int)status; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupLockChanged, + (jint)group_id, (jboolean)locked, + (jint)status); + } +}; + +static CsisClientCallbacksImpl sCsisClientCallbacks; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); + + method_onDeviceAvailable = + env->GetMethodID(clazz, "onDeviceAvailable", "([BIIJJ)V"); + + method_onSetMemberAvailable = + env->GetMethodID(clazz, "onSetMemberAvailable", "([BI)V"); + + method_onGroupLockChanged = + env->GetMethodID(clazz, "onGroupLockChanged", "(IZI)V"); + + LOG(INFO) << __func__ << ": succeeds"; +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sCsisClientInterface != nullptr) { + LOG(INFO) << "Cleaning up Csis Interface before initializing..."; + sCsisClientInterface->Cleanup(); + sCsisClientInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + LOG(INFO) << "Cleaning up Csis callback object"; + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + LOG(ERROR) << "Failed to allocate Global Ref for Csis Client Callbacks"; + return; + } + + sCsisClientInterface = (CsisClientInterface*)btInf->get_profile_interface( + BT_PROFILE_CSIS_CLIENT_ID); + if (sCsisClientInterface == nullptr) { + LOG(ERROR) << "Failed to get Csis Client Interface"; + return; + } + + sCsisClientInterface->Init(&sCsisClientCallbacks); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sCsisClientInterface != nullptr) { + sCsisClientInterface->Cleanup(); + sCsisClientInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static jboolean connectNative(JNIEnv* env, jobject object, jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sCsisClientInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Csis Client Interface Interface"; + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sCsisClientInterface->Connect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static jboolean disconnectNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sCsisClientInterface) { + LOG(ERROR) << __func__ << ": Failed to get the Csis Client Interface"; + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sCsisClientInterface->Disconnect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static void groupLockSetNative(JNIEnv* env, jobject object, jint group_id, + jboolean lock) { + LOG(INFO) << __func__; + + if (!sCsisClientInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Bluetooth Csis Client Interface"; + return; + } + + sCsisClientInterface->LockGroup(group_id, lock); +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectNative", "([B)Z", (void*)connectNative}, + {"disconnectNative", "([B)Z", (void*)disconnectNative}, + {"groupLockSetNative", "(IZ)V", (void*)groupLockSetNative}, +}; + +int register_com_android_bluetooth_csip_set_coordinator(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface", + sMethods, NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..642e589ee5af29047d879dc367fd91ea13005cb8 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_gatt.cpp @@ -0,0 +1,2468 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "BtGatt.JNI" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_gatt.h" +#include "utils/Log.h" + +#include +#include +#include +#include +#include + +#include +#define info(fmt, ...) ALOGI("%s(L%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define debug(fmt, ...) \ + ALOGD("%s(L%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define warn(fmt, ...) \ + ALOGW("WARNING: %s(L%d): " fmt "##", __func__, __LINE__, ##__VA_ARGS__) +#define error(fmt, ...) \ + ALOGE("ERROR: %s(L%d): " fmt "##", __func__, __LINE__, ##__VA_ARGS__) +#define asrt(s) \ + if (!(s)) ALOGE("%s(L%d): ASSERT %s failed! ##", __func__, __LINE__, #s) + +using bluetooth::Uuid; + +#define UUID_PARAMS(uuid) uuid_lsb(uuid), uuid_msb(uuid) + +static Uuid from_java_uuid(jlong uuid_msb, jlong uuid_lsb) { + std::array uu; + for (int i = 0; i < 8; i++) { + uu[7 - i] = (uuid_msb >> (8 * i)) & 0xFF; + uu[15 - i] = (uuid_lsb >> (8 * i)) & 0xFF; + } + return Uuid::From128BitBE(uu); +} + +static uint64_t uuid_lsb(const Uuid& uuid) { + uint64_t lsb = 0; + + auto uu = uuid.To128BitBE(); + for (int i = 8; i <= 15; i++) { + lsb <<= 8; + lsb |= uu[i]; + } + + return lsb; +} + +static uint64_t uuid_msb(const Uuid& uuid) { + uint64_t msb = 0; + + auto uu = uuid.To128BitBE(); + for (int i = 0; i <= 7; i++) { + msb <<= 8; + msb |= uu[i]; + } + + return msb; +} + +static RawAddress str2addr(JNIEnv* env, jstring address) { + RawAddress bd_addr; + const char* c_address = env->GetStringUTFChars(address, NULL); + if (!c_address) return bd_addr; + + RawAddress::FromString(std::string(c_address), bd_addr); + env->ReleaseStringUTFChars(address, c_address); + + return bd_addr; +} + +static jstring bdaddr2newjstr(JNIEnv* env, const RawAddress* bda) { + char c_address[32]; + snprintf(c_address, sizeof(c_address), "%02X:%02X:%02X:%02X:%02X:%02X", + bda->address[0], bda->address[1], bda->address[2], bda->address[3], + bda->address[4], bda->address[5]); + + return env->NewStringUTF(c_address); +} + +static std::vector toVector(JNIEnv* env, jbyteArray ba) { + jbyte* data_data = env->GetByteArrayElements(ba, NULL); + uint16_t data_len = (uint16_t)env->GetArrayLength(ba); + std::vector data_vec(data_data, data_data + data_len); + env->ReleaseByteArrayElements(ba, data_data, JNI_ABORT); + return data_vec; +} + +namespace android { + +/** + * Client callback methods + */ + +static jmethodID method_onClientRegistered; +static jmethodID method_onScannerRegistered; +static jmethodID method_onScanResult; +static jmethodID method_onConnected; +static jmethodID method_onDisconnected; +static jmethodID method_onReadCharacteristic; +static jmethodID method_onWriteCharacteristic; +static jmethodID method_onExecuteCompleted; +static jmethodID method_onSearchCompleted; +static jmethodID method_onReadDescriptor; +static jmethodID method_onWriteDescriptor; +static jmethodID method_onNotify; +static jmethodID method_onRegisterForNotifications; +static jmethodID method_onReadRemoteRssi; +static jmethodID method_onConfigureMTU; +static jmethodID method_onScanFilterConfig; +static jmethodID method_onScanFilterParamsConfigured; +static jmethodID method_onScanFilterEnableDisabled; +static jmethodID method_onClientCongestion; +static jmethodID method_onBatchScanStorageConfigured; +static jmethodID method_onBatchScanStartStopped; +static jmethodID method_onBatchScanReports; +static jmethodID method_onBatchScanThresholdCrossed; + +static jmethodID method_createOnTrackAdvFoundLostObject; +static jmethodID method_onTrackAdvFoundLost; +static jmethodID method_onScanParamSetupCompleted; +static jmethodID method_getSampleGattDbElement; +static jmethodID method_onGetGattDb; +static jmethodID method_onClientPhyUpdate; +static jmethodID method_onClientPhyRead; +static jmethodID method_onClientConnUpdate; +static jmethodID method_onServiceChanged; + +/** + * Server callback methods + */ +static jmethodID method_onServerRegistered; +static jmethodID method_onClientConnected; +static jmethodID method_onServiceAdded; +static jmethodID method_onServiceStopped; +static jmethodID method_onServiceDeleted; +static jmethodID method_onResponseSendCompleted; +static jmethodID method_onServerReadCharacteristic; +static jmethodID method_onServerReadDescriptor; +static jmethodID method_onServerWriteCharacteristic; +static jmethodID method_onServerWriteDescriptor; +static jmethodID method_onExecuteWrite; +static jmethodID method_onNotificationSent; +static jmethodID method_onServerCongestion; +static jmethodID method_onServerMtuChanged; +static jmethodID method_onServerPhyUpdate; +static jmethodID method_onServerPhyRead; +static jmethodID method_onServerConnUpdate; + +/** + * Advertiser callback methods + */ +static jmethodID method_onAdvertisingSetStarted; +static jmethodID method_onOwnAddressRead; +static jmethodID method_onAdvertisingEnabled; +static jmethodID method_onAdvertisingDataSet; +static jmethodID method_onScanResponseDataSet; +static jmethodID method_onAdvertisingParametersUpdated; +static jmethodID method_onPeriodicAdvertisingParametersUpdated; +static jmethodID method_onPeriodicAdvertisingDataSet; +static jmethodID method_onPeriodicAdvertisingEnabled; + +/** + * Periodic scanner callback methods + */ +static jmethodID method_onSyncLost; +static jmethodID method_onSyncReport; +static jmethodID method_onSyncStarted; + +/** + * Static variables + */ + +static const btgatt_interface_t* sGattIf = NULL; +static jobject mCallbacksObj = NULL; +static jobject mAdvertiseCallbacksObj = NULL; +static jobject mPeriodicScanCallbacksObj = NULL; + +/** + * BTA client callbacks + */ + +void btgattc_register_app_cb(int status, int clientIf, const Uuid& app_uuid) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status, + clientIf, UUID_PARAMS(app_uuid)); +} + +void btgattc_scan_result_cb(uint16_t event_type, uint8_t addr_type, + RawAddress* bda, 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) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), bda)); + ScopedLocalRef jb(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(adv_data.size())); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(), + (jbyte*)adv_data.data()); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type, + addr_type, address.get(), primary_phy, + secondary_phy, advertising_sid, tx_power, rssi, + periodic_adv_int, jb.get()); +} + +void btgattc_open_cb(int conn_id, int status, int clientIf, + const RawAddress& bda) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnected, clientIf, + conn_id, status, address.get()); +} + +void btgattc_close_cb(int conn_id, int status, int clientIf, + const RawAddress& bda) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDisconnected, clientIf, + conn_id, status, address.get()); +} + +void btgattc_search_complete_cb(int conn_id, int status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSearchCompleted, conn_id, + status); +} + +void btgattc_register_for_notification_cb(int conn_id, int registered, + int status, uint16_t handle) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) 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) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address( + sCallbackEnv.get(), bdaddr2newjstr(sCallbackEnv.get(), &p_data.bda)); + ScopedLocalRef jb(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(p_data.len)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, p_data.len, + (jbyte*)p_data.value); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNotify, conn_id, + address.get(), p_data.handle, p_data.is_notify, + jb.get()); +} + +void btgattc_read_characteristic_cb(int conn_id, int status, + btgatt_read_params_t* p_data) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef jb(sCallbackEnv.get(), NULL); + if (status == 0) { // Success + jb.reset(sCallbackEnv->NewByteArray(p_data->value.len)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, p_data->value.len, + (jbyte*)p_data->value.value); + } else { + uint8_t value = 0; + jb.reset(sCallbackEnv->NewByteArray(1)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 1, (jbyte*)&value); + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadCharacteristic, + conn_id, status, p_data->handle, jb.get()); +} + +void btgattc_write_characteristic_cb(int conn_id, int status, uint16_t handle, + uint16_t len, const uint8_t* value) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef jb(sCallbackEnv.get(), NULL); + if (status == 0) { // Success + jb.reset(sCallbackEnv->NewByteArray(len)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, len, (jbyte*)value); + } else { + uint8_t value = 0; + jb.reset(sCallbackEnv->NewByteArray(1)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 1, (jbyte*)&value); + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWriteCharacteristic, + conn_id, status, handle, jb.get()); +} + +void btgattc_execute_write_cb(int conn_id, int status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExecuteCompleted, + conn_id, status); +} + +void btgattc_read_descriptor_cb(int conn_id, int status, + const btgatt_read_params_t& p_data) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef jb(sCallbackEnv.get(), NULL); + if (p_data.value.len != 0) { + jb.reset(sCallbackEnv->NewByteArray(p_data.value.len)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, p_data.value.len, + (jbyte*)p_data.value.value); + } else { + jb.reset(sCallbackEnv->NewByteArray(1)); + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadDescriptor, conn_id, + status, p_data.handle, jb.get()); +} + +void btgattc_write_descriptor_cb(int conn_id, int status, uint16_t handle, + uint16_t len, const uint8_t* value) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef jb(sCallbackEnv.get(), NULL); + if (status == 0) { // Success + jb.reset(sCallbackEnv->NewByteArray(len)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, len, (jbyte*)value); + } else { + uint8_t value = 0; + jb.reset(sCallbackEnv->NewByteArray(1)); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 1, (jbyte*)&value); + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWriteDescriptor, conn_id, + status, handle, jb.get()); +} + +void btgattc_remote_rssi_cb(int client_if, const RawAddress& bda, int rssi, + int status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadRemoteRssi, + client_if, address.get(), rssi, status); +} + +void btgattc_configure_mtu_cb(int conn_id, int status, int mtu) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConfigureMTU, conn_id, + status, mtu); +} + +void btgattc_congestion_cb(int conn_id, bool congested) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) 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) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + ScopedLocalRef jb(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(data.size())); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(), + (jbyte*)data.data()); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBatchScanReports, status, + client_if, report_format, num_records, jb.get()); +} + +void btgattc_batchscan_threshold_cb(int client_if) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, + method_onBatchScanThresholdCrossed, client_if); +} + +void btgattc_track_adv_event_cb(btgatt_track_adv_info_t* p_adv_track_info) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address( + sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &p_adv_track_info->bd_addr)); + + ScopedLocalRef jb_adv_pkt( + sCallbackEnv.get(), + sCallbackEnv->NewByteArray(p_adv_track_info->adv_pkt_len)); + ScopedLocalRef jb_scan_rsp( + sCallbackEnv.get(), + sCallbackEnv->NewByteArray(p_adv_track_info->scan_rsp_len)); + + sCallbackEnv->SetByteArrayRegion(jb_adv_pkt.get(), 0, + p_adv_track_info->adv_pkt_len, + (jbyte*)p_adv_track_info->p_adv_pkt_data); + + sCallbackEnv->SetByteArrayRegion(jb_scan_rsp.get(), 0, + p_adv_track_info->scan_rsp_len, + (jbyte*)p_adv_track_info->p_scan_rsp_data); + + ScopedLocalRef trackadv_obj( + sCallbackEnv.get(), + sCallbackEnv->CallObjectMethod( + mCallbacksObj, method_createOnTrackAdvFoundLostObject, + p_adv_track_info->client_if, p_adv_track_info->adv_pkt_len, + jb_adv_pkt.get(), p_adv_track_info->scan_rsp_len, jb_scan_rsp.get(), + p_adv_track_info->filt_index, p_adv_track_info->advertiser_state, + p_adv_track_info->advertiser_info_present, address.get(), + p_adv_track_info->addr_type, p_adv_track_info->tx_power, + p_adv_track_info->rssi_value, p_adv_track_info->time_stamp)); + + if (NULL != trackadv_obj.get()) { + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onTrackAdvFoundLost, + trackadv_obj.get()); + } +} + +void fillGattDbElementArray(JNIEnv* env, jobject* array, + const btgatt_db_element_t* db, int count) { + // Because JNI uses a different class loader in the callback context, we + // cannot simply get the class. + // As a workaround, we have to make sure we obtain an object of the class + // first, as this will cause + // class loader to load it. + ScopedLocalRef objectForClass( + env, env->CallObjectMethod(mCallbacksObj, method_getSampleGattDbElement)); + ScopedLocalRef gattDbElementClazz( + env, env->GetObjectClass(objectForClass.get())); + + jmethodID gattDbElementConstructor = + env->GetMethodID(gattDbElementClazz.get(), "", "()V"); + + ScopedLocalRef arrayListclazz(env, + env->FindClass("java/util/ArrayList")); + jmethodID arrayAdd = + env->GetMethodID(arrayListclazz.get(), "add", "(Ljava/lang/Object;)Z"); + + ScopedLocalRef uuidClazz(env, env->FindClass("java/util/UUID")); + jmethodID uuidConstructor = + env->GetMethodID(uuidClazz.get(), "", "(JJ)V"); + + for (int i = 0; i < count; i++) { + const btgatt_db_element_t& curr = db[i]; + + ScopedLocalRef element( + env, + env->NewObject(gattDbElementClazz.get(), gattDbElementConstructor)); + + jfieldID fid = env->GetFieldID(gattDbElementClazz.get(), "id", "I"); + env->SetIntField(element.get(), fid, curr.id); + + fid = env->GetFieldID(gattDbElementClazz.get(), "attributeHandle", "I"); + env->SetIntField(element.get(), fid, curr.attribute_handle); + + ScopedLocalRef uuid( + env, env->NewObject(uuidClazz.get(), uuidConstructor, + uuid_msb(curr.uuid), uuid_lsb(curr.uuid))); + fid = env->GetFieldID(gattDbElementClazz.get(), "uuid", "Ljava/util/UUID;"); + env->SetObjectField(element.get(), fid, uuid.get()); + + fid = env->GetFieldID(gattDbElementClazz.get(), "type", "I"); + env->SetIntField(element.get(), fid, curr.type); + + fid = env->GetFieldID(gattDbElementClazz.get(), "attributeHandle", "I"); + env->SetIntField(element.get(), fid, curr.attribute_handle); + + fid = env->GetFieldID(gattDbElementClazz.get(), "startHandle", "I"); + env->SetIntField(element.get(), fid, curr.start_handle); + + fid = env->GetFieldID(gattDbElementClazz.get(), "endHandle", "I"); + env->SetIntField(element.get(), fid, curr.end_handle); + + fid = env->GetFieldID(gattDbElementClazz.get(), "properties", "I"); + env->SetIntField(element.get(), fid, curr.properties); + + env->CallBooleanMethod(*array, arrayAdd, element.get()); + } +} + +void btgattc_get_gatt_db_cb(int conn_id, const btgatt_db_element_t* db, + int count) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + jclass arrayListclazz = sCallbackEnv->FindClass("java/util/ArrayList"); + ScopedLocalRef array( + sCallbackEnv.get(), + sCallbackEnv->NewObject( + arrayListclazz, + sCallbackEnv->GetMethodID(arrayListclazz, "", "()V"))); + + jobject arrayPtr = array.get(); + fillGattDbElementArray(sCallbackEnv.get(), &arrayPtr, db, count); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetGattDb, conn_id, + array.get()); +} + +void btgattc_phy_updated_cb(int conn_id, uint8_t tx_phy, uint8_t rx_phy, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientPhyUpdate, conn_id, + tx_phy, rx_phy, status); +} + +void btgattc_conn_updated_cb(int conn_id, uint16_t interval, uint16_t latency, + uint16_t timeout, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientConnUpdate, + conn_id, interval, latency, timeout, status); +} + +void btgattc_service_changed_cb(int conn_id) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceChanged, conn_id); +} + +static const btgatt_scanner_callbacks_t sGattScannerCallbacks = { + btgattc_scan_result_cb, + btgattc_batchscan_reports_cb, + btgattc_batchscan_threshold_cb, + btgattc_track_adv_event_cb, +}; + +static const btgatt_client_callbacks_t sGattClientCallbacks = { + btgattc_register_app_cb, + btgattc_open_cb, + btgattc_close_cb, + btgattc_search_complete_cb, + btgattc_register_for_notification_cb, + btgattc_notify_cb, + btgattc_read_characteristic_cb, + btgattc_write_characteristic_cb, + btgattc_read_descriptor_cb, + btgattc_write_descriptor_cb, + btgattc_execute_write_cb, + btgattc_remote_rssi_cb, + btgattc_configure_mtu_cb, + btgattc_congestion_cb, + btgattc_get_gatt_db_cb, + NULL, /* services_removed_cb */ + NULL, /* services_added_cb */ + btgattc_phy_updated_cb, + btgattc_conn_updated_cb, + btgattc_service_changed_cb, +}; + +/** + * BTA server callbacks + */ + +void btgatts_register_app_cb(int status, int server_if, const Uuid& uuid) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) 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) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientConnected, + address.get(), connected, conn_id, server_if); +} + +void btgatts_service_added_cb(int status, int server_if, + const btgatt_db_element_t* service, + size_t service_count) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + jclass arrayListclazz = sCallbackEnv->FindClass("java/util/ArrayList"); + ScopedLocalRef array( + sCallbackEnv.get(), + sCallbackEnv->NewObject( + arrayListclazz, + sCallbackEnv->GetMethodID(arrayListclazz, "", "()V"))); + jobject arrayPtr = array.get(); + fillGattDbElementArray(sCallbackEnv.get(), &arrayPtr, service, service_count); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceAdded, status, + server_if, array.get()); +} + +void btgatts_service_stopped_cb(int status, int server_if, int srvc_handle) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceStopped, status, + server_if, srvc_handle); +} + +void btgatts_service_deleted_cb(int status, int server_if, int srvc_handle) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceDeleted, status, + server_if, srvc_handle); +} + +void btgatts_request_read_characteristic_cb(int conn_id, int trans_id, + const RawAddress& bda, + int attr_handle, int offset, + bool is_long) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerReadCharacteristic, + address.get(), conn_id, trans_id, attr_handle, + offset, is_long); +} + +void btgatts_request_read_descriptor_cb(int conn_id, int trans_id, + const RawAddress& bda, int attr_handle, + int offset, bool is_long) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerReadDescriptor, + address.get(), conn_id, trans_id, attr_handle, + offset, is_long); +} + +void btgatts_request_write_characteristic_cb(int conn_id, int trans_id, + const RawAddress& bda, + int attr_handle, int offset, + bool need_rsp, bool is_prep, + const uint8_t* value, + size_t length) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + ScopedLocalRef val(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(length)); + if (val.get()) + sCallbackEnv->SetByteArrayRegion(val.get(), 0, length, (jbyte*)value); + sCallbackEnv->CallVoidMethod( + mCallbacksObj, method_onServerWriteCharacteristic, address.get(), conn_id, + trans_id, attr_handle, offset, length, need_rsp, is_prep, val.get()); +} + +void btgatts_request_write_descriptor_cb(int conn_id, int trans_id, + const RawAddress& bda, int attr_handle, + int offset, bool need_rsp, + bool is_prep, const uint8_t* value, + size_t length) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + ScopedLocalRef val(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(length)); + if (val.get()) + sCallbackEnv->SetByteArrayRegion(val.get(), 0, length, (jbyte*)value); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerWriteDescriptor, + address.get(), conn_id, trans_id, attr_handle, + offset, length, need_rsp, is_prep, val.get()); +} + +void btgatts_request_exec_write_cb(int conn_id, int trans_id, + const RawAddress& bda, int exec_write) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExecuteWrite, + address.get(), conn_id, trans_id, exec_write); +} + +void btgatts_response_confirmation_cb(int status, int handle) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onResponseSendCompleted, + status, handle); +} + +void btgatts_indication_sent_cb(int conn_id, int status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNotificationSent, + conn_id, status); +} + +void btgatts_congestion_cb(int conn_id, bool congested) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerCongestion, + conn_id, congested); +} + +void btgatts_mtu_changed_cb(int conn_id, int mtu) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) 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) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerPhyUpdate, conn_id, + tx_phy, rx_phy, status); +} + +void btgatts_conn_updated_cb(int conn_id, uint16_t interval, uint16_t latency, + uint16_t timeout, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerConnUpdate, + conn_id, interval, latency, timeout, status); +} + +static const btgatt_server_callbacks_t sGattServerCallbacks = { + btgatts_register_app_cb, + btgatts_connection_cb, + btgatts_service_added_cb, + btgatts_service_stopped_cb, + btgatts_service_deleted_cb, + btgatts_request_read_characteristic_cb, + btgatts_request_read_descriptor_cb, + btgatts_request_write_characteristic_cb, + btgatts_request_write_descriptor_cb, + btgatts_request_exec_write_cb, + btgatts_response_confirmation_cb, + btgatts_indication_sent_cb, + btgatts_congestion_cb, + btgatts_mtu_changed_cb, + btgatts_phy_updated_cb, + btgatts_conn_updated_cb}; + +/** + * GATT callbacks + */ + +static const btgatt_callbacks_t sGattCallbacks = { + sizeof(btgatt_callbacks_t), &sGattClientCallbacks, &sGattServerCallbacks, + &sGattScannerCallbacks, +}; + +class JniAdvertisingCallbacks : AdvertisingCallbacks { + public: + static AdvertisingCallbacks* GetInstance() { + static AdvertisingCallbacks* instance = new JniAdvertisingCallbacks(); + return instance; + } + + void OnAdvertisingSetStarted(int reg_id, uint8_t advertiser_id, + int8_t tx_power, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingSetStarted, reg_id, + advertiser_id, tx_power, status); + } + + void OnAdvertisingEnabled(uint8_t advertiser_id, bool enable, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingEnabled, advertiser_id, + enable, status); + } + + void OnAdvertisingDataSet(uint8_t advertiser_id, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingDataSet, advertiser_id, + status); + } + + void OnScanResponseDataSet(uint8_t advertiser_id, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onScanResponseDataSet, advertiser_id, + status); + } + + void OnAdvertisingParametersUpdated(uint8_t advertiser_id, int8_t tx_power, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingParametersUpdated, + advertiser_id, tx_power, status); + } + + void OnPeriodicAdvertisingParametersUpdated(uint8_t advertiser_id, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onPeriodicAdvertisingParametersUpdated, + advertiser_id, status); + } + + void OnPeriodicAdvertisingDataSet(uint8_t advertiser_id, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onPeriodicAdvertisingDataSet, + advertiser_id, status); + } + + void OnPeriodicAdvertisingEnabled(uint8_t advertiser_id, bool enable, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onPeriodicAdvertisingEnabled, + advertiser_id, enable, status); + } + + void OnOwnAddressRead(uint8_t advertiser_id, uint8_t address_type, + RawAddress address) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &address)); + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onOwnAddressRead, advertiser_id, + address_type, addr.get()); + } +}; + +class JniScanningCallbacks : ScanningCallbacks { + public: + static ScanningCallbacks* GetInstance() { + static ScanningCallbacks* instance = new JniScanningCallbacks(); + return instance; + } + + void OnScannerRegistered(const Uuid app_uuid, uint8_t scannerId, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScannerRegistered, + status, scannerId, UUID_PARAMS(app_uuid)); + } + + void OnSetScannerParameterComplete(uint8_t scannerId, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod( + mCallbacksObj, method_onScanParamSetupCompleted, status, scannerId); + } + + void OnScanResult(uint16_t event_type, uint8_t addr_type, RawAddress bda, + 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) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + ScopedLocalRef jb(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(adv_data.size())); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(), + (jbyte*)adv_data.data()); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type, + addr_type, address.get(), primary_phy, + secondary_phy, advertising_sid, tx_power, rssi, + periodic_adv_int, jb.get()); + } + + void OnTrackAdvFoundLost(AdvertisingTrackInfo track_info) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address( + sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &track_info.advertiser_address)); + + ScopedLocalRef jb_adv_pkt( + sCallbackEnv.get(), + sCallbackEnv->NewByteArray(track_info.adv_packet_len)); + ScopedLocalRef jb_scan_rsp( + sCallbackEnv.get(), + sCallbackEnv->NewByteArray(track_info.scan_response_len)); + + sCallbackEnv->SetByteArrayRegion(jb_adv_pkt.get(), 0, + track_info.adv_packet_len, + (jbyte*)track_info.adv_packet.data()); + + sCallbackEnv->SetByteArrayRegion(jb_scan_rsp.get(), 0, + track_info.scan_response_len, + (jbyte*)track_info.scan_response.data()); + + ScopedLocalRef trackadv_obj( + sCallbackEnv.get(), + sCallbackEnv->CallObjectMethod( + mCallbacksObj, method_createOnTrackAdvFoundLostObject, + track_info.scanner_id, track_info.adv_packet_len, jb_adv_pkt.get(), + track_info.scan_response_len, jb_scan_rsp.get(), + track_info.filter_index, track_info.advertiser_state, + track_info.advertiser_info_present, address.get(), + track_info.advertiser_address_type, track_info.tx_power, + track_info.rssi, track_info.time_stamp)); + + if (NULL != trackadv_obj.get()) { + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onTrackAdvFoundLost, + trackadv_obj.get()); + } + } + + void OnBatchScanReports(int client_if, int status, int report_format, + int num_records, std::vector data) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + ScopedLocalRef jb(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(data.size())); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(), + (jbyte*)data.data()); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBatchScanReports, + status, client_if, report_format, num_records, + jb.get()); + } + + void OnBatchScanThresholdCrossed(int client_if) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, + method_onBatchScanThresholdCrossed, client_if); + } +}; + +/** + * Native function definitions + */ +static void classInitNative(JNIEnv* env, jclass clazz) { + // Client callbacks + + method_onClientRegistered = + env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V"); + method_onScannerRegistered = + env->GetMethodID(clazz, "onScannerRegistered", "(IIJJ)V"); + method_onScanResult = env->GetMethodID(clazz, "onScanResult", + "(IILjava/lang/String;IIIIII[B)V"); + method_onConnected = + env->GetMethodID(clazz, "onConnected", "(IIILjava/lang/String;)V"); + method_onDisconnected = + env->GetMethodID(clazz, "onDisconnected", "(IIILjava/lang/String;)V"); + method_onReadCharacteristic = + env->GetMethodID(clazz, "onReadCharacteristic", "(III[B)V"); + method_onWriteCharacteristic = + env->GetMethodID(clazz, "onWriteCharacteristic", "(III[B)V"); + method_onExecuteCompleted = + env->GetMethodID(clazz, "onExecuteCompleted", "(II)V"); + method_onSearchCompleted = + env->GetMethodID(clazz, "onSearchCompleted", "(II)V"); + method_onReadDescriptor = + env->GetMethodID(clazz, "onReadDescriptor", "(III[B)V"); + method_onWriteDescriptor = + env->GetMethodID(clazz, "onWriteDescriptor", "(III[B)V"); + method_onNotify = + env->GetMethodID(clazz, "onNotify", "(ILjava/lang/String;IZ[B)V"); + method_onRegisterForNotifications = + env->GetMethodID(clazz, "onRegisterForNotifications", "(IIII)V"); + method_onReadRemoteRssi = + env->GetMethodID(clazz, "onReadRemoteRssi", "(ILjava/lang/String;II)V"); + method_onConfigureMTU = env->GetMethodID(clazz, "onConfigureMTU", "(III)V"); + method_onScanFilterConfig = + env->GetMethodID(clazz, "onScanFilterConfig", "(IIIII)V"); + method_onScanFilterParamsConfigured = + env->GetMethodID(clazz, "onScanFilterParamsConfigured", "(IIII)V"); + method_onScanFilterEnableDisabled = + env->GetMethodID(clazz, "onScanFilterEnableDisabled", "(III)V"); + method_onClientCongestion = + env->GetMethodID(clazz, "onClientCongestion", "(IZ)V"); + method_onBatchScanStorageConfigured = + env->GetMethodID(clazz, "onBatchScanStorageConfigured", "(II)V"); + method_onBatchScanStartStopped = + env->GetMethodID(clazz, "onBatchScanStartStopped", "(III)V"); + method_onBatchScanReports = + env->GetMethodID(clazz, "onBatchScanReports", "(IIII[B)V"); + method_onBatchScanThresholdCrossed = + env->GetMethodID(clazz, "onBatchScanThresholdCrossed", "(I)V"); + method_createOnTrackAdvFoundLostObject = + env->GetMethodID(clazz, "createOnTrackAdvFoundLostObject", + "(II[BI[BIIILjava/lang/String;IIII)Lcom/android/" + "bluetooth/gatt/AdvtFilterOnFoundOnLostInfo;"); + method_onTrackAdvFoundLost = env->GetMethodID( + clazz, "onTrackAdvFoundLost", + "(Lcom/android/bluetooth/gatt/AdvtFilterOnFoundOnLostInfo;)V"); + method_onScanParamSetupCompleted = + env->GetMethodID(clazz, "onScanParamSetupCompleted", "(II)V"); + method_getSampleGattDbElement = + env->GetMethodID(clazz, "getSampleGattDbElement", + "()Lcom/android/bluetooth/gatt/GattDbElement;"); + method_onGetGattDb = + env->GetMethodID(clazz, "onGetGattDb", "(ILjava/util/ArrayList;)V"); + method_onClientPhyRead = + env->GetMethodID(clazz, "onClientPhyRead", "(ILjava/lang/String;III)V"); + method_onClientPhyUpdate = + env->GetMethodID(clazz, "onClientPhyUpdate", "(IIII)V"); + method_onClientConnUpdate = + env->GetMethodID(clazz, "onClientConnUpdate", "(IIIII)V"); + method_onServiceChanged = + env->GetMethodID(clazz, "onServiceChanged", "(I)V"); + + // Server callbacks + + method_onServerRegistered = + env->GetMethodID(clazz, "onServerRegistered", "(IIJJ)V"); + method_onClientConnected = + env->GetMethodID(clazz, "onClientConnected", "(Ljava/lang/String;ZII)V"); + method_onServiceAdded = + env->GetMethodID(clazz, "onServiceAdded", "(IILjava/util/List;)V"); + method_onServiceStopped = + env->GetMethodID(clazz, "onServiceStopped", "(III)V"); + method_onServiceDeleted = + env->GetMethodID(clazz, "onServiceDeleted", "(III)V"); + method_onResponseSendCompleted = + env->GetMethodID(clazz, "onResponseSendCompleted", "(II)V"); + method_onServerReadCharacteristic = env->GetMethodID( + clazz, "onServerReadCharacteristic", "(Ljava/lang/String;IIIIZ)V"); + method_onServerReadDescriptor = env->GetMethodID( + clazz, "onServerReadDescriptor", "(Ljava/lang/String;IIIIZ)V"); + method_onServerWriteCharacteristic = env->GetMethodID( + clazz, "onServerWriteCharacteristic", "(Ljava/lang/String;IIIIIZZ[B)V"); + method_onServerWriteDescriptor = env->GetMethodID( + clazz, "onServerWriteDescriptor", "(Ljava/lang/String;IIIIIZZ[B)V"); + method_onExecuteWrite = + env->GetMethodID(clazz, "onExecuteWrite", "(Ljava/lang/String;III)V"); + method_onNotificationSent = + env->GetMethodID(clazz, "onNotificationSent", "(II)V"); + method_onServerCongestion = + env->GetMethodID(clazz, "onServerCongestion", "(IZ)V"); + method_onServerMtuChanged = env->GetMethodID(clazz, "onMtuChanged", "(II)V"); + method_onServerPhyRead = + env->GetMethodID(clazz, "onServerPhyRead", "(ILjava/lang/String;III)V"); + method_onServerPhyUpdate = + env->GetMethodID(clazz, "onServerPhyUpdate", "(IIII)V"); + method_onServerConnUpdate = + env->GetMethodID(clazz, "onServerConnUpdate", "(IIIII)V"); + + info("classInitNative: Success!"); +} + +static const bt_interface_t* btIf; + +static void initializeNative(JNIEnv* env, jobject object) { + if (btIf) return; + + btIf = getBluetoothInterface(); + if (btIf == NULL) { + error("Bluetooth module is not loaded"); + return; + } + + if (sGattIf != NULL) { + ALOGW("Cleaning up Bluetooth GATT Interface before initializing..."); + sGattIf->cleanup(); + sGattIf = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth GATT callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + sGattIf = + (btgatt_interface_t*)btIf->get_profile_interface(BT_PROFILE_GATT_ID); + if (sGattIf == NULL) { + error("Failed to get Bluetooth GATT Interface"); + return; + } + + bt_status_t status = sGattIf->init(&sGattCallbacks); + if (status != BT_STATUS_SUCCESS) { + error("Failed to initialize Bluetooth GATT, status: %d", status); + sGattIf = NULL; + return; + } + + sGattIf->advertiser->RegisterCallbacks( + JniAdvertisingCallbacks::GetInstance()); + sGattIf->scanner->RegisterCallbacks(JniScanningCallbacks::GetInstance()); + + mCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + if (!btIf) return; + + if (sGattIf != NULL) { + sGattIf->cleanup(); + sGattIf = NULL; + } + + if (mCallbacksObj != NULL) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + btIf = NULL; +} + +/** + * Native Client functions + */ + +static int gattClientGetDeviceTypeNative(JNIEnv* env, jobject object, + jstring address) { + if (!sGattIf) return 0; + return sGattIf->client->get_device_type(str2addr(env, address)); +} + +static void gattClientRegisterAppNative(JNIEnv* env, jobject object, + jlong app_uuid_lsb, jlong app_uuid_msb, + jboolean eatt_support) { + if (!sGattIf) return; + Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb); + sGattIf->client->register_client(uuid, eatt_support); +} + +static void gattClientUnregisterAppNative(JNIEnv* env, jobject object, + jint clientIf) { + if (!sGattIf) return; + sGattIf->client->unregister_client(clientIf); +} + +void btgattc_register_scanner_cb(const Uuid& app_uuid, uint8_t scannerId, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScannerRegistered, + status, scannerId, UUID_PARAMS(app_uuid)); +} + +static void registerScannerNative(JNIEnv* env, jobject object, + jlong app_uuid_lsb, jlong app_uuid_msb) { + if (!sGattIf) return; + + Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb); + sGattIf->scanner->RegisterScanner( + uuid, base::Bind(&btgattc_register_scanner_cb, uuid)); +} + +static void unregisterScannerNative(JNIEnv* env, jobject object, + jint scanner_id) { + if (!sGattIf) return; + + sGattIf->scanner->Unregister(scanner_id); +} + +static void gattClientScanNative(JNIEnv* env, jobject object, jboolean start) { + if (!sGattIf) return; + sGattIf->scanner->Scan(start); +} + +static void gattClientConnectNative(JNIEnv* env, jobject object, jint clientif, + jstring address, jboolean isDirect, + jint transport, jboolean opportunistic, + jint initiating_phys) { + if (!sGattIf) return; + + sGattIf->client->connect(clientif, str2addr(env, address), isDirect, + transport, opportunistic, initiating_phys); +} + +static void gattClientDisconnectNative(JNIEnv* env, jobject object, + jint clientIf, jstring address, + jint conn_id) { + if (!sGattIf) return; + sGattIf->client->disconnect(clientIf, str2addr(env, address), conn_id); +} + +static void gattClientSetPreferredPhyNative(JNIEnv* env, jobject object, + jint clientIf, jstring address, + jint tx_phy, jint rx_phy, + jint phy_options) { + if (!sGattIf) return; + sGattIf->client->set_preferred_phy(str2addr(env, address), tx_phy, rx_phy, + phy_options); +} + +static void readClientPhyCb(uint8_t clientIf, RawAddress bda, uint8_t tx_phy, + uint8_t rx_phy, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientPhyRead, clientIf, + address.get(), tx_phy, rx_phy, status); +} + +static void gattClientReadPhyNative(JNIEnv* env, jobject object, jint clientIf, + jstring address) { + if (!sGattIf) return; + + RawAddress bda = str2addr(env, address); + sGattIf->client->read_phy(bda, base::Bind(&readClientPhyCb, clientIf, bda)); +} + +static void gattClientRefreshNative(JNIEnv* env, jobject object, jint clientIf, + jstring address) { + if (!sGattIf) return; + + sGattIf->client->refresh(clientIf, str2addr(env, address)); +} + +static void gattClientSearchServiceNative(JNIEnv* env, jobject object, + jint conn_id, jboolean search_all, + jlong service_uuid_lsb, + jlong service_uuid_msb) { + if (!sGattIf) return; + + Uuid uuid = from_java_uuid(service_uuid_msb, service_uuid_lsb); + sGattIf->client->search_service(conn_id, search_all ? 0 : &uuid); +} + +static void gattClientDiscoverServiceByUuidNative(JNIEnv* env, jobject object, + jint conn_id, + jlong service_uuid_lsb, + jlong service_uuid_msb) { + if (!sGattIf) return; + + Uuid uuid = from_java_uuid(service_uuid_msb, service_uuid_lsb); + sGattIf->client->btif_gattc_discover_service_by_uuid(conn_id, uuid); +} + +static void gattClientGetGattDbNative(JNIEnv* env, jobject object, + jint conn_id) { + if (!sGattIf) return; + + sGattIf->client->get_gatt_db(conn_id); +} + +static void gattClientReadCharacteristicNative(JNIEnv* env, jobject object, + jint conn_id, jint handle, + jint authReq) { + if (!sGattIf) return; + + sGattIf->client->read_characteristic(conn_id, handle, authReq); +} + +static void gattClientReadUsingCharacteristicUuidNative( + JNIEnv* env, jobject object, jint conn_id, jlong uuid_lsb, jlong uuid_msb, + jint s_handle, jint e_handle, jint authReq) { + if (!sGattIf) return; + + Uuid uuid = from_java_uuid(uuid_msb, uuid_lsb); + sGattIf->client->read_using_characteristic_uuid(conn_id, uuid, s_handle, + e_handle, authReq); +} + +static void gattClientReadDescriptorNative(JNIEnv* env, jobject object, + jint conn_id, jint handle, + jint authReq) { + if (!sGattIf) return; + + sGattIf->client->read_descriptor(conn_id, handle, authReq); +} + +static void gattClientWriteCharacteristicNative(JNIEnv* env, jobject object, + jint conn_id, jint handle, + jint write_type, jint auth_req, + jbyteArray value) { + if (!sGattIf) return; + + if (value == NULL) { + warn("gattClientWriteCharacteristicNative() ignoring NULL array"); + return; + } + + uint16_t len = (uint16_t)env->GetArrayLength(value); + jbyte* p_value = env->GetByteArrayElements(value, NULL); + if (p_value == NULL) return; + + sGattIf->client->write_characteristic(conn_id, handle, write_type, auth_req, + reinterpret_cast(p_value), + len); + + env->ReleaseByteArrayElements(value, p_value, 0); +} + +static void gattClientExecuteWriteNative(JNIEnv* env, jobject object, + jint conn_id, jboolean execute) { + if (!sGattIf) return; + sGattIf->client->execute_write(conn_id, execute ? 1 : 0); +} + +static void gattClientWriteDescriptorNative(JNIEnv* env, jobject object, + jint conn_id, jint handle, + jint auth_req, jbyteArray value) { + if (!sGattIf) return; + + if (value == NULL) { + warn("gattClientWriteDescriptorNative() ignoring NULL array"); + return; + } + + uint16_t len = (uint16_t)env->GetArrayLength(value); + jbyte* p_value = env->GetByteArrayElements(value, NULL); + if (p_value == NULL) return; + + sGattIf->client->write_descriptor(conn_id, handle, auth_req, + reinterpret_cast(p_value), len); + + env->ReleaseByteArrayElements(value, p_value, 0); +} + +static void gattClientRegisterForNotificationsNative( + JNIEnv* env, jobject object, jint clientIf, jstring address, jint handle, + jboolean enable) { + if (!sGattIf) return; + + RawAddress bd_addr = str2addr(env, address); + if (enable) + sGattIf->client->register_for_notification(clientIf, bd_addr, handle); + else + sGattIf->client->deregister_for_notification(clientIf, bd_addr, handle); +} + +static void gattClientReadRemoteRssiNative(JNIEnv* env, jobject object, + jint clientif, jstring address) { + if (!sGattIf) return; + + sGattIf->client->read_remote_rssi(clientif, str2addr(env, address)); +} + +void set_scan_params_cmpl_cb(int client_if, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanParamSetupCompleted, + status, client_if); +} + +static void gattSetScanParametersNative(JNIEnv* env, jobject object, + jint client_if, jint scan_interval_unit, + jint scan_window_unit) { + if (!sGattIf) return; + sGattIf->scanner->SetScanParameters( + client_if, scan_interval_unit, scan_window_unit, + base::Bind(&set_scan_params_cmpl_cb, client_if)); +} + +void scan_filter_param_cb(uint8_t client_if, uint8_t avbl_space, uint8_t action, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, + method_onScanFilterParamsConfigured, action, + status, client_if, avbl_space); +} + +static void gattClientScanFilterParamAddNative(JNIEnv* env, jobject object, + jobject params) { + if (!sGattIf) return; + const int add_scan_filter_params_action = 0; + auto filt_params = std::make_unique(); + + jmethodID methodId = 0; + ScopedLocalRef filtparam(env, env->GetObjectClass(params)); + + methodId = env->GetMethodID(filtparam.get(), "getClientIf", "()I"); + uint8_t client_if = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getFiltIndex", "()I"); + uint8_t filt_index = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getFeatSeln", "()I"); + filt_params->feat_seln = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getListLogicType", "()I"); + filt_params->list_logic_type = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getFiltLogicType", "()I"); + filt_params->filt_logic_type = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getDelyMode", "()I"); + filt_params->dely_mode = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getFoundTimeout", "()I"); + filt_params->found_timeout = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getLostTimeout", "()I"); + filt_params->lost_timeout = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getFoundTimeOutCnt", "()I"); + filt_params->found_timeout_cnt = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getNumOfTrackEntries", "()I"); + filt_params->num_of_tracking_entries = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getRSSIHighValue", "()I"); + filt_params->rssi_high_thres = env->CallIntMethod(params, methodId); + + methodId = env->GetMethodID(filtparam.get(), "getRSSILowValue", "()I"); + filt_params->rssi_low_thres = env->CallIntMethod(params, methodId); + + sGattIf->scanner->ScanFilterParamSetup( + client_if, add_scan_filter_params_action, filt_index, + std::move(filt_params), base::Bind(&scan_filter_param_cb, client_if)); +} + +static void gattClientScanFilterParamDeleteNative(JNIEnv* env, jobject object, + jint client_if, + jint filt_index) { + if (!sGattIf) return; + const int delete_scan_filter_params_action = 1; + sGattIf->scanner->ScanFilterParamSetup( + client_if, delete_scan_filter_params_action, filt_index, nullptr, + base::Bind(&scan_filter_param_cb, client_if)); +} + +static void gattClientScanFilterParamClearAllNative(JNIEnv* env, jobject object, + jint client_if) { + if (!sGattIf) return; + const int clear_scan_filter_params_action = 2; + sGattIf->scanner->ScanFilterParamSetup( + client_if, clear_scan_filter_params_action, 0 /* index, unused */, + nullptr, base::Bind(&scan_filter_param_cb, client_if)); +} + +static void scan_filter_cfg_cb(uint8_t client_if, uint8_t filt_type, + uint8_t avbl_space, uint8_t action, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanFilterConfig, action, + status, client_if, filt_type, avbl_space); +} + +static void gattClientScanFilterAddNative(JNIEnv* env, jobject object, + jint client_if, jobjectArray filters, + jint filter_index) { + if (!sGattIf) return; + + jclass uuidClazz = env->FindClass("java/util/UUID"); + jmethodID uuidGetMsb = + env->GetMethodID(uuidClazz, "getMostSignificantBits", "()J"); + jmethodID uuidGetLsb = + env->GetMethodID(uuidClazz, "getLeastSignificantBits", "()J"); + + std::vector native_filters; + + int numFilters = env->GetArrayLength(filters); + if (numFilters == 0) { + sGattIf->scanner->ScanFilterAdd(filter_index, std::move(native_filters), + base::Bind(&scan_filter_cfg_cb, client_if)); + return; + } + + jclass entryClazz = + env->GetObjectClass(env->GetObjectArrayElement(filters, 0)); + + jfieldID typeFid = env->GetFieldID(entryClazz, "type", "B"); + jfieldID addressFid = + env->GetFieldID(entryClazz, "address", "Ljava/lang/String;"); + jfieldID addrTypeFid = env->GetFieldID(entryClazz, "addr_type", "B"); + jfieldID irkTypeFid = env->GetFieldID(entryClazz, "irk", "[B"); + jfieldID uuidFid = env->GetFieldID(entryClazz, "uuid", "Ljava/util/UUID;"); + jfieldID uuidMaskFid = + env->GetFieldID(entryClazz, "uuid_mask", "Ljava/util/UUID;"); + jfieldID nameFid = env->GetFieldID(entryClazz, "name", "Ljava/lang/String;"); + jfieldID companyFid = env->GetFieldID(entryClazz, "company", "I"); + jfieldID companyMaskFid = env->GetFieldID(entryClazz, "company_mask", "I"); + jfieldID dataFid = env->GetFieldID(entryClazz, "data", "[B"); + jfieldID dataMaskFid = env->GetFieldID(entryClazz, "data_mask", "[B"); + + for (int i = 0; i < numFilters; ++i) { + ApcfCommand curr{}; + + ScopedLocalRef current(env, + env->GetObjectArrayElement(filters, i)); + + curr.type = env->GetByteField(current.get(), typeFid); + + ScopedLocalRef address( + env, (jstring)env->GetObjectField(current.get(), addressFid)); + if (address.get() != NULL) { + curr.address = str2addr(env, address.get()); + } + + curr.addr_type = env->GetByteField(current.get(), addrTypeFid); + + ScopedLocalRef irkByteArray( + env, (jbyteArray)env->GetObjectField(current.get(), irkTypeFid)); + + if (irkByteArray.get() != nullptr) { + int len = env->GetArrayLength(irkByteArray.get()); + // IRK is 128 bits or 16 octets, set the bytes or zero it out + if (len != 16) { + ALOGE("%s: Invalid IRK length '%d'; expected 16", __func__, len); + jniThrowIOException(env, EINVAL); + return; + } + jbyte* irkBytes = env->GetByteArrayElements(irkByteArray.get(), NULL); + if (irkBytes == NULL) { + jniThrowIOException(env, EINVAL); + return; + } + for (int j = 0; j < len; j++) { + curr.irk[j] = irkBytes[j]; + } + } + + ScopedLocalRef uuid(env, + env->GetObjectField(current.get(), uuidFid)); + if (uuid.get() != NULL) { + jlong uuid_msb = env->CallLongMethod(uuid.get(), uuidGetMsb); + jlong uuid_lsb = env->CallLongMethod(uuid.get(), uuidGetLsb); + curr.uuid = from_java_uuid(uuid_msb, uuid_lsb); + } + + ScopedLocalRef uuid_mask( + env, env->GetObjectField(current.get(), uuidMaskFid)); + if (uuid.get() != NULL) { + jlong uuid_msb = env->CallLongMethod(uuid_mask.get(), uuidGetMsb); + jlong uuid_lsb = env->CallLongMethod(uuid_mask.get(), uuidGetLsb); + curr.uuid_mask = from_java_uuid(uuid_msb, uuid_lsb); + } + + ScopedLocalRef name( + env, (jstring)env->GetObjectField(current.get(), nameFid)); + if (name.get() != NULL) { + const char* c_name = env->GetStringUTFChars(name.get(), NULL); + if (c_name != NULL && strlen(c_name) != 0) { + curr.name = std::vector(c_name, c_name + strlen(c_name)); + env->ReleaseStringUTFChars(name.get(), c_name); + } + } + + curr.company = env->GetIntField(current.get(), companyFid); + + curr.company_mask = env->GetIntField(current.get(), companyMaskFid); + + ScopedLocalRef data( + env, (jbyteArray)env->GetObjectField(current.get(), dataFid)); + if (data.get() != NULL) { + jbyte* data_array = env->GetByteArrayElements(data.get(), 0); + int data_len = env->GetArrayLength(data.get()); + if (data_array && data_len) { + curr.data = std::vector(data_array, data_array + data_len); + env->ReleaseByteArrayElements(data.get(), data_array, JNI_ABORT); + } + } + + ScopedLocalRef data_mask( + env, (jbyteArray)env->GetObjectField(current.get(), dataMaskFid)); + if (data_mask.get() != NULL) { + jbyte* data_array = env->GetByteArrayElements(data_mask.get(), 0); + int data_len = env->GetArrayLength(data_mask.get()); + if (data_array && data_len) { + curr.data_mask = + std::vector(data_array, data_array + data_len); + env->ReleaseByteArrayElements(data_mask.get(), data_array, JNI_ABORT); + } + } + native_filters.push_back(curr); + } + + sGattIf->scanner->ScanFilterAdd(filter_index, std::move(native_filters), + base::Bind(&scan_filter_cfg_cb, client_if)); +} + +static void gattClientScanFilterClearNative(JNIEnv* env, jobject object, + jint client_if, jint filt_index) { + if (!sGattIf) return; + sGattIf->scanner->ScanFilterClear(filt_index, + base::Bind(&scan_filter_cfg_cb, client_if)); +} + +void scan_enable_cb(uint8_t client_if, uint8_t action, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanFilterEnableDisabled, + action, status, client_if); +} + +static void gattClientScanFilterEnableNative(JNIEnv* env, jobject object, + jint client_if, jboolean enable) { + if (!sGattIf) return; + sGattIf->scanner->ScanFilterEnable(enable, + base::Bind(&scan_enable_cb, client_if)); +} + +static void gattClientConfigureMTUNative(JNIEnv* env, jobject object, + jint conn_id, jint mtu) { + if (!sGattIf) return; + sGattIf->client->configure_mtu(conn_id, mtu); +} + +static void gattConnectionParameterUpdateNative(JNIEnv* env, jobject object, + jint client_if, jstring address, + jint min_interval, + jint max_interval, jint latency, + jint timeout, jint min_ce_len, + jint max_ce_len) { + if (!sGattIf) return; + sGattIf->client->conn_parameter_update( + str2addr(env, address), min_interval, max_interval, latency, timeout, + (uint16_t)min_ce_len, (uint16_t)max_ce_len); +} + +void batchscan_cfg_storage_cb(uint8_t client_if, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod( + mCallbacksObj, method_onBatchScanStorageConfigured, status, client_if); +} + +static void gattClientConfigBatchScanStorageNative( + JNIEnv* env, jobject object, jint client_if, jint max_full_reports_percent, + jint max_trunc_reports_percent, jint notify_threshold_level_percent) { + if (!sGattIf) return; + sGattIf->scanner->BatchscanConfigStorage( + client_if, max_full_reports_percent, max_trunc_reports_percent, + notify_threshold_level_percent, + base::Bind(&batchscan_cfg_storage_cb, client_if)); +} + +void batchscan_enable_cb(uint8_t client_if, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBatchScanStartStopped, + 0 /* unused */, status, client_if); +} + +static void gattClientStartBatchScanNative(JNIEnv* env, jobject object, + jint client_if, jint scan_mode, + jint scan_interval_unit, + jint scan_window_unit, + jint addr_type, jint discard_rule) { + if (!sGattIf) return; + sGattIf->scanner->BatchscanEnable( + scan_mode, scan_interval_unit, scan_window_unit, addr_type, discard_rule, + base::Bind(&batchscan_enable_cb, client_if)); +} + +static void gattClientStopBatchScanNative(JNIEnv* env, jobject object, + jint client_if) { + if (!sGattIf) return; + sGattIf->scanner->BatchscanDisable( + base::Bind(&batchscan_enable_cb, client_if)); +} + +static void gattClientReadScanReportsNative(JNIEnv* env, jobject object, + jint client_if, jint scan_type) { + if (!sGattIf) return; + sGattIf->scanner->BatchscanReadReports(client_if, scan_type); +} + +/** + * Native server functions + */ +static void gattServerRegisterAppNative(JNIEnv* env, jobject object, + jlong app_uuid_lsb, jlong app_uuid_msb, + jboolean eatt_support) { + if (!sGattIf) return; + Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb); + sGattIf->server->register_server(uuid, eatt_support); +} + +static void gattServerUnregisterAppNative(JNIEnv* env, jobject object, + jint serverIf) { + if (!sGattIf) return; + sGattIf->server->unregister_server(serverIf); +} + +static void gattServerConnectNative(JNIEnv* env, jobject object, jint server_if, + jstring address, jboolean is_direct, + jint transport) { + if (!sGattIf) return; + + RawAddress bd_addr = str2addr(env, address); + sGattIf->server->connect(server_if, bd_addr, is_direct, transport); +} + +static void gattServerDisconnectNative(JNIEnv* env, jobject object, + jint serverIf, jstring address, + jint conn_id) { + if (!sGattIf) return; + sGattIf->server->disconnect(serverIf, str2addr(env, address), conn_id); +} + +static void gattServerSetPreferredPhyNative(JNIEnv* env, jobject object, + jint serverIf, jstring address, + jint tx_phy, jint rx_phy, + jint phy_options) { + if (!sGattIf) return; + RawAddress bda = str2addr(env, address); + sGattIf->server->set_preferred_phy(bda, tx_phy, rx_phy, phy_options); +} + +static void readServerPhyCb(uint8_t serverIf, RawAddress bda, uint8_t tx_phy, + uint8_t rx_phy, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef address(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &bda)); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerPhyRead, serverIf, + address.get(), tx_phy, rx_phy, status); +} + +static void gattServerReadPhyNative(JNIEnv* env, jobject object, jint serverIf, + jstring address) { + if (!sGattIf) return; + + RawAddress bda = str2addr(env, address); + sGattIf->server->read_phy(bda, base::Bind(&readServerPhyCb, serverIf, bda)); +} + +static void gattServerAddServiceNative(JNIEnv* env, jobject object, + jint server_if, + jobject gatt_db_elements) { + if (!sGattIf) return; + + jclass arrayListclazz = env->FindClass("java/util/List"); + jmethodID arrayGet = + env->GetMethodID(arrayListclazz, "get", "(I)Ljava/lang/Object;"); + jmethodID arraySize = env->GetMethodID(arrayListclazz, "size", "()I"); + + int count = env->CallIntMethod(gatt_db_elements, arraySize); + std::vector db; + + jclass uuidClazz = env->FindClass("java/util/UUID"); + jmethodID uuidGetMsb = + env->GetMethodID(uuidClazz, "getMostSignificantBits", "()J"); + jmethodID uuidGetLsb = + env->GetMethodID(uuidClazz, "getLeastSignificantBits", "()J"); + + jobject objectForClass = + env->CallObjectMethod(mCallbacksObj, method_getSampleGattDbElement); + jclass gattDbElementClazz = env->GetObjectClass(objectForClass); + + for (int i = 0; i < count; i++) { + btgatt_db_element_t curr; + + jint index = i; + ScopedLocalRef element( + env, env->CallObjectMethod(gatt_db_elements, arrayGet, index)); + + jfieldID fid; + + fid = env->GetFieldID(gattDbElementClazz, "id", "I"); + curr.id = env->GetIntField(element.get(), fid); + + fid = env->GetFieldID(gattDbElementClazz, "uuid", "Ljava/util/UUID;"); + ScopedLocalRef uuid(env, env->GetObjectField(element.get(), fid)); + if (uuid.get() != NULL) { + jlong uuid_msb = env->CallLongMethod(uuid.get(), uuidGetMsb); + jlong uuid_lsb = env->CallLongMethod(uuid.get(), uuidGetLsb); + curr.uuid = from_java_uuid(uuid_msb, uuid_lsb); + } + + fid = env->GetFieldID(gattDbElementClazz, "type", "I"); + curr.type = + (bt_gatt_db_attribute_type_t)env->GetIntField(element.get(), fid); + + fid = env->GetFieldID(gattDbElementClazz, "attributeHandle", "I"); + curr.attribute_handle = env->GetIntField(element.get(), fid); + + fid = env->GetFieldID(gattDbElementClazz, "startHandle", "I"); + curr.start_handle = env->GetIntField(element.get(), fid); + + fid = env->GetFieldID(gattDbElementClazz, "endHandle", "I"); + curr.end_handle = env->GetIntField(element.get(), fid); + + fid = env->GetFieldID(gattDbElementClazz, "properties", "I"); + curr.properties = env->GetIntField(element.get(), fid); + + fid = env->GetFieldID(gattDbElementClazz, "permissions", "I"); + curr.permissions = env->GetIntField(element.get(), fid); + + db.push_back(curr); + } + + sGattIf->server->add_service(server_if, db.data(), db.size()); +} + +static void gattServerStopServiceNative(JNIEnv* env, jobject object, + jint server_if, jint svc_handle) { + if (!sGattIf) return; + sGattIf->server->stop_service(server_if, svc_handle); +} + +static void gattServerDeleteServiceNative(JNIEnv* env, jobject object, + jint server_if, jint svc_handle) { + if (!sGattIf) return; + sGattIf->server->delete_service(server_if, svc_handle); +} + +static void gattServerSendIndicationNative(JNIEnv* env, jobject object, + jint server_if, jint attr_handle, + jint conn_id, jbyteArray val) { + if (!sGattIf) return; + + jbyte* array = env->GetByteArrayElements(val, 0); + int val_len = env->GetArrayLength(val); + + sGattIf->server->send_indication(server_if, attr_handle, conn_id, + /*confirm*/ 1, (uint8_t*)array, val_len); + + env->ReleaseByteArrayElements(val, array, JNI_ABORT); +} + +static void gattServerSendNotificationNative(JNIEnv* env, jobject object, + jint server_if, jint attr_handle, + jint conn_id, jbyteArray val) { + if (!sGattIf) return; + + jbyte* array = env->GetByteArrayElements(val, 0); + int val_len = env->GetArrayLength(val); + + sGattIf->server->send_indication(server_if, attr_handle, conn_id, + /*confirm*/ 0, (uint8_t*)array, val_len); + + env->ReleaseByteArrayElements(val, array, JNI_ABORT); +} + +static void gattServerSendResponseNative(JNIEnv* env, jobject object, + jint server_if, jint conn_id, + jint trans_id, jint status, + jint handle, jint offset, + jbyteArray val, jint auth_req) { + if (!sGattIf) return; + + btgatt_response_t response; + + response.attr_value.handle = handle; + response.attr_value.auth_req = auth_req; + response.attr_value.offset = offset; + response.attr_value.len = 0; + + if (val != NULL) { + 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; + } + + jbyte* array = env->GetByteArrayElements(val, 0); + + for (int i = 0; i != response.attr_value.len; ++i) + response.attr_value.value[i] = (uint8_t)array[i]; + env->ReleaseByteArrayElements(val, array, JNI_ABORT); + } + + sGattIf->server->send_response(conn_id, trans_id, status, response); +} + +static void advertiseClassInitNative(JNIEnv* env, jclass clazz) { + method_onAdvertisingSetStarted = + env->GetMethodID(clazz, "onAdvertisingSetStarted", "(IIII)V"); + method_onOwnAddressRead = + env->GetMethodID(clazz, "onOwnAddressRead", "(IILjava/lang/String;)V"); + method_onAdvertisingEnabled = + env->GetMethodID(clazz, "onAdvertisingEnabled", "(IZI)V"); + method_onAdvertisingDataSet = + env->GetMethodID(clazz, "onAdvertisingDataSet", "(II)V"); + method_onScanResponseDataSet = + env->GetMethodID(clazz, "onScanResponseDataSet", "(II)V"); + method_onAdvertisingParametersUpdated = + env->GetMethodID(clazz, "onAdvertisingParametersUpdated", "(III)V"); + method_onPeriodicAdvertisingParametersUpdated = env->GetMethodID( + clazz, "onPeriodicAdvertisingParametersUpdated", "(II)V"); + method_onPeriodicAdvertisingDataSet = + env->GetMethodID(clazz, "onPeriodicAdvertisingDataSet", "(II)V"); + method_onPeriodicAdvertisingEnabled = + env->GetMethodID(clazz, "onPeriodicAdvertisingEnabled", "(IZI)V"); +} + +static void advertiseInitializeNative(JNIEnv* env, jobject object) { + if (mAdvertiseCallbacksObj != NULL) { + ALOGW("Cleaning up Advertise callback object"); + env->DeleteGlobalRef(mAdvertiseCallbacksObj); + mAdvertiseCallbacksObj = NULL; + } + + mAdvertiseCallbacksObj = env->NewGlobalRef(object); +} + +static void advertiseCleanupNative(JNIEnv* env, jobject object) { + if (mAdvertiseCallbacksObj != NULL) { + env->DeleteGlobalRef(mAdvertiseCallbacksObj); + mAdvertiseCallbacksObj = NULL; + } +} + +static uint32_t INTERVAL_MAX = 0xFFFFFF; +// Always give controller 31.25ms difference between min and max +static uint32_t INTERVAL_DELTA = 50; + +static AdvertiseParameters parseParams(JNIEnv* env, jobject i) { + AdvertiseParameters p; + + jclass clazz = env->GetObjectClass(i); + jmethodID methodId; + + methodId = env->GetMethodID(clazz, "isConnectable", "()Z"); + jboolean isConnectable = env->CallBooleanMethod(i, methodId); + methodId = env->GetMethodID(clazz, "isScannable", "()Z"); + jboolean isScannable = env->CallBooleanMethod(i, methodId); + methodId = env->GetMethodID(clazz, "isLegacy", "()Z"); + jboolean isLegacy = env->CallBooleanMethod(i, methodId); + methodId = env->GetMethodID(clazz, "isAnonymous", "()Z"); + jboolean isAnonymous = env->CallBooleanMethod(i, methodId); + methodId = env->GetMethodID(clazz, "includeTxPower", "()Z"); + jboolean includeTxPower = env->CallBooleanMethod(i, methodId); + methodId = env->GetMethodID(clazz, "getPrimaryPhy", "()I"); + uint8_t primaryPhy = env->CallIntMethod(i, methodId); + methodId = env->GetMethodID(clazz, "getSecondaryPhy", "()I"); + uint8_t secondaryPhy = env->CallIntMethod(i, methodId); + methodId = env->GetMethodID(clazz, "getInterval", "()I"); + uint32_t interval = env->CallIntMethod(i, methodId); + methodId = env->GetMethodID(clazz, "getTxPowerLevel", "()I"); + int8_t txPowerLevel = env->CallIntMethod(i, methodId); + + uint16_t props = 0; + if (isConnectable) props |= 0x01; + if (isScannable) props |= 0x02; + if (isLegacy) props |= 0x10; + if (isAnonymous) props |= 0x20; + if (includeTxPower) props |= 0x40; + + if (interval > INTERVAL_MAX - INTERVAL_DELTA) { + interval = INTERVAL_MAX - INTERVAL_DELTA; + } + + p.advertising_event_properties = props; + p.min_interval = interval; + p.max_interval = interval + INTERVAL_DELTA; + p.channel_map = 0x07; /* all channels */ + p.tx_power = txPowerLevel; + p.primary_advertising_phy = primaryPhy; + p.secondary_advertising_phy = secondaryPhy; + p.scan_request_notification_enable = false; + return p; +} + +static PeriodicAdvertisingParameters parsePeriodicParams(JNIEnv* env, + jobject i) { + PeriodicAdvertisingParameters p; + + if (i == NULL) { + p.enable = false; + return p; + } + + jclass clazz = env->GetObjectClass(i); + jmethodID methodId; + + methodId = env->GetMethodID(clazz, "getIncludeTxPower", "()Z"); + jboolean includeTxPower = env->CallBooleanMethod(i, methodId); + methodId = env->GetMethodID(clazz, "getInterval", "()I"); + uint16_t interval = env->CallIntMethod(i, methodId); + + p.enable = true; + p.min_interval = interval; + p.max_interval = interval + 16; /* 20ms difference betwen min and max */ + uint16_t props = 0; + if (includeTxPower) props |= 0x40; + p.periodic_advertising_properties = props; + return p; +} + +static void ble_advertising_set_started_cb(int reg_id, uint8_t advertiser_id, + int8_t tx_power, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingSetStarted, reg_id, + advertiser_id, tx_power, status); +} + +static void ble_advertising_set_timeout_cb(uint8_t advertiser_id, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingEnabled, advertiser_id, + false, status); +} + +static void startAdvertisingSetNative(JNIEnv* env, jobject object, + jobject parameters, jbyteArray adv_data, + jbyteArray scan_resp, + jobject periodic_parameters, + jbyteArray periodic_data, jint duration, + jint maxExtAdvEvents, jint reg_id) { + if (!sGattIf) return; + + jbyte* scan_resp_data = env->GetByteArrayElements(scan_resp, NULL); + uint16_t scan_resp_len = (uint16_t)env->GetArrayLength(scan_resp); + std::vector scan_resp_vec(scan_resp_data, + scan_resp_data + scan_resp_len); + env->ReleaseByteArrayElements(scan_resp, scan_resp_data, JNI_ABORT); + + AdvertiseParameters params = parseParams(env, parameters); + PeriodicAdvertisingParameters periodicParams = + parsePeriodicParams(env, periodic_parameters); + + jbyte* adv_data_data = env->GetByteArrayElements(adv_data, NULL); + uint16_t adv_data_len = (uint16_t)env->GetArrayLength(adv_data); + std::vector data_vec(adv_data_data, adv_data_data + adv_data_len); + env->ReleaseByteArrayElements(adv_data, adv_data_data, JNI_ABORT); + + jbyte* periodic_data_data = env->GetByteArrayElements(periodic_data, NULL); + uint16_t periodic_data_len = (uint16_t)env->GetArrayLength(periodic_data); + std::vector periodic_data_vec( + periodic_data_data, periodic_data_data + periodic_data_len); + env->ReleaseByteArrayElements(periodic_data, periodic_data_data, JNI_ABORT); + + sGattIf->advertiser->StartAdvertisingSet( + reg_id, base::Bind(&ble_advertising_set_started_cb, reg_id), params, + data_vec, scan_resp_vec, periodicParams, periodic_data_vec, duration, + maxExtAdvEvents, base::Bind(ble_advertising_set_timeout_cb)); +} + +static void stopAdvertisingSetNative(JNIEnv* env, jobject object, + jint advertiser_id) { + if (!sGattIf) return; + + sGattIf->advertiser->Unregister(advertiser_id); +} + +static void getOwnAddressCb(uint8_t advertiser_id, uint8_t address_type, + RawAddress address) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr(sCallbackEnv.get(), + bdaddr2newjstr(sCallbackEnv.get(), &address)); + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, method_onOwnAddressRead, + advertiser_id, address_type, addr.get()); +} + +static void getOwnAddressNative(JNIEnv* env, jobject object, + jint advertiser_id) { + if (!sGattIf) return; + sGattIf->advertiser->GetOwnAddress( + advertiser_id, base::Bind(&getOwnAddressCb, advertiser_id)); +} + +static void callJniCallback(jmethodID method, uint8_t advertiser_id, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, method, advertiser_id, + status); +} + +static void enableSetCb(uint8_t advertiser_id, bool enable, uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingEnabled, advertiser_id, + enable, status); +} + +static void enableAdvertisingSetNative(JNIEnv* env, jobject object, + jint advertiser_id, jboolean enable, + jint duration, jint maxExtAdvEvents) { + if (!sGattIf) return; + + sGattIf->advertiser->Enable(advertiser_id, enable, + base::Bind(&enableSetCb, advertiser_id, enable), + duration, maxExtAdvEvents, + base::Bind(&enableSetCb, advertiser_id, false)); +} + +static void setAdvertisingDataNative(JNIEnv* env, jobject object, + jint advertiser_id, jbyteArray data) { + if (!sGattIf) return; + + sGattIf->advertiser->SetData( + advertiser_id, false, toVector(env, data), + base::Bind(&callJniCallback, method_onAdvertisingDataSet, advertiser_id)); +} + +static void setScanResponseDataNative(JNIEnv* env, jobject object, + jint advertiser_id, jbyteArray data) { + if (!sGattIf) return; + + sGattIf->advertiser->SetData( + advertiser_id, true, toVector(env, data), + base::Bind(&callJniCallback, method_onScanResponseDataSet, + advertiser_id)); +} + +static void setAdvertisingParametersNativeCb(uint8_t advertiser_id, + uint8_t status, int8_t tx_power) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onAdvertisingParametersUpdated, + advertiser_id, tx_power, status); +} + +static void setAdvertisingParametersNative(JNIEnv* env, jobject object, + jint advertiser_id, + jobject parameters) { + if (!sGattIf) return; + + AdvertiseParameters params = parseParams(env, parameters); + sGattIf->advertiser->SetParameters( + advertiser_id, params, + base::Bind(&setAdvertisingParametersNativeCb, advertiser_id)); +} + +static void setPeriodicAdvertisingParametersNative( + JNIEnv* env, jobject object, jint advertiser_id, + jobject periodic_parameters) { + if (!sGattIf) return; + + PeriodicAdvertisingParameters periodicParams = + parsePeriodicParams(env, periodic_parameters); + sGattIf->advertiser->SetPeriodicAdvertisingParameters( + advertiser_id, periodicParams, + base::Bind(&callJniCallback, + method_onPeriodicAdvertisingParametersUpdated, advertiser_id)); +} + +static void setPeriodicAdvertisingDataNative(JNIEnv* env, jobject object, + jint advertiser_id, + jbyteArray data) { + if (!sGattIf) return; + + sGattIf->advertiser->SetPeriodicAdvertisingData( + advertiser_id, toVector(env, data), + base::Bind(&callJniCallback, method_onPeriodicAdvertisingDataSet, + advertiser_id)); +} + +static void enablePeriodicSetCb(uint8_t advertiser_id, bool enable, + uint8_t status) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj, + method_onPeriodicAdvertisingEnabled, + advertiser_id, enable, status); +} + +static void setPeriodicAdvertisingEnableNative(JNIEnv* env, jobject object, + jint advertiser_id, + jboolean enable) { + if (!sGattIf) return; + + sGattIf->advertiser->SetPeriodicAdvertisingEnable( + advertiser_id, enable, + base::Bind(&enablePeriodicSetCb, advertiser_id, enable)); +} + +static void periodicScanClassInitNative(JNIEnv* env, jclass clazz) { + method_onSyncStarted = + env->GetMethodID(clazz, "onSyncStarted", "(IIIILjava/lang/String;III)V"); + method_onSyncReport = env->GetMethodID(clazz, "onSyncReport", "(IIII[B)V"); + method_onSyncLost = env->GetMethodID(clazz, "onSyncLost", "(I)V"); +} + +static void periodicScanInitializeNative(JNIEnv* env, jobject object) { + if (mPeriodicScanCallbacksObj != NULL) { + ALOGW("Cleaning up periodic scan callback object"); + env->DeleteGlobalRef(mPeriodicScanCallbacksObj); + mPeriodicScanCallbacksObj = NULL; + } + + mPeriodicScanCallbacksObj = env->NewGlobalRef(object); +} + +static void periodicScanCleanupNative(JNIEnv* env, jobject object) { + if (mPeriodicScanCallbacksObj != NULL) { + env->DeleteGlobalRef(mPeriodicScanCallbacksObj); + mPeriodicScanCallbacksObj = NULL; + } +} + +static void onSyncStarted(int reg_id, uint8_t status, uint16_t sync_handle, + uint8_t sid, uint8_t address_type, RawAddress address, + uint8_t phy, uint16_t interval) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncStarted, + reg_id, sync_handle, sid, address_type, address, + phy, interval, status); +} + +static void onSyncReport(uint16_t sync_handle, int8_t tx_power, int8_t rssi, + uint8_t data_status, std::vector data) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef jb(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(data.size())); + sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(), + (jbyte*)data.data()); + + sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncReport, + sync_handle, tx_power, rssi, data_status, + jb.get()); +} + +static void onSyncLost(uint16_t sync_handle) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncLost, + sync_handle); +} + +static void startSyncNative(JNIEnv* env, jobject object, jint sid, + jstring address, jint skip, jint timeout, + jint reg_id) { + if (!sGattIf) return; + + sGattIf->scanner->StartSync(sid, str2addr(env, address), skip, timeout, + base::Bind(&onSyncStarted, reg_id), + base::Bind(&onSyncReport), + base::Bind(&onSyncLost)); +} + +static void stopSyncNative(int sync_handle) { + if (!sGattIf) return; + + sGattIf->scanner->StopSync(sync_handle); +} + +static void gattTestNative(JNIEnv* env, jobject object, jint command, + jlong uuid1_lsb, jlong uuid1_msb, jstring bda1, + jint p1, jint p2, jint p3, jint p4, jint p5) { + if (!sGattIf) return; + + RawAddress bt_bda1 = str2addr(env, bda1); + + Uuid uuid1 = from_java_uuid(uuid1_msb, uuid1_lsb); + + btgatt_test_params_t params; + params.bda1 = &bt_bda1; + params.uuid1 = &uuid1; + params.u1 = p1; + params.u2 = p2; + params.u3 = p3; + params.u4 = p4; + params.u5 = p5; + sGattIf->client->test_command(command, params); +} + +/** + * JNI function definitinos + */ + +// JNI functions defined in AdvertiseManager class. +static JNINativeMethod sAdvertiseMethods[] = { + {"classInitNative", "()V", (void*)advertiseClassInitNative}, + {"initializeNative", "()V", (void*)advertiseInitializeNative}, + {"cleanupNative", "()V", (void*)advertiseCleanupNative}, + {"startAdvertisingSetNative", + "(Landroid/bluetooth/le/AdvertisingSetParameters;[B[BLandroid/bluetooth/" + "le/PeriodicAdvertisingParameters;[BIII)V", + (void*)startAdvertisingSetNative}, + {"getOwnAddressNative", "(I)V", (void*)getOwnAddressNative}, + {"stopAdvertisingSetNative", "(I)V", (void*)stopAdvertisingSetNative}, + {"enableAdvertisingSetNative", "(IZII)V", + (void*)enableAdvertisingSetNative}, + {"setAdvertisingDataNative", "(I[B)V", (void*)setAdvertisingDataNative}, + {"setScanResponseDataNative", "(I[B)V", (void*)setScanResponseDataNative}, + {"setAdvertisingParametersNative", + "(ILandroid/bluetooth/le/AdvertisingSetParameters;)V", + (void*)setAdvertisingParametersNative}, + {"setPeriodicAdvertisingParametersNative", + "(ILandroid/bluetooth/le/PeriodicAdvertisingParameters;)V", + (void*)setPeriodicAdvertisingParametersNative}, + {"setPeriodicAdvertisingDataNative", "(I[B)V", + (void*)setPeriodicAdvertisingDataNative}, + {"setPeriodicAdvertisingEnableNative", "(IZ)V", + (void*)setPeriodicAdvertisingEnableNative}, +}; + +// JNI functions defined in PeriodicScanManager class. +static JNINativeMethod sPeriodicScanMethods[] = { + {"classInitNative", "()V", (void*)periodicScanClassInitNative}, + {"initializeNative", "()V", (void*)periodicScanInitializeNative}, + {"cleanupNative", "()V", (void*)periodicScanCleanupNative}, + {"startSyncNative", "(ILjava/lang/String;III)V", (void*)startSyncNative}, + {"stopSyncNative", "(I)V", (void*)stopSyncNative}, +}; + +// JNI functions defined in ScanManager class. +static JNINativeMethod sScanMethods[] = { + {"registerScannerNative", "(JJ)V", (void*)registerScannerNative}, + {"unregisterScannerNative", "(I)V", (void*)unregisterScannerNative}, + {"gattClientScanNative", "(Z)V", (void*)gattClientScanNative}, + // Batch scan JNI functions. + {"gattClientConfigBatchScanStorageNative", "(IIII)V", + (void*)gattClientConfigBatchScanStorageNative}, + {"gattClientStartBatchScanNative", "(IIIIII)V", + (void*)gattClientStartBatchScanNative}, + {"gattClientStopBatchScanNative", "(I)V", + (void*)gattClientStopBatchScanNative}, + {"gattClientReadScanReportsNative", "(II)V", + (void*)gattClientReadScanReportsNative}, + // Scan filter JNI functions. + {"gattClientScanFilterParamAddNative", + "(Lcom/android/bluetooth/gatt/FilterParams;)V", + (void*)gattClientScanFilterParamAddNative}, + {"gattClientScanFilterParamDeleteNative", "(II)V", + (void*)gattClientScanFilterParamDeleteNative}, + {"gattClientScanFilterParamClearAllNative", "(I)V", + (void*)gattClientScanFilterParamClearAllNative}, + {"gattClientScanFilterAddNative", + "(I[Lcom/android/bluetooth/gatt/ScanFilterQueue$Entry;I)V", + (void*)gattClientScanFilterAddNative}, + {"gattClientScanFilterClearNative", "(II)V", + (void*)gattClientScanFilterClearNative}, + {"gattClientScanFilterEnableNative", "(IZ)V", + (void*)gattClientScanFilterEnableNative}, + {"gattSetScanParametersNative", "(III)V", + (void*)gattSetScanParametersNative}, +}; + +// JNI functions defined in GattService class. +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNative", "()V", (void*)initializeNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"gattClientGetDeviceTypeNative", "(Ljava/lang/String;)I", + (void*)gattClientGetDeviceTypeNative}, + {"gattClientRegisterAppNative", "(JJZ)V", + (void*)gattClientRegisterAppNative}, + {"gattClientUnregisterAppNative", "(I)V", + (void*)gattClientUnregisterAppNative}, + {"gattClientConnectNative", "(ILjava/lang/String;ZIZI)V", + (void*)gattClientConnectNative}, + {"gattClientDisconnectNative", "(ILjava/lang/String;I)V", + (void*)gattClientDisconnectNative}, + {"gattClientSetPreferredPhyNative", "(ILjava/lang/String;III)V", + (void*)gattClientSetPreferredPhyNative}, + {"gattClientReadPhyNative", "(ILjava/lang/String;)V", + (void*)gattClientReadPhyNative}, + {"gattClientRefreshNative", "(ILjava/lang/String;)V", + (void*)gattClientRefreshNative}, + {"gattClientSearchServiceNative", "(IZJJ)V", + (void*)gattClientSearchServiceNative}, + {"gattClientDiscoverServiceByUuidNative", "(IJJ)V", + (void*)gattClientDiscoverServiceByUuidNative}, + {"gattClientGetGattDbNative", "(I)V", (void*)gattClientGetGattDbNative}, + {"gattClientReadCharacteristicNative", "(III)V", + (void*)gattClientReadCharacteristicNative}, + {"gattClientReadUsingCharacteristicUuidNative", "(IJJIII)V", + (void*)gattClientReadUsingCharacteristicUuidNative}, + {"gattClientReadDescriptorNative", "(III)V", + (void*)gattClientReadDescriptorNative}, + {"gattClientWriteCharacteristicNative", "(IIII[B)V", + (void*)gattClientWriteCharacteristicNative}, + {"gattClientWriteDescriptorNative", "(III[B)V", + (void*)gattClientWriteDescriptorNative}, + {"gattClientExecuteWriteNative", "(IZ)V", + (void*)gattClientExecuteWriteNative}, + {"gattClientRegisterForNotificationsNative", "(ILjava/lang/String;IZ)V", + (void*)gattClientRegisterForNotificationsNative}, + {"gattClientReadRemoteRssiNative", "(ILjava/lang/String;)V", + (void*)gattClientReadRemoteRssiNative}, + {"gattClientConfigureMTUNative", "(II)V", + (void*)gattClientConfigureMTUNative}, + {"gattConnectionParameterUpdateNative", "(ILjava/lang/String;IIIIII)V", + (void*)gattConnectionParameterUpdateNative}, + {"gattServerRegisterAppNative", "(JJZ)V", + (void*)gattServerRegisterAppNative}, + {"gattServerUnregisterAppNative", "(I)V", + (void*)gattServerUnregisterAppNative}, + {"gattServerConnectNative", "(ILjava/lang/String;ZI)V", + (void*)gattServerConnectNative}, + {"gattServerDisconnectNative", "(ILjava/lang/String;I)V", + (void*)gattServerDisconnectNative}, + {"gattServerSetPreferredPhyNative", "(ILjava/lang/String;III)V", + (void*)gattServerSetPreferredPhyNative}, + {"gattServerReadPhyNative", "(ILjava/lang/String;)V", + (void*)gattServerReadPhyNative}, + {"gattServerAddServiceNative", "(ILjava/util/List;)V", + (void*)gattServerAddServiceNative}, + {"gattServerStopServiceNative", "(II)V", + (void*)gattServerStopServiceNative}, + {"gattServerDeleteServiceNative", "(II)V", + (void*)gattServerDeleteServiceNative}, + {"gattServerSendIndicationNative", "(III[B)V", + (void*)gattServerSendIndicationNative}, + {"gattServerSendNotificationNative", "(III[B)V", + (void*)gattServerSendNotificationNative}, + {"gattServerSendResponseNative", "(IIIIII[BI)V", + (void*)gattServerSendResponseNative}, + + {"gattTestNative", "(IJJLjava/lang/String;IIIII)V", (void*)gattTestNative}, +}; + +int register_com_android_bluetooth_gatt(JNIEnv* env) { + int register_success = jniRegisterNativeMethods( + env, "com/android/bluetooth/gatt/ScanManager$ScanNative", sScanMethods, + NELEM(sScanMethods)); + register_success &= jniRegisterNativeMethods( + env, "com/android/bluetooth/gatt/AdvertiseManager", sAdvertiseMethods, + NELEM(sAdvertiseMethods)); + register_success &= jniRegisterNativeMethods( + env, "com/android/bluetooth/gatt/PeriodicScanManager", + sPeriodicScanMethods, NELEM(sPeriodicScanMethods)); + return register_success & + jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/GattService", + sMethods, NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_hearing_aid.cpp b/android/app/jni/com_android_bluetooth_hearing_aid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e359faf8a4b15270cdc5fab58be0de80e27315a9 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_hearing_aid.cpp @@ -0,0 +1,236 @@ +/* + * Copyright 2018 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. + */ + +#define LOG_TAG "BluetoothHearingAidServiceJni" + +#define LOG_NDEBUG 0 + +#include "base/logging.h" +#include "com_android_bluetooth.h" +#include "hardware/bt_hearing_aid.h" + +#include +#include + +using bluetooth::hearing_aid::ConnectionState; +using bluetooth::hearing_aid::HearingAidInterface; +using bluetooth::hearing_aid::HearingAidCallbacks; + +namespace android { +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onDeviceAvailable; + +static HearingAidInterface* sHearingAidInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +class HearingAidCallbacksImpl : public HearingAidCallbacks { + public: + ~HearingAidCallbacksImpl() = default; + void OnConnectionState(ConnectionState state, + const RawAddress& bd_addr) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + (jint)state, addr.get()); + } + + void OnDeviceAvailable(uint8_t capabilities, uint64_t hi_sync_id, + const RawAddress& bd_addr) override { + LOG(INFO) << __func__ << ": capabilities=" << +capabilities + << " hi_sync_id=" << hi_sync_id; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable, + (jbyte)capabilities, (jlong)hi_sync_id, + addr.get()); + } +}; + +static HearingAidCallbacksImpl sHearingAidCallbacks; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); + + method_onDeviceAvailable = + env->GetMethodID(clazz, "onDeviceAvailable", "(BJ[B)V"); + + LOG(INFO) << __func__ << ": succeeds"; +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sHearingAidInterface != nullptr) { + LOG(INFO) << "Cleaning up HearingAid Interface before initializing..."; + sHearingAidInterface->Cleanup(); + sHearingAidInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + LOG(INFO) << "Cleaning up HearingAid callback object"; + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + LOG(ERROR) << "Failed to allocate Global Ref for Hearing Aid Callbacks"; + return; + } + + sHearingAidInterface = (HearingAidInterface*)btInf->get_profile_interface( + BT_PROFILE_HEARING_AID_ID); + if (sHearingAidInterface == nullptr) { + LOG(ERROR) << "Failed to get Bluetooth Hearing Aid Interface"; + return; + } + + sHearingAidInterface->Init(&sHearingAidCallbacks); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sHearingAidInterface != nullptr) { + sHearingAidInterface->Cleanup(); + sHearingAidInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static jboolean connectHearingAidNative(JNIEnv* env, jobject object, + jbyteArray address) { + LOG(INFO) << __func__; + std::shared_lock lock(interface_mutex); + if (!sHearingAidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sHearingAidInterface->Connect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static jboolean disconnectHearingAidNative(JNIEnv* env, jobject object, + jbyteArray address) { + LOG(INFO) << __func__; + std::shared_lock lock(interface_mutex); + if (!sHearingAidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sHearingAidInterface->Disconnect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static jboolean addToAcceptlistNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sHearingAidInterface) return JNI_FALSE; + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sHearingAidInterface->AddToAcceptlist(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static void setVolumeNative(JNIEnv* env, jclass clazz, jint volume) { + if (!sHearingAidInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Bluetooth Hearing Aid Interface"; + return; + } + sHearingAidInterface->SetVolume(volume); +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative}, + {"disconnectHearingAidNative", "([B)Z", (void*)disconnectHearingAidNative}, + {"addToAcceptlistNative", "([B)Z", (void*)addToAcceptlistNative}, + {"setVolumeNative", "(I)V", (void*)setVolumeNative}, +}; + +int register_com_android_bluetooth_hearing_aid(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface", + sMethods, NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_hfp.cpp b/android/app/jni/com_android_bluetooth_hfp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..813bdd0818b8516ff9167131c420b296415f51d9 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_hfp.cpp @@ -0,0 +1,995 @@ +/* + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothHeadsetServiceJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bluetooth_headset_callbacks.h" +#include "hardware/bluetooth_headset_interface.h" +#include "hardware/bt_hf.h" +#include "utils/Log.h" + +#include +#include + +namespace android { + +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onAudioStateChanged; +static jmethodID method_onVrStateChanged; +static jmethodID method_onAnswerCall; +static jmethodID method_onHangupCall; +static jmethodID method_onVolumeChanged; +static jmethodID method_onDialCall; +static jmethodID method_onSendDtmf; +static jmethodID method_onNoiseReductionEnable; +static jmethodID method_onWBS; +static jmethodID method_onAtChld; +static jmethodID method_onAtCnum; +static jmethodID method_onAtCind; +static jmethodID method_onAtCops; +static jmethodID method_onAtClcc; +static jmethodID method_onUnknownAt; +static jmethodID method_onKeyPressed; +static jmethodID method_onAtBind; +static jmethodID method_onAtBiev; +static jmethodID method_onAtBia; + +static bluetooth::headset::Interface* sBluetoothHfpInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +static jbyteArray marshall_bda(RawAddress* bd_addr) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return nullptr; + + jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); + if (!addr) { + ALOGE("Fail to new jbyteArray bd addr"); + return nullptr; + } + sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), + (jbyte*)bd_addr); + return addr; +} + +class JniHeadsetCallbacks : bluetooth::headset::Callbacks { + public: + static bluetooth::headset::Callbacks* GetInstance() { + static bluetooth::headset::Callbacks* instance = new JniHeadsetCallbacks(); + return instance; + } + + void ConnectionStateCallback( + bluetooth::headset::bthf_connection_state_t state, + RawAddress* bd_addr) override { + ALOGI("%s %d for %s", __func__, state, bd_addr->ToString().c_str()); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + (jint)state, addr.get()); + } + + void AudioStateCallback(bluetooth::headset::bthf_audio_state_t state, + RawAddress* bd_addr) override { + ALOGI("%s, %d for %s", __func__, state, bd_addr->ToString().c_str()); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, + (jint)state, addr.get()); + } + + void VoiceRecognitionCallback(bluetooth::headset::bthf_vr_state_t state, + RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVrStateChanged, + (jint)state, addr.get()); + } + + void AnswerCallCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAnswerCall, + addr.get()); + } + + void HangupCallCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHangupCall, + addr.get()); + } + + void VolumeControlCallback(bluetooth::headset::bthf_volume_type_t type, + int volume, RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeChanged, + (jint)type, (jint)volume, addr.get()); + } + + void DialCallCallback(char* number, RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + char null_str[] = ""; + if (!sCallbackEnv.isValidUtf(number)) { + android_errorWriteLog(0x534e4554, "109838537"); + ALOGE("%s: number is not a valid UTF string.", __func__); + number = null_str; + } + + ScopedLocalRef js_number(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(number)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDialCall, + js_number.get(), addr.get()); + } + + void DtmfCmdCallback(char dtmf, RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + // TBD dtmf has changed from int to char + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSendDtmf, dtmf, + addr.get()); + } + + void NoiseReductionCallback(bluetooth::headset::bthf_nrec_t nrec, + RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNoiseReductionEnable, + nrec == bluetooth::headset::BTHF_NREC_START, + addr.get()); + } + + void WbsCallback(bluetooth::headset::bthf_wbs_config_t wbs_config, + RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (addr.get() == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWBS, wbs_config, + addr.get()); + } + + void AtChldCallback(bluetooth::headset::bthf_chld_type_t chld, + RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtChld, chld, + addr.get()); + } + + void AtCnumCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCnum, addr.get()); + } + + void AtCindCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCind, addr.get()); + } + + void AtCopsCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCops, addr.get()); + } + + void AtClccCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtClcc, addr.get()); + } + + void UnknownAtCallback(char* at_string, RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + 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; + } + + ScopedLocalRef js_at_string(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(at_string)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onUnknownAt, + js_at_string.get(), addr.get()); + } + + void KeyPressedCallback(RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for audio state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onKeyPressed, + addr.get()); + } + + void AtBindCallback(char* at_string, RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (addr.get() == nullptr) return; + + 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; + } + + ScopedLocalRef js_at_string(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(at_string)); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBind, + js_at_string.get(), addr.get()); + } + + void AtBievCallback(bluetooth::headset::bthf_hf_ind_type_t ind_id, + int ind_value, RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (addr.get() == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBiev, ind_id, + (jint)ind_value, addr.get()); + } + + void AtBiaCallback(bool service, bool roam, bool signal, bool battery, + RawAddress* bd_addr) override { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || !mCallbacksObj) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (addr.get() == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBia, service, roam, + signal, battery, addr.get()); + } +}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); + method_onAudioStateChanged = + env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); + method_onVrStateChanged = + env->GetMethodID(clazz, "onVrStateChanged", "(I[B)V"); + method_onAnswerCall = env->GetMethodID(clazz, "onAnswerCall", "([B)V"); + method_onHangupCall = env->GetMethodID(clazz, "onHangupCall", "([B)V"); + method_onVolumeChanged = + env->GetMethodID(clazz, "onVolumeChanged", "(II[B)V"); + method_onDialCall = + env->GetMethodID(clazz, "onDialCall", "(Ljava/lang/String;[B)V"); + method_onSendDtmf = env->GetMethodID(clazz, "onSendDtmf", "(I[B)V"); + method_onNoiseReductionEnable = + env->GetMethodID(clazz, "onNoiseReductionEnable", "(Z[B)V"); + method_onWBS = env->GetMethodID(clazz, "onWBS", "(I[B)V"); + method_onAtChld = env->GetMethodID(clazz, "onAtChld", "(I[B)V"); + method_onAtCnum = env->GetMethodID(clazz, "onAtCnum", "([B)V"); + method_onAtCind = env->GetMethodID(clazz, "onAtCind", "([B)V"); + method_onAtCops = env->GetMethodID(clazz, "onAtCops", "([B)V"); + method_onAtClcc = env->GetMethodID(clazz, "onAtClcc", "([B)V"); + method_onUnknownAt = + env->GetMethodID(clazz, "onUnknownAt", "(Ljava/lang/String;[B)V"); + method_onKeyPressed = env->GetMethodID(clazz, "onKeyPressed", "([B)V"); + method_onAtBind = + env->GetMethodID(clazz, "onATBind", "(Ljava/lang/String;[B)V"); + method_onAtBiev = env->GetMethodID(clazz, "onATBiev", "(II[B)V"); + method_onAtBia = env->GetMethodID(clazz, "onAtBia", "(ZZZZ[B)V"); + + ALOGI("%s: succeeds", __func__); +} + +static void initializeNative(JNIEnv* env, jobject object, jint max_hf_clients, + jboolean inband_ringing_enabled) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (!btInf) { + ALOGE("%s: Bluetooth module is not loaded", __func__); + jniThrowIOException(env, EINVAL); + return; + } + + if (sBluetoothHfpInterface) { + ALOGI("%s: Cleaning up Bluetooth Handsfree Interface before initializing", + __func__); + sBluetoothHfpInterface->Cleanup(); + sBluetoothHfpInterface = nullptr; + } + + if (mCallbacksObj) { + ALOGI("%s: Cleaning up Bluetooth Handsfree callback object", __func__); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + sBluetoothHfpInterface = + (bluetooth::headset::Interface*)btInf->get_profile_interface( + BT_PROFILE_HANDSFREE_ID); + if (!sBluetoothHfpInterface) { + ALOGW("%s: Failed to get Bluetooth Handsfree Interface", __func__); + jniThrowIOException(env, EINVAL); + return; + } + bt_status_t status = + sBluetoothHfpInterface->Init(JniHeadsetCallbacks::GetInstance(), + max_hf_clients, inband_ringing_enabled); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed to initialize Bluetooth Handsfree Interface, status: %d", + __func__, status); + sBluetoothHfpInterface = nullptr; + return; + } + + mCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (!btInf) { + ALOGW("%s: Bluetooth module is not loaded", __func__); + return; + } + + if (sBluetoothHfpInterface) { + ALOGI("%s: Cleaning up Bluetooth Handsfree Interface", __func__); + sBluetoothHfpInterface->Cleanup(); + sBluetoothHfpInterface = nullptr; + } + + if (mCallbacksObj) { + ALOGI("%s: Cleaning up Bluetooth Handsfree callback object", __func__); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static jboolean connectHfpNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); + bt_status_t status = sBluetoothHfpInterface->Connect((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF connection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean disconnectHfpNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); + bt_status_t status = sBluetoothHfpInterface->Disconnect((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF disconnection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean connectAudioNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); + bt_status_t status = sBluetoothHfpInterface->ConnectAudio((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF audio connection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean disconnectAudioNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); + bt_status_t status = + sBluetoothHfpInterface->DisconnectAudio((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF audio disconnection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean isNoiseReductionSupportedNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = + sBluetoothHfpInterface->isNoiseReductionSupported((RawAddress*)addr); + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean isVoiceRecognitionSupportedNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = + sBluetoothHfpInterface->isVoiceRecognitionSupported((RawAddress*)addr); + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean startVoiceRecognitionNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = + sBluetoothHfpInterface->StartVoiceRecognition((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to start voice recognition, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean stopVoiceRecognitionNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = + sBluetoothHfpInterface->StopVoiceRecognition((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to stop voice recognition, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume_type, + jint volume, jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = sBluetoothHfpInterface->VolumeControl( + (bluetooth::headset::bthf_volume_type_t)volume_type, volume, + (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("FAILED to control volume, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean notifyDeviceStatusNative(JNIEnv* env, jobject object, + jint network_state, jint service_type, + jint signal, jint battery_charge, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = sBluetoothHfpInterface->DeviceStatusNotification( + (bluetooth::headset::bthf_network_state_t)network_state, + (bluetooth::headset::bthf_service_type_t)service_type, signal, + battery_charge, (RawAddress*)addr); + env->ReleaseByteArrayElements(address, addr, 0); + if (status != BT_STATUS_SUCCESS) { + ALOGE("FAILED to notify device status, status: %d", status); + } + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean copsResponseNative(JNIEnv* env, jobject object, + jstring operator_str, jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + const char* operator_name = env->GetStringUTFChars(operator_str, nullptr); + bt_status_t status = + sBluetoothHfpInterface->CopsResponse(operator_name, (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending cops response, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + env->ReleaseStringUTFChars(operator_str, operator_name); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean cindResponseNative(JNIEnv* env, jobject object, jint service, + jint num_active, jint num_held, + jint call_state, jint signal, jint roam, + jint battery_charge, jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = sBluetoothHfpInterface->CindResponse( + service, num_active, num_held, + (bluetooth::headset::bthf_call_state_t)call_state, signal, roam, + battery_charge, (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: failed, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean atResponseStringNative(JNIEnv* env, jobject object, + jstring response_str, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + const char* response = env->GetStringUTFChars(response_str, nullptr); + bt_status_t status = + sBluetoothHfpInterface->FormattedAtResponse(response, (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed formatted AT response, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + env->ReleaseStringUTFChars(response_str, response); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean atResponseCodeNative(JNIEnv* env, jobject object, + jint response_code, jint cmee_code, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = sBluetoothHfpInterface->AtResponse( + (bluetooth::headset::bthf_at_response_t)response_code, cmee_code, + (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed AT response, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean clccResponseNative(JNIEnv* env, jobject object, jint index, + jint dir, jint callStatus, jint mode, + jboolean mpty, jstring number_str, jint type, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + const char* number = nullptr; + if (number_str) { + number = env->GetStringUTFChars(number_str, nullptr); + } + bt_status_t status = sBluetoothHfpInterface->ClccResponse( + index, (bluetooth::headset::bthf_call_direction_t)dir, + (bluetooth::headset::bthf_call_state_t)callStatus, + (bluetooth::headset::bthf_call_mode_t)mode, + mpty ? bluetooth::headset::BTHF_CALL_MPTY_TYPE_MULTI + : bluetooth::headset::BTHF_CALL_MPTY_TYPE_SINGLE, + number, (bluetooth::headset::bthf_call_addrtype_t)type, + (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending CLCC response, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + if (number) { + env->ReleaseStringUTFChars(number_str, number); + } + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean phoneStateChangeNative(JNIEnv* env, jobject object, + jint num_active, jint num_held, + jint call_state, jstring number_str, + jint type, jstring name_str, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + const char* number = env->GetStringUTFChars(number_str, nullptr); + const char* name = nullptr; + if (name_str != nullptr) { + name = env->GetStringUTFChars(name_str, nullptr); + } + bt_status_t status = sBluetoothHfpInterface->PhoneStateChange( + num_active, num_held, (bluetooth::headset::bthf_call_state_t)call_state, + number, (bluetooth::headset::bthf_call_addrtype_t)type, name, + (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed report phone state change, status: %d", status); + } + env->ReleaseStringUTFChars(number_str, number); + if (name != nullptr) { + env->ReleaseStringUTFChars(name_str, name); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setScoAllowedNative(JNIEnv* env, jobject object, + jboolean value) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + bt_status_t status = sBluetoothHfpInterface->SetScoAllowed(value == JNI_TRUE); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed HF set sco allowed, status: %d", status); + } + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean sendBsirNative(JNIEnv* env, jobject object, jboolean value, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = + sBluetoothHfpInterface->SendBsir(value == JNI_TRUE, (RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed sending BSIR, value=%d, status=%d", value, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setActiveDeviceNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpInterface) { + ALOGW("%s: sBluetoothHfpInterface is null", __func__); + return JNI_FALSE; + } + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("%s: failed to get device address", __func__); + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + bt_status_t status = + sBluetoothHfpInterface->SetActiveDevice((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to set active device, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNative", "(IZ)V", (void*)initializeNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectHfpNative", "([B)Z", (void*)connectHfpNative}, + {"disconnectHfpNative", "([B)Z", (void*)disconnectHfpNative}, + {"connectAudioNative", "([B)Z", (void*)connectAudioNative}, + {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative}, + {"isNoiseReductionSupportedNative", "([B)Z", + (void*)isNoiseReductionSupportedNative}, + {"isVoiceRecognitionSupportedNative", "([B)Z", + (void*)isVoiceRecognitionSupportedNative}, + {"startVoiceRecognitionNative", "([B)Z", + (void*)startVoiceRecognitionNative}, + {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative}, + {"setVolumeNative", "(II[B)Z", (void*)setVolumeNative}, + {"notifyDeviceStatusNative", "(IIII[B)Z", (void*)notifyDeviceStatusNative}, + {"copsResponseNative", "(Ljava/lang/String;[B)Z", + (void*)copsResponseNative}, + {"cindResponseNative", "(IIIIIII[B)Z", (void*)cindResponseNative}, + {"atResponseStringNative", "(Ljava/lang/String;[B)Z", + (void*)atResponseStringNative}, + {"atResponseCodeNative", "(II[B)Z", (void*)atResponseCodeNative}, + {"clccResponseNative", "(IIIIZLjava/lang/String;I[B)Z", + (void*)clccResponseNative}, + {"phoneStateChangeNative", "(IIILjava/lang/String;ILjava/lang/String;[B)Z", + (void*)phoneStateChangeNative}, + {"setScoAllowedNative", "(Z)Z", (void*)setScoAllowedNative}, + {"sendBsirNative", "(Z[B)Z", (void*)sendBsirNative}, + {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative}, +}; + +int register_com_android_bluetooth_hfp(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/hfp/HeadsetNativeInterface", sMethods, + NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/android/app/jni/com_android_bluetooth_hfpclient.cpp b/android/app/jni/com_android_bluetooth_hfpclient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..763885e3d3eb6ef2333b8704532e12914e1b508f --- /dev/null +++ b/android/app/jni/com_android_bluetooth_hfpclient.cpp @@ -0,0 +1,921 @@ +/* + * Copyright (c) 2014 The Android Open Source Project + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothHeadsetClientServiceJni" +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_hf_client.h" +#include "utils/Log.h" + +#include + +namespace android { + +static bthf_client_interface_t* sBluetoothHfpClientInterface = NULL; +static std::shared_mutex interface_mutex; + +static jobject mCallbacksObj = NULL; +static std::shared_mutex callbacks_mutex; + +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onAudioStateChanged; +static jmethodID method_onVrStateChanged; +static jmethodID method_onNetworkState; +static jmethodID method_onNetworkRoaming; +static jmethodID method_onNetworkSignal; +static jmethodID method_onBatteryLevel; +static jmethodID method_onCurrentOperator; +static jmethodID method_onCall; +static jmethodID method_onCallSetup; +static jmethodID method_onCallHeld; +static jmethodID method_onRespAndHold; +static jmethodID method_onClip; +static jmethodID method_onCallWaiting; +static jmethodID method_onCurrentCalls; +static jmethodID method_onVolumeChange; +static jmethodID method_onCmdResult; +static jmethodID method_onSubscriberInfo; +static jmethodID method_onInBandRing; +static jmethodID method_onLastVoiceTagNumber; +static jmethodID method_onRingIndication; +static jmethodID method_onUnknownEvent; + +static jbyteArray marshall_bda(const RawAddress* bd_addr) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return NULL; + + jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); + if (!addr) { + ALOGE("Fail to new jbyteArray bd addr"); + return NULL; + } + sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), + (jbyte*)bd_addr); + return addr; +} + +static void connection_state_cb(const RawAddress* bd_addr, + bthf_client_connection_state_t state, + unsigned int peer_feat, + unsigned int chld_feat) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + ALOGD("%s: state %d peer_feat %d chld_feat %d", __func__, state, peer_feat, chld_feat); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + (jint)state, (jint)peer_feat, (jint)chld_feat, + addr.get()); +} + +static void audio_state_cb(const RawAddress* bd_addr, + bthf_client_audio_state_t state) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, + (jint)state, addr.get()); +} + +static void vr_cmd_cb(const RawAddress* bd_addr, bthf_client_vr_state_t state) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVrStateChanged, + (jint)state, addr.get()); +} + +static void network_state_cb(const RawAddress* bd_addr, + bthf_client_network_state_t state) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNetworkState, + (jint)state, addr.get()); +} + +static void network_roaming_cb(const RawAddress* bd_addr, + bthf_client_service_type_t type) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNetworkRoaming, + (jint)type, addr.get()); +} + +static void network_signal_cb(const RawAddress* bd_addr, int signal) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNetworkSignal, + (jint)signal, addr.get()); +} + +static void battery_level_cb(const RawAddress* bd_addr, int level) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBatteryLevel, + (jint)level, addr.get()); +} + +static void current_operator_cb(const RawAddress* bd_addr, const char* name) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + 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; + } + + ScopedLocalRef js_name(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(name)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCurrentOperator, + js_name.get(), addr.get()); +} + +static void call_cb(const RawAddress* bd_addr, bthf_client_call_t call) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCall, (jint)call, + addr.get()); +} + +static void callsetup_cb(const RawAddress* bd_addr, + bthf_client_callsetup_t callsetup) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + ALOGD("callsetup_cb bdaddr %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr->address[0], bd_addr->address[1], bd_addr->address[2], + bd_addr->address[3], bd_addr->address[4], bd_addr->address[5]); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCallSetup, + (jint)callsetup, addr.get()); +} + +static void callheld_cb(const RawAddress* bd_addr, + bthf_client_callheld_t callheld) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCallHeld, (jint)callheld, + addr.get()); +} + +static void resp_and_hold_cb(const RawAddress* bd_addr, + bthf_client_resp_and_hold_t resp_and_hold) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRespAndHold, + (jint)resp_and_hold, addr.get()); +} + +static void clip_cb(const RawAddress* bd_addr, const char* number) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + 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; + } + + ScopedLocalRef js_number(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(number)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClip, js_number.get(), + addr.get()); +} + +static void call_waiting_cb(const RawAddress* bd_addr, const char* number) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + 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; + } + + ScopedLocalRef js_number(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(number)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCallWaiting, + js_number.get(), addr.get()); +} + +static void current_calls_cb(const RawAddress* bd_addr, int index, + bthf_client_call_direction_t dir, + bthf_client_call_state_t state, + bthf_client_call_mpty_type_t mpty, + const char* number) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + 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; + } + + ScopedLocalRef js_number(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(number)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCurrentCalls, index, dir, + state, mpty, js_number.get(), addr.get()); +} + +static void volume_change_cb(const RawAddress* bd_addr, + bthf_client_volume_type_t type, int volume) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeChange, (jint)type, + (jint)volume, addr.get()); +} + +static void cmd_complete_cb(const RawAddress* bd_addr, + bthf_client_cmd_complete_t type, int cme) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCmdResult, (jint)type, + (jint)cme, addr.get()); +} + +static void subscriber_info_cb(const RawAddress* bd_addr, const char* name, + bthf_client_subscriber_service_type_t type) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + 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; + } + + ScopedLocalRef js_name(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(name)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSubscriberInfo, + js_name.get(), (jint)type, addr.get()); +} + +static void in_band_ring_cb(const RawAddress* bd_addr, + bthf_client_in_band_ring_state_t in_band) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onInBandRing, + (jint)in_band, addr.get()); +} + +static void last_voice_tag_number_cb(const RawAddress* bd_addr, + const char* number) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + 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; + } + + ScopedLocalRef js_number(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(number)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onLastVoiceTagNumber, + js_number.get(), addr.get()); +} + +static void ring_indication_cb(const RawAddress* bd_addr) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRingIndication, + addr.get()); +} + +static void unknown_event_cb(const RawAddress* bd_addr, + const char* eventString) { + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) return; + + ScopedLocalRef js_event(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(eventString)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onUnknownEvent, + js_event.get(), addr.get()); +} + +static bthf_client_callbacks_t sBluetoothHfpClientCallbacks = { + sizeof(sBluetoothHfpClientCallbacks), + connection_state_cb, + audio_state_cb, + vr_cmd_cb, + network_state_cb, + network_roaming_cb, + network_signal_cb, + battery_level_cb, + current_operator_cb, + call_cb, + callsetup_cb, + callheld_cb, + resp_and_hold_cb, + clip_cb, + call_waiting_cb, + current_calls_cb, + volume_change_cb, + cmd_complete_cb, + subscriber_info_cb, + in_band_ring_cb, + last_voice_tag_number_cb, + ring_indication_cb, + unknown_event_cb, +}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "(III[B)V"); + method_onAudioStateChanged = + env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); + method_onVrStateChanged = + env->GetMethodID(clazz, "onVrStateChanged", "(I[B)V"); + method_onNetworkState = env->GetMethodID(clazz, "onNetworkState", "(I[B)V"); + method_onNetworkRoaming = env->GetMethodID(clazz, "onNetworkRoaming", "(I[B)V"); + method_onNetworkSignal = env->GetMethodID(clazz, "onNetworkSignal", "(I[B)V"); + method_onBatteryLevel = env->GetMethodID(clazz, "onBatteryLevel", "(I[B)V"); + method_onCurrentOperator = + env->GetMethodID(clazz, "onCurrentOperator", "(Ljava/lang/String;[B)V"); + method_onCall = env->GetMethodID(clazz, "onCall", "(I[B)V"); + method_onCallSetup = env->GetMethodID(clazz, "onCallSetup", "(I[B)V"); + method_onCallHeld = env->GetMethodID(clazz, "onCallHeld", "(I[B)V"); + method_onRespAndHold = env->GetMethodID(clazz, "onRespAndHold", "(I[B)V"); + method_onClip = env->GetMethodID(clazz, "onClip", "(Ljava/lang/String;[B)V"); + method_onCallWaiting = + env->GetMethodID(clazz, "onCallWaiting", "(Ljava/lang/String;[B)V"); + method_onCurrentCalls = + env->GetMethodID(clazz, "onCurrentCalls", "(IIIILjava/lang/String;[B)V"); + method_onVolumeChange = env->GetMethodID(clazz, "onVolumeChange", "(II[B)V"); + method_onCmdResult = env->GetMethodID(clazz, "onCmdResult", "(II[B)V"); + method_onSubscriberInfo = + env->GetMethodID(clazz, "onSubscriberInfo", "(Ljava/lang/String;I[B)V"); + method_onInBandRing = env->GetMethodID(clazz, "onInBandRing", "(I[B)V"); + method_onLastVoiceTagNumber = + env->GetMethodID(clazz, "onLastVoiceTagNumber", "(Ljava/lang/String;[B)V"); + method_onRingIndication = env->GetMethodID(clazz, "onRingIndication", "([B)V"); + method_onUnknownEvent = + env->GetMethodID(clazz, "onUnknownEvent", "(Ljava/lang/String;[B)V"); + + ALOGI("%s succeeds", __func__); +} + +static void initializeNative(JNIEnv* env, jobject object) { + ALOGD("%s: HfpClient", __func__); + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothHfpClientInterface != NULL) { + ALOGW("Cleaning up Bluetooth HFP Client Interface before initializing"); + sBluetoothHfpClientInterface->cleanup(); + sBluetoothHfpClientInterface = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth HFP Client callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + sBluetoothHfpClientInterface = + (bthf_client_interface_t*)btInf->get_profile_interface( + BT_PROFILE_HANDSFREE_CLIENT_ID); + if (sBluetoothHfpClientInterface == NULL) { + ALOGE("Failed to get Bluetooth HFP Client Interface"); + return; + } + + bt_status_t status = + sBluetoothHfpClientInterface->init(&sBluetoothHfpClientCallbacks); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to initialize Bluetooth HFP Client, status: %d", status); + sBluetoothHfpClientInterface = NULL; + return; + } + + mCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothHfpClientInterface != NULL) { + ALOGW("Cleaning up Bluetooth HFP Client Interface..."); + sBluetoothHfpClientInterface->cleanup(); + sBluetoothHfpClientInterface = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth HFP Client callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } +} + +static jboolean connectNative(JNIEnv* env, jobject object, jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->connect((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed AG connection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean disconnectNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = + sBluetoothHfpClientInterface->disconnect((const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed AG disconnection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean connectAudioNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = + sBluetoothHfpClientInterface->connect_audio((const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed AG audio connection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean disconnectAudioNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = + sBluetoothHfpClientInterface->disconnect_audio((const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed AG audio disconnection, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean startVoiceRecognitionNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->start_voice_recognition( + (const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to start voice recognition, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean stopVoiceRecognitionNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->stop_voice_recognition( + (const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to stop voice recognition, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setVolumeNative(JNIEnv* env, jobject object, jbyteArray address, + jint volume_type, jint volume) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->volume_control( + (const RawAddress*)addr, (bthf_client_volume_type_t)volume_type, volume); + if (status != BT_STATUS_SUCCESS) { + ALOGE("FAILED to control volume, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean dialNative(JNIEnv* env, jobject object, jbyteArray address, + jstring number_str) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + const char* number = nullptr; + if (number_str != nullptr) { + number = env->GetStringUTFChars(number_str, nullptr); + } + bt_status_t status = + sBluetoothHfpClientInterface->dial((const RawAddress*)addr, + number == nullptr ? "" : number); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to dial, status: %d", status); + } + if (number != nullptr) { + env->ReleaseStringUTFChars(number_str, number); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean dialMemoryNative(JNIEnv* env, jobject object, + jbyteArray address, jint location) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->dial_memory( + (const RawAddress*)addr, (int)location); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to dial from memory, status: %d", status); + } + + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean handleCallActionNative(JNIEnv* env, jobject object, + jbyteArray address, jint action, + jint index) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->handle_call_action( + (const RawAddress*)addr, (bthf_client_call_action_t)action, (int)index); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to enter private mode, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean queryCurrentCallsNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->query_current_calls( + (const RawAddress*)addr); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to query current calls, status: %d", status); + } + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean queryCurrentOperatorNameNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = + sBluetoothHfpClientInterface->query_current_operator_name( + (const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to query current operator name, status: %d", status); + } + + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean retrieveSubscriberInfoNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->retrieve_subscriber_info( + (const RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to retrieve subscriber info, status: %d", status); + } + + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean sendDtmfNative(JNIEnv* env, jobject object, jbyteArray address, + jbyte code) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHfpClientInterface->send_dtmf( + (const RawAddress*)addr, (char)code); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to send DTMF, status: %d", status); + } + + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean requestLastVoiceTagNumberNative(JNIEnv* env, jobject object, + jbyteArray address) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + bt_status_t status = + sBluetoothHfpClientInterface->request_last_voice_tag_number( + (const RawAddress*)addr); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to request last Voice Tag number, status: %d", status); + } + + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean sendATCmdNative(JNIEnv* env, jobject object, jbyteArray address, + jint cmd, jint val1, jint val2, + jstring arg_str) { + std::shared_lock lock(interface_mutex); + if (!sBluetoothHfpClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + const char* arg = NULL; + if (arg_str != NULL) { + arg = env->GetStringUTFChars(arg_str, NULL); + } + + bt_status_t status = sBluetoothHfpClientInterface->send_at_cmd( + (const RawAddress*)addr, cmd, val1, val2, arg); + + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to send cmd, status: %d", status); + } + + if (arg != NULL) { + env->ReleaseStringUTFChars(arg_str, arg); + } + + env->ReleaseByteArrayElements(address, addr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNative", "()V", (void*)initializeNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectNative", "([B)Z", (void*)connectNative}, + {"disconnectNative", "([B)Z", (void*)disconnectNative}, + {"connectAudioNative", "([B)Z", (void*)connectAudioNative}, + {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative}, + {"startVoiceRecognitionNative", "([B)Z", + (void*)startVoiceRecognitionNative}, + {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative}, + {"setVolumeNative", "([BII)Z", (void*)setVolumeNative}, + {"dialNative", "([BLjava/lang/String;)Z", (void*)dialNative}, + {"dialMemoryNative", "([BI)Z", (void*)dialMemoryNative}, + {"handleCallActionNative", "([BII)Z", (void*)handleCallActionNative}, + {"queryCurrentCallsNative", "([B)Z", (void*)queryCurrentCallsNative}, + {"queryCurrentOperatorNameNative", "([B)Z", + (void*)queryCurrentOperatorNameNative}, + {"retrieveSubscriberInfoNative", "([B)Z", + (void*)retrieveSubscriberInfoNative}, + {"sendDtmfNative", "([BB)Z", (void*)sendDtmfNative}, + {"requestLastVoiceTagNumberNative", "([B)Z", + (void*)requestLastVoiceTagNumberNative}, + {"sendATCmdNative", "([BIIILjava/lang/String;)Z", (void*)sendATCmdNative}, +}; + +int register_com_android_bluetooth_hfpclient(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/hfpclient/NativeInterface", + sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/android/app/jni/com_android_bluetooth_hid_device.cpp b/android/app/jni/com_android_bluetooth_hid_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17b6e97c015aed4495e7beb70bf3b971078c61a4 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_hid_device.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BluetoothHidDeviceServiceJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_hd.h" +#include "utils/Log.h" + +#include + +namespace android { + +static jmethodID method_onApplicationStateChanged; +static jmethodID method_onConnectStateChanged; +static jmethodID method_onGetReport; +static jmethodID method_onSetReport; +static jmethodID method_onSetProtocol; +static jmethodID method_onInterruptData; +static jmethodID method_onVirtualCableUnplug; + +static const bthd_interface_t* sHiddIf = NULL; +static jobject mCallbacksObj = NULL; + +static jbyteArray marshall_bda(RawAddress* bd_addr) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return NULL; + + jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); + if (!addr) { + ALOGE("Fail to new jbyteArray bd addr"); + return NULL; + } + sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), + (jbyte*)bd_addr); + return addr; +} + +static void application_state_callback(RawAddress* bd_addr, + bthd_application_state_t state) { + jboolean registered = JNI_FALSE; + + CallbackEnv sCallbackEnv(__func__); + + if (state == BTHD_APP_STATE_REGISTERED) { + registered = JNI_TRUE; + } + + ScopedLocalRef addr(sCallbackEnv.get(), NULL); + + if (bd_addr) { + addr.reset(marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("%s: failed to allocate storage for bt_addr", __FUNCTION__); + return; + } + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onApplicationStateChanged, + addr.get(), registered); +} + +static void connection_state_callback(RawAddress* bd_addr, + bthd_connection_state_t state) { + CallbackEnv sCallbackEnv(__func__); + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("%s: failed to allocate storage for bt_addr", __FUNCTION__); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, + addr.get(), (jint)state); +} + +static void get_report_callback(uint8_t type, uint8_t id, + uint16_t buffer_size) { + CallbackEnv sCallbackEnv(__func__); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, type, id, + buffer_size); +} + +static void set_report_callback(uint8_t type, uint8_t id, uint16_t len, + uint8_t* p_data) { + CallbackEnv sCallbackEnv(__func__); + + ScopedLocalRef data(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(len)); + if (!data.get()) { + ALOGE("%s: failed to allocate storage for report data", __FUNCTION__); + return; + } + sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetReport, (jbyte)type, + (jbyte)id, data.get()); +} + +static void set_protocol_callback(uint8_t protocol) { + CallbackEnv sCallbackEnv(__func__); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetProtocol, protocol); +} + +static void intr_data_callback(uint8_t report_id, uint16_t len, + uint8_t* p_data) { + CallbackEnv sCallbackEnv(__func__); + + ScopedLocalRef data(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(len)); + if (!data.get()) { + ALOGE("%s: failed to allocate storage for report data", __FUNCTION__); + return; + } + sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data); + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onInterruptData, + (jbyte)report_id, data.get()); +} + +static void vc_unplug_callback(void) { + CallbackEnv sCallbackEnv(__func__); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualCableUnplug); +} + +static bthd_callbacks_t sHiddCb = { + sizeof(sHiddCb), + + application_state_callback, + connection_state_callback, + get_report_callback, + set_report_callback, + set_protocol_callback, + intr_data_callback, + vc_unplug_callback, +}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + ALOGV("%s: done", __FUNCTION__); + + method_onApplicationStateChanged = + env->GetMethodID(clazz, "onApplicationStateChanged", "([BZ)V"); + method_onConnectStateChanged = + env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V"); + method_onGetReport = env->GetMethodID(clazz, "onGetReport", "(BBS)V"); + method_onSetReport = env->GetMethodID(clazz, "onSetReport", "(BB[B)V"); + method_onSetProtocol = env->GetMethodID(clazz, "onSetProtocol", "(B)V"); + method_onInterruptData = env->GetMethodID(clazz, "onInterruptData", "(B[B)V"); + method_onVirtualCableUnplug = + env->GetMethodID(clazz, "onVirtualCableUnplug", "()V"); +} + +static void initNative(JNIEnv* env, jobject object) { + const bt_interface_t* btif; + bt_status_t status; + + ALOGV("%s enter", __FUNCTION__); + + if ((btif = getBluetoothInterface()) == NULL) { + ALOGE("Cannot obtain BT interface"); + return; + } + + if (sHiddIf != NULL) { + ALOGW("Cleaning up interface"); + sHiddIf->cleanup(); + sHiddIf = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + if ((sHiddIf = (bthd_interface_t*)btif->get_profile_interface( + BT_PROFILE_HIDDEV_ID)) == NULL) { + ALOGE("Cannot obtain interface"); + return; + } + + if ((status = sHiddIf->init(&sHiddCb)) != BT_STATUS_SUCCESS) { + ALOGE("Failed to initialize interface (%d)", status); + sHiddIf = NULL; + return; + } + + mCallbacksObj = env->NewGlobalRef(object); + + ALOGV("%s done", __FUNCTION__); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + ALOGV("%s enter", __FUNCTION__); + + if (sHiddIf != NULL) { + ALOGI("Cleaning up interface"); + sHiddIf->cleanup(); + sHiddIf = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGI("Cleaning up callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + ALOGV("%s done", __FUNCTION__); +} + +static void fill_qos(JNIEnv* env, jintArray in, bthd_qos_param_t* out) { + // set default values + out->service_type = 0x01; // best effort + out->token_rate = out->token_bucket_size = out->peak_bandwidth = + 0; // don't care + out->access_latency = out->delay_variation = 0xffffffff; // don't care + + if (in == NULL) return; + + jsize len = env->GetArrayLength(in); + + if (len != 6) return; + + uint32_t* buf = (uint32_t*)calloc(len, sizeof(uint32_t)); + + if (buf == NULL) return; + + env->GetIntArrayRegion(in, 0, len, (jint*)buf); + + out->service_type = (uint8_t)buf[0]; + out->token_rate = buf[1]; + out->token_bucket_size = buf[2]; + out->peak_bandwidth = buf[3]; + out->access_latency = buf[4]; + out->delay_variation = buf[5]; + + free(buf); +} + +static jboolean registerAppNative(JNIEnv* env, jobject thiz, jstring name, + jstring description, jstring provider, + jbyte subclass, jbyteArray descriptors, + jintArray p_in_qos, jintArray p_out_qos) { + ALOGV("%s enter", __FUNCTION__); + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jboolean result = JNI_FALSE; + bthd_app_param_t app_param; + bthd_qos_param_t in_qos; + bthd_qos_param_t out_qos; + jsize size; + uint8_t* data; + + size = env->GetArrayLength(descriptors); + data = (uint8_t*)malloc(size); + + if (data != NULL) { + env->GetByteArrayRegion(descriptors, 0, size, (jbyte*)data); + + app_param.name = env->GetStringUTFChars(name, NULL); + app_param.description = env->GetStringUTFChars(description, NULL); + app_param.provider = env->GetStringUTFChars(provider, NULL); + app_param.subclass = subclass; + app_param.desc_list = data; + app_param.desc_list_len = size; + + fill_qos(env, p_in_qos, &in_qos); + fill_qos(env, p_out_qos, &out_qos); + + bt_status_t ret = sHiddIf->register_app(&app_param, &in_qos, &out_qos); + + ALOGV("%s: register_app() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + env->ReleaseStringUTFChars(name, app_param.name); + env->ReleaseStringUTFChars(description, app_param.description); + env->ReleaseStringUTFChars(provider, app_param.provider); + + free(data); + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static jboolean unregisterAppNative(JNIEnv* env, jobject thiz) { + ALOGV("%s enter", __FUNCTION__); + + jboolean result = JNI_FALSE; + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + bt_status_t ret = sHiddIf->unregister_app(); + + ALOGV("%s: unregister_app() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static jboolean sendReportNative(JNIEnv* env, jobject thiz, jint id, + jbyteArray data) { + jboolean result = JNI_FALSE; + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jsize size; + uint8_t* buf; + + size = env->GetArrayLength(data); + buf = (uint8_t*)malloc(size); + + if (buf != NULL) { + env->GetByteArrayRegion(data, 0, size, (jbyte*)buf); + + bt_status_t ret = + sHiddIf->send_report(BTHD_REPORT_TYPE_INTRDATA, id, size, buf); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + free(buf); + } + + return result; +} + +static jboolean replyReportNative(JNIEnv* env, jobject thiz, jbyte type, + jbyte id, jbyteArray data) { + ALOGV("%s enter", __FUNCTION__); + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jboolean result = JNI_FALSE; + jsize size; + uint8_t* buf; + + size = env->GetArrayLength(data); + buf = (uint8_t*)malloc(size); + + if (buf != NULL) { + int report_type = (type & 0x03); + env->GetByteArrayRegion(data, 0, size, (jbyte*)buf); + + bt_status_t ret = + sHiddIf->send_report((bthd_report_type_t)report_type, id, size, buf); + + ALOGV("%s: send_report() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + free(buf); + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static jboolean reportErrorNative(JNIEnv* env, jobject thiz, jbyte error) { + ALOGV("%s enter", __FUNCTION__); + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jboolean result = JNI_FALSE; + + bt_status_t ret = sHiddIf->report_error(error); + + ALOGV("%s: report_error() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static jboolean unplugNative(JNIEnv* env, jobject thiz) { + ALOGV("%s enter", __FUNCTION__); + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jboolean result = JNI_FALSE; + + bt_status_t ret = sHiddIf->virtual_cable_unplug(); + + ALOGV("%s: virtual_cable_unplug() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static jboolean connectNative(JNIEnv* env, jobject thiz, jbyteArray address) { + ALOGV("%s enter", __FUNCTION__); + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jboolean result = JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + bt_status_t ret = sHiddIf->connect((RawAddress*)addr); + + ALOGV("%s: connect() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static jboolean disconnectNative(JNIEnv* env, jobject thiz) { + ALOGV("%s enter", __FUNCTION__); + + if (!sHiddIf) { + ALOGE("%s: Failed to get the Bluetooth HIDD Interface", __func__); + return JNI_FALSE; + } + + jboolean result = JNI_FALSE; + + bt_status_t ret = sHiddIf->disconnect(); + + ALOGV("%s: disconnect() returned %d", __FUNCTION__, ret); + + if (ret == BT_STATUS_SUCCESS) { + result = JNI_TRUE; + } + + ALOGV("%s done (%d)", __FUNCTION__, result); + + return result; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"registerAppNative", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z", + (void*)registerAppNative}, + {"unregisterAppNative", "()Z", (void*)unregisterAppNative}, + {"sendReportNative", "(I[B)Z", (void*)sendReportNative}, + {"replyReportNative", "(BB[B)Z", (void*)replyReportNative}, + {"reportErrorNative", "(B)Z", (void*)reportErrorNative}, + {"unplugNative", "()Z", (void*)unplugNative}, + {"connectNative", "([B)Z", (void*)connectNative}, + {"disconnectNative", "()Z", (void*)disconnectNative}, +}; + +int register_com_android_bluetooth_hid_device(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/hid/HidDeviceNativeInterface", sMethods, + NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_hid_host.cpp b/android/app/jni/com_android_bluetooth_hid_host.cpp new file mode 100644 index 0000000000000000000000000000000000000000..074e39d5b024c921ca0c4ff3451cad732b40ded3 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_hid_host.cpp @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothHidHostServiceJni" + +#define LOG_NDEBUG 1 + +#include "com_android_bluetooth.h" +#include "hardware/bt_hh.h" +#include "utils/Log.h" + +#include +#include +namespace android { + +static jmethodID method_onConnectStateChanged; +static jmethodID method_onGetProtocolMode; +static jmethodID method_onGetReport; +static jmethodID method_onHandshake; +static jmethodID method_onVirtualUnplug; +static jmethodID method_onGetIdleTime; + +static const bthh_interface_t* sBluetoothHidInterface = NULL; +static jobject mCallbacksObj = NULL; +static std::shared_timed_mutex mCallbacks_mutex; + +static jbyteArray marshall_bda(RawAddress* bd_addr) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return NULL; + + jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); + if (!addr) { + ALOGE("Fail to new jbyteArray bd addr"); + return NULL; + } + sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), + (jbyte*)bd_addr); + return addr; +} + +static void connection_state_callback(RawAddress* bd_addr, + bthh_connection_state_t state) { + std::shared_lock lock(mCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!mCallbacksObj) { + ALOGE("%s: mCallbacksObj is null", __func__); + return; + } + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for HID channel state"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, + addr.get(), (jint)state); +} + +static void get_protocol_mode_callback(RawAddress* bd_addr, + bthh_status_t hh_status, + bthh_protocol_mode_t mode) { + std::shared_lock lock(mCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!mCallbacksObj) { + ALOGE("%s: mCallbacksObj is null", __func__); + return; + } + if (hh_status != BTHH_OK) { + ALOGE("BTHH Status is not OK!"); + return; + } + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for get protocal mode callback"); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetProtocolMode, + addr.get(), (jint)mode); +} + +static void get_report_callback(RawAddress* bd_addr, bthh_status_t hh_status, + uint8_t* rpt_data, int rpt_size) { + std::shared_lock lock(mCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!mCallbacksObj) { + ALOGE("%s: mCallbacksObj is null", __func__); + return; + } + if (hh_status != BTHH_OK) { + ALOGE("BTHH Status is not OK!"); + return; + } + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for get report callback"); + return; + } + ScopedLocalRef data(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(rpt_size)); + if (!data.get()) { + ALOGE("Fail to new jbyteArray data for get report callback"); + return; + } + + sCallbackEnv->SetByteArrayRegion(data.get(), 0, rpt_size, (jbyte*)rpt_data); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, addr.get(), + data.get(), (jint)rpt_size); +} + +static void virtual_unplug_callback(RawAddress* bd_addr, + bthh_status_t hh_status) { + ALOGV("call to virtual_unplug_callback"); + std::shared_lock lock(mCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!mCallbacksObj) { + ALOGE("%s: mCallbacksObj is null", __func__); + return; + } + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for HID channel state"); + return; + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug, + addr.get(), (jint)hh_status); +} + +static void handshake_callback(RawAddress* bd_addr, bthh_status_t hh_status) { + std::shared_lock lock(mCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + if (!mCallbacksObj) { + ALOGE("%s: mCallbacksObj is null", __func__); + return; + } + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("Fail to new jbyteArray bd addr for handshake callback"); + return; + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHandshake, addr.get(), + (jint)hh_status); +} + +static void get_idle_time_callback(RawAddress* bd_addr, bthh_status_t hh_status, + int idle_time) { + std::shared_lock lock(mCallbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + ALOGE("%s: Fail to new jbyteArray bd addr", __func__); + return; + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetIdleTime, addr.get(), + (jint)idle_time); +} + +static bthh_callbacks_t sBluetoothHidCallbacks = { + sizeof(sBluetoothHidCallbacks), + connection_state_callback, + NULL, + get_protocol_mode_callback, + get_idle_time_callback, + get_report_callback, + virtual_unplug_callback, + handshake_callback}; + +// Define native functions + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectStateChanged = + env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V"); + method_onGetProtocolMode = + env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V"); + method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V"); + method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V"); + method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V"); + method_onGetIdleTime = env->GetMethodID(clazz, "onGetIdleTime", "([BI)V"); + + ALOGI("%s: succeeds", __func__); +} + +static void initializeNative(JNIEnv* env, jobject object) { + std::unique_lock lock(mCallbacks_mutex); + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothHidInterface != NULL) { + ALOGW("Cleaning up Bluetooth HID Interface before initializing..."); + sBluetoothHidInterface->cleanup(); + sBluetoothHidInterface = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth GID callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + sBluetoothHidInterface = + (bthh_interface_t*)btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID); + if (sBluetoothHidInterface == NULL) { + ALOGE("Failed to get Bluetooth HID Interface"); + return; + } + + bt_status_t status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed to initialize Bluetooth HID, status: %d", status); + sBluetoothHidInterface = NULL; + return; + } + + mCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock lock(mCallbacks_mutex); + const bt_interface_t* btInf = getBluetoothInterface(); + + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothHidInterface != NULL) { + ALOGW("Cleaning up Bluetooth HID Interface..."); + sBluetoothHidInterface->cleanup(); + sBluetoothHidInterface = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth GID callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } +} + +static jboolean connectHidNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + jboolean ret = JNI_TRUE; + bt_status_t status = sBluetoothHidInterface->connect((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS && status != BT_STATUS_BUSY) { + ALOGE("Failed HID channel connection, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean disconnectHidNative(JNIEnv* env, jobject object, + jbyteArray address) { + jbyte* addr; + jboolean ret = JNI_TRUE; + if (!sBluetoothHidInterface) return JNI_FALSE; + + addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHidInterface->disconnect((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed disconnect hid channel, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean getProtocolModeNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + jboolean ret = JNI_TRUE; + // TODO: protocolMode is unused by the backend: see b/28908173 + bthh_protocol_mode_t protocolMode = BTHH_UNSUPPORTED_MODE; + bt_status_t status = sBluetoothHidInterface->get_protocol( + (RawAddress*)addr, (bthh_protocol_mode_t)protocolMode); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed get protocol mode, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean virtualUnPlugNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + jboolean ret = JNI_TRUE; + bt_status_t status = + sBluetoothHidInterface->virtual_unplug((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed virual unplug, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + return ret; +} + +static jboolean setProtocolModeNative(JNIEnv* env, jobject object, + jbyteArray address, jint protocolMode) { + if (!sBluetoothHidInterface) return JNI_FALSE; + + ALOGD("%s: protocolMode = %d", __func__, protocolMode); + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + bthh_protocol_mode_t mode; + switch (protocolMode) { + case 0: + mode = BTHH_REPORT_MODE; + break; + case 1: + mode = BTHH_BOOT_MODE; + break; + default: + ALOGE("Unknown HID protocol mode"); + return JNI_FALSE; + } + + jboolean ret = JNI_TRUE; + bt_status_t status = + sBluetoothHidInterface->set_protocol((RawAddress*)addr, mode); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed set protocol mode, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean getReportNative(JNIEnv* env, jobject object, jbyteArray address, + jbyte reportType, jbyte reportId, + jint bufferSize) { + ALOGV("%s: reportType = %d, reportId = %d, bufferSize = %d", __func__, + reportType, reportId, bufferSize); + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + jint rType = reportType; + jint rId = reportId; + + bt_status_t status = sBluetoothHidInterface->get_report( + (RawAddress*)addr, (bthh_report_type_t)rType, (uint8_t)rId, bufferSize); + jboolean ret = JNI_TRUE; + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed get report, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean setReportNative(JNIEnv* env, jobject object, jbyteArray address, + jbyte reportType, jstring report) { + ALOGV("%s: reportType = %d", __func__, reportType); + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + jint rType = reportType; + const char* c_report = env->GetStringUTFChars(report, NULL); + + jboolean ret = JNI_TRUE; + bt_status_t status = sBluetoothHidInterface->set_report( + (RawAddress*)addr, (bthh_report_type_t)rType, (char*)c_report); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed set report, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseStringUTFChars(report, c_report); + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean sendDataNative(JNIEnv* env, jobject object, jbyteArray address, + jstring report) { + ALOGV("%s", __func__); + jboolean ret = JNI_TRUE; + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("Bluetooth device address null"); + return JNI_FALSE; + } + + const char* c_report = env->GetStringUTFChars(report, NULL); + + bt_status_t status = + sBluetoothHidInterface->send_data((RawAddress*)addr, (char*)c_report); + if (status != BT_STATUS_SUCCESS) { + ALOGE("Failed set data, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseStringUTFChars(report, c_report); + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean getIdleTimeNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("%s: Bluetooth device address null", __func__); + return JNI_FALSE; + } + + bt_status_t status = sBluetoothHidInterface->get_idle_time((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed get idle time, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + + return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE; +} + +static jboolean setIdleTimeNative(JNIEnv* env, jobject object, + jbyteArray address, jbyte idle_time) { + if (!sBluetoothHidInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + ALOGE("%s: Bluetooth device address null", __func__); + return JNI_FALSE; + } + + bt_status_t status = + sBluetoothHidInterface->set_idle_time((RawAddress*)addr, idle_time); + if (status != BT_STATUS_SUCCESS) { + ALOGE("%s: Failed set idle time, status: %d", __func__, status); + } + env->ReleaseByteArrayElements(address, addr, 0); + + return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNative", "()V", (void*)initializeNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectHidNative", "([B)Z", (void*)connectHidNative}, + {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative}, + {"getProtocolModeNative", "([B)Z", (void*)getProtocolModeNative}, + {"virtualUnPlugNative", "([B)Z", (void*)virtualUnPlugNative}, + {"setProtocolModeNative", "([BB)Z", (void*)setProtocolModeNative}, + {"getReportNative", "([BBBI)Z", (void*)getReportNative}, + {"setReportNative", "([BBLjava/lang/String;)Z", (void*)setReportNative}, + {"sendDataNative", "([BLjava/lang/String;)Z", (void*)sendDataNative}, + {"getIdleTimeNative", "([B)Z", (void*)getIdleTimeNative}, + {"setIdleTimeNative", "([BB)Z", (void*)setIdleTimeNative}, +}; + +int register_com_android_bluetooth_hid_host(JNIEnv* env) { + return jniRegisterNativeMethods(env, + "com/android/bluetooth/hid/HidHostService", + sMethods, NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..659a694065ea7e53f2faef4fe80037d49c4a54ee --- /dev/null +++ b/android/app/jni/com_android_bluetooth_le_audio.cpp @@ -0,0 +1,289 @@ +/* Copyright 2019 HIMSA II K/S - www.himsa.com + * Represented by EHIMA - www.ehima.com + * + * 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. + */ + +#define LOG_TAG "BluetoothLeAudioServiceJni" + +#define LOG_NDEBUG 0 + +#include + +#include +#include +#include + +#include "com_android_bluetooth.h" +#include "hardware/bt_le_audio.h" + +using bluetooth::le_audio::ConnectionState; +using bluetooth::le_audio::GroupNodeStatus; +using bluetooth::le_audio::GroupStatus; +using bluetooth::le_audio::LeAudioClientCallbacks; +using bluetooth::le_audio::LeAudioClientInterface; + +namespace android { +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onGroupStatus; +static jmethodID method_onGroupNodeStatus; +static jmethodID method_onAudioConf; + +static LeAudioClientInterface* sLeAudioClientInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks { + public: + ~LeAudioClientCallbacksImpl() = default; + + void OnConnectionState(ConnectionState state, + const RawAddress& bd_addr) override { + LOG(INFO) << __func__ << ", state:" << int(state); + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + (jint)state, addr.get()); + } + + void OnGroupStatus(int group_id, GroupStatus group_status) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupStatus, + (jint)group_id, (jint)group_status); + } + + void OnGroupNodeStatus(const RawAddress& bd_addr, int group_id, + GroupNodeStatus node_status) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for group status"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupNodeStatus, + addr.get(), (jint)group_id, (jint)node_status); + } + + void OnAudioConf(uint8_t direction, int group_id, + uint32_t sink_audio_location, uint32_t source_audio_location, + uint16_t avail_cont) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConf, + (jint)direction, (jint)group_id, + (jint)sink_audio_location, + (jint)source_audio_location, (jint)avail_cont); + } +}; + +static LeAudioClientCallbacksImpl sLeAudioClientCallbacks; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onGroupStatus = env->GetMethodID(clazz, "onGroupStatus", "(II)V"); + method_onGroupNodeStatus = + env->GetMethodID(clazz, "onGroupNodeStatus", "([BII)V"); + method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIIII)V"); + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (mCallbacksObj != nullptr) { + LOG(INFO) << "Cleaning up LeAudio callback object"; + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + LOG(ERROR) << "Failed to allocate Global Ref for LeAudio Callbacks"; + return; + } + + sLeAudioClientInterface = + (LeAudioClientInterface*)btInf->get_profile_interface( + BT_PROFILE_LE_AUDIO_ID); + if (sLeAudioClientInterface == nullptr) { + LOG(ERROR) << "Failed to get Bluetooth LeAudio Interface"; + return; + } + + sLeAudioClientInterface->Initialize(&sLeAudioClientCallbacks); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sLeAudioClientInterface != nullptr) { + sLeAudioClientInterface->Cleanup(); + sLeAudioClientInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static jboolean connectLeAudioNative(JNIEnv* env, jobject object, + jbyteArray address) { + LOG(INFO) << __func__; + std::shared_lock lock(interface_mutex); + if (!sLeAudioClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sLeAudioClientInterface->Connect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static jboolean disconnectLeAudioNative(JNIEnv* env, jobject object, + jbyteArray address) { + LOG(INFO) << __func__; + std::shared_lock lock(interface_mutex); + if (!sLeAudioClientInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sLeAudioClientInterface->Disconnect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static jboolean groupAddNodeNative(JNIEnv* env, jobject object, jint group_id, + jbyteArray address) { + jbyte* addr = env->GetByteArrayElements(address, nullptr); + + if (!sLeAudioClientInterface) { + LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface"; + return JNI_FALSE; + } + + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sLeAudioClientInterface->GroupAddNode(group_id, *tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + + return JNI_TRUE; +} + +static jboolean groupRemoveNodeNative(JNIEnv* env, jobject object, + jint group_id, jbyteArray address) { + + if (!sLeAudioClientInterface) { + LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface"; + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sLeAudioClientInterface->GroupRemoveNode(group_id, *tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static void groupSetActiveNative(JNIEnv* env, jobject object, jint group_id) { + LOG(INFO) << __func__; + + if (!sLeAudioClientInterface) { + LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface"; + return; + } + + sLeAudioClientInterface->GroupSetActive(group_id); +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectLeAudioNative", "([B)Z", (void*)connectLeAudioNative}, + {"disconnectLeAudioNative", "([B)Z", (void*)disconnectLeAudioNative}, + {"groupAddNodeNative", "(I[B)Z", (void*)groupAddNodeNative}, + {"groupRemoveNodeNative", "(I[B)Z", (void*)groupRemoveNodeNative}, + {"groupSetActiveNative", "(I)V", (void*)groupSetActiveNative}, +}; + +int register_com_android_bluetooth_le_audio(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/le_audio/LeAudioNativeInterface", sMethods, + NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/com_android_bluetooth_pan.cpp b/android/app/jni/com_android_bluetooth_pan.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b02ae9f9b153100ea429be98ffaca8e0fadbb1f0 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_pan.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 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. + */ + +#define LOG_TAG "BluetoothPanServiceJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_pan.h" +#include "utils/Log.h" + +#include + +#include +#define info(fmt, ...) ALOGI("%s(L%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define debug(fmt, ...) \ + ALOGD("%s(L%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define warn(fmt, ...) \ + ALOGW("## WARNING : %s(L%d): " fmt "##", __func__, __LINE__, ##__VA_ARGS__) +#define error(fmt, ...) \ + ALOGE("## ERROR : %s(L%d): " fmt "##", __func__, __LINE__, ##__VA_ARGS__) +#define asrt(s) \ + if (!(s)) ALOGE("## %s(L%d): ASSERT %s failed! ##", __func__, __LINE__, #s) + +namespace android { + +static jmethodID method_onConnectStateChanged; +static jmethodID method_onControlStateChanged; + +static const btpan_interface_t* sPanIf = NULL; +static jobject mCallbacksObj = NULL; + +static jbyteArray marshall_bda(const RawAddress* bd_addr) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return NULL; + + jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); + if (!addr) { + ALOGE("Fail to new jbyteArray bd addr"); + return NULL; + } + sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), + (jbyte*)bd_addr); + return addr; +} + +static void control_state_callback(btpan_control_state_t state, int local_role, + bt_status_t error, const char* ifname) { + debug("state:%d, local_role:%d, ifname:%s", state, local_role, ifname); + if (mCallbacksObj == NULL) { + error("Callbacks Obj is NULL: '%s", __func__); + return; + } + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + ScopedLocalRef js_ifname(sCallbackEnv.get(), + sCallbackEnv->NewStringUTF(ifname)); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onControlStateChanged, + (jint)local_role, (jint)state, (jint)error, + js_ifname.get()); +} + +static void connection_state_callback(btpan_connection_state_t state, + bt_status_t error, + const RawAddress* bd_addr, int local_role, + int remote_role) { + debug("state:%d, local_role:%d, remote_role:%d", state, local_role, + remote_role); + if (mCallbacksObj == NULL) { + error("Callbacks Obj is NULL: '%s", __func__); + return; + } + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); + if (!addr.get()) { + error("Fail to new jbyteArray bd addr for PAN channel state"); + return; + } + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, + addr.get(), (jint)state, (jint)error, + (jint)local_role, (jint)remote_role); +} + +static btpan_callbacks_t sBluetoothPanCallbacks = { + sizeof(sBluetoothPanCallbacks), control_state_callback, + connection_state_callback}; + +// Define native functions + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectStateChanged = + env->GetMethodID(clazz, "onConnectStateChanged", "([BIIII)V"); + method_onControlStateChanged = env->GetMethodID( + clazz, "onControlStateChanged", "(IIILjava/lang/String;)V"); + + info("succeeds"); +} +static const bt_interface_t* btIf; + +static void initializeNative(JNIEnv* env, jobject object) { + debug("pan"); + if (btIf) return; + + btIf = getBluetoothInterface(); + if (btIf == NULL) { + error("Bluetooth module is not loaded"); + return; + } + + if (sPanIf != NULL) { + ALOGW("Cleaning up Bluetooth PAN Interface before initializing..."); + sPanIf->cleanup(); + sPanIf = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth PAN callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + sPanIf = (btpan_interface_t*)btIf->get_profile_interface(BT_PROFILE_PAN_ID); + if (sPanIf == NULL) { + error("Failed to get Bluetooth PAN Interface"); + return; + } + + mCallbacksObj = env->NewGlobalRef(object); + + bt_status_t status = sPanIf->init(&sBluetoothPanCallbacks); + if (status != BT_STATUS_SUCCESS) { + error("Failed to initialize Bluetooth PAN, status: %d", status); + sPanIf = NULL; + if (mCallbacksObj != NULL) { + ALOGW("initialization failed: Cleaning up Bluetooth PAN callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + return; + } +} + +static void cleanupNative(JNIEnv* env, jobject object) { + if (!btIf) return; + + if (sPanIf != NULL) { + ALOGW("Cleaning up Bluetooth PAN Interface..."); + sPanIf->cleanup(); + sPanIf = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth PAN callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + btIf = NULL; +} + +static jboolean connectPanNative(JNIEnv* env, jobject object, + jbyteArray address, jint src_role, + jint dest_role) { + debug("in"); + if (!sPanIf) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + error("Bluetooth device address null"); + return JNI_FALSE; + } + + jboolean ret = JNI_TRUE; + bt_status_t status = sPanIf->connect((RawAddress*)addr, src_role, dest_role); + if (status != BT_STATUS_SUCCESS) { + error("Failed PAN channel connection, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static jboolean disconnectPanNative(JNIEnv* env, jobject object, + jbyteArray address) { + if (!sPanIf) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (!addr) { + error("Bluetooth device address null"); + return JNI_FALSE; + } + + jboolean ret = JNI_TRUE; + bt_status_t status = sPanIf->disconnect((RawAddress*)addr); + if (status != BT_STATUS_SUCCESS) { + error("Failed disconnect pan channel, status: %d", status); + ret = JNI_FALSE; + } + env->ReleaseByteArrayElements(address, addr, 0); + + return ret; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNative", "()V", (void*)initializeNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectPanNative", "([BII)Z", (void*)connectPanNative}, + {"disconnectPanNative", "([B)Z", (void*)disconnectPanNative}, + // TBD cleanup +}; + +int register_com_android_bluetooth_pan(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/bluetooth/pan/PanService", + sMethods, NELEM(sMethods)); +} +} diff --git a/android/app/jni/com_android_bluetooth_sdp.cpp b/android/app/jni/com_android_bluetooth_sdp.cpp new file mode 100755 index 0000000000000000000000000000000000000000..bba109ac45609c04804ac91a4458f49f566a30ce --- /dev/null +++ b/android/app/jni/com_android_bluetooth_sdp.cpp @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BluetoothSdpJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_sdp.h" +#include "utils/Log.h" + +#include + +using bluetooth::Uuid; + +static const Uuid UUID_OBEX_OBJECT_PUSH = Uuid::From16Bit(0x1105); +static const Uuid UUID_PBAP_PSE = Uuid::From16Bit(0x112F); +static const Uuid UUID_MAP_MAS = Uuid::From16Bit(0x1132); +static const Uuid UUID_MAP_MNS = Uuid::From16Bit(0x1133); +static const Uuid UUID_SAP = Uuid::From16Bit(0x112D); +static const Uuid UUID_DIP = Uuid::From16Bit(0x1200); + +namespace android { +static jmethodID method_sdpRecordFoundCallback; +static jmethodID method_sdpMasRecordFoundCallback; +static jmethodID method_sdpMnsRecordFoundCallback; +static jmethodID method_sdpPseRecordFoundCallback; +static jmethodID method_sdpOppOpsRecordFoundCallback; +static jmethodID method_sdpSapsRecordFoundCallback; +static jmethodID method_sdpDipRecordFoundCallback; + +static const btsdp_interface_t* sBluetoothSdpInterface = NULL; + +static void sdp_search_callback(bt_status_t status, const RawAddress& bd_addr, + const Uuid& uuid_in, int record_size, + bluetooth_sdp_record* record); + +btsdp_callbacks_t sBluetoothSdpCallbacks = {sizeof(sBluetoothSdpCallbacks), + sdp_search_callback}; + +static jobject sCallbacksObj = NULL; + +static void initializeNative(JNIEnv* env, jobject object) { + const bt_interface_t* btInf = getBluetoothInterface(); + + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + if (sBluetoothSdpInterface != NULL) { + ALOGW("Cleaning up Bluetooth SDP Interface before initializing..."); + sBluetoothSdpInterface->deinit(); + sBluetoothSdpInterface = NULL; + } + + sBluetoothSdpInterface = (btsdp_interface_t*)btInf->get_profile_interface( + BT_PROFILE_SDP_CLIENT_ID); + if (sBluetoothSdpInterface == NULL) { + ALOGE("Error getting SDP client interface"); + } else { + sBluetoothSdpInterface->init(&sBluetoothSdpCallbacks); + } + + sCallbacksObj = env->NewGlobalRef(object); +} + +static void classInitNative(JNIEnv* env, jclass clazz) { + /* generic SDP record (raw data)*/ + method_sdpRecordFoundCallback = + env->GetMethodID(clazz, "sdpRecordFoundCallback", "(I[B[BI[B)V"); + + /* MAS SDP record*/ + method_sdpMasRecordFoundCallback = env->GetMethodID( + clazz, "sdpMasRecordFoundCallback", "(I[B[BIIIIIILjava/lang/String;Z)V"); + /* MNS SDP record*/ + method_sdpMnsRecordFoundCallback = env->GetMethodID( + clazz, "sdpMnsRecordFoundCallback", "(I[B[BIIIILjava/lang/String;Z)V"); + /* PBAP PSE record */ + method_sdpPseRecordFoundCallback = env->GetMethodID( + clazz, "sdpPseRecordFoundCallback", "(I[B[BIIIIILjava/lang/String;Z)V"); + /* OPP Server record */ + method_sdpOppOpsRecordFoundCallback = + env->GetMethodID(clazz, "sdpOppOpsRecordFoundCallback", + "(I[B[BIIILjava/lang/String;[BZ)V"); + /* SAP Server record */ + method_sdpSapsRecordFoundCallback = env->GetMethodID( + clazz, "sdpSapsRecordFoundCallback", "(I[B[BIILjava/lang/String;Z)V"); + /* DIP record */ + method_sdpDipRecordFoundCallback = env->GetMethodID( + clazz, "sdpDipRecordFoundCallback", "(I[B[BIIIIIZZ)V"); +} + +static jboolean sdpSearchNative(JNIEnv* env, jobject obj, jbyteArray address, + jbyteArray uuidObj) { + ALOGD("%s", __func__); + + if (!sBluetoothSdpInterface) return JNI_FALSE; + + jbyte* addr = env->GetByteArrayElements(address, NULL); + if (addr == NULL) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + jbyte* uuid = env->GetByteArrayElements(uuidObj, NULL); + if (!uuid) { + ALOGE("failed to get uuid"); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_FALSE; + } + ALOGD("%s UUID %.*s", __func__, 16, (uint8_t*)uuid); + + int ret = sBluetoothSdpInterface->sdp_search( + (RawAddress*)addr, Uuid::From128BitBE((uint8_t*)uuid)); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Search initialization failed: %d", ret); + } + + if (addr) env->ReleaseByteArrayElements(address, addr, 0); + if (uuid) env->ReleaseByteArrayElements(uuidObj, uuid, 0); + return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static void sdp_search_callback(bt_status_t status, const RawAddress& bd_addr, + const Uuid& uuid_in, int count, + bluetooth_sdp_record* records) { + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid()) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) return; + + ScopedLocalRef uuid(sCallbackEnv.get(), + sCallbackEnv->NewByteArray(sizeof(Uuid))); + if (!uuid.get()) return; + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (const jbyte*)&bd_addr); + sCallbackEnv->SetByteArrayRegion(uuid.get(), 0, sizeof(Uuid), + (const jbyte*)uuid_in.To128BitBE().data()); + + ALOGD("%s: Status is: %d, Record count: %d", __func__, status, count); + + // Ensure we run the loop at least once, to also signal errors if they occur + for (int i = 0; i < count || i == 0; i++) { + bool more_results = (i < (count - 1)) ? true : false; + bluetooth_sdp_record* record = &records[i]; + ScopedLocalRef service_name(sCallbackEnv.get(), NULL); + if (record->hdr.service_name_length > 0) { + ALOGD("%s, ServiceName: %s", __func__, record->mas.hdr.service_name); + service_name.reset( + (jstring)sCallbackEnv->NewStringUTF(record->mas.hdr.service_name)); + } + + /* call the right callback according to the uuid*/ + if (uuid_in == UUID_MAP_MAS) { + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_sdpMasRecordFoundCallback, (jint)status, + addr.get(), uuid.get(), (jint)record->mas.mas_instance_id, + (jint)record->mas.hdr.l2cap_psm, + (jint)record->mas.hdr.rfcomm_channel_number, + (jint)record->mas.hdr.profile_version, + (jint)record->mas.supported_features, + (jint)record->mas.supported_message_types, service_name.get(), + more_results); + + } else if (uuid_in == UUID_MAP_MNS) { + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_sdpMnsRecordFoundCallback, (jint)status, + addr.get(), uuid.get(), (jint)record->mns.hdr.l2cap_psm, + (jint)record->mns.hdr.rfcomm_channel_number, + (jint)record->mns.hdr.profile_version, + (jint)record->mns.supported_features, service_name.get(), + more_results); + + } else if (uuid_in == UUID_PBAP_PSE) { + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_sdpPseRecordFoundCallback, (jint)status, + addr.get(), uuid.get(), (jint)record->pse.hdr.l2cap_psm, + (jint)record->pse.hdr.rfcomm_channel_number, + (jint)record->pse.hdr.profile_version, + (jint)record->pse.supported_features, + (jint)record->pse.supported_repositories, service_name.get(), + more_results); + + } else if (uuid_in == UUID_OBEX_OBJECT_PUSH) { + jint formats_list_size = record->ops.supported_formats_list_len; + ScopedLocalRef formats_list( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(formats_list_size)); + if (!formats_list.get()) return; + sCallbackEnv->SetByteArrayRegion( + formats_list.get(), 0, formats_list_size, + (jbyte*)record->ops.supported_formats_list); + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_sdpOppOpsRecordFoundCallback, (jint)status, + addr.get(), uuid.get(), (jint)record->ops.hdr.l2cap_psm, + (jint)record->ops.hdr.rfcomm_channel_number, + (jint)record->ops.hdr.profile_version, service_name.get(), + formats_list.get(), more_results); + + } else if (uuid_in == UUID_SAP) { + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_sdpSapsRecordFoundCallback, (jint)status, + addr.get(), uuid.get(), (jint)record->mas.hdr.rfcomm_channel_number, + (jint)record->mas.hdr.profile_version, service_name.get(), + more_results); + } else if (uuid_in == UUID_DIP) { + ALOGD("%s, Get UUID_DIP", __func__); + sCallbackEnv->CallVoidMethod( + sCallbacksObj, method_sdpDipRecordFoundCallback, (jint)status, + addr.get(), uuid.get(), (jint)record->dip.spec_id, + (jint)record->dip.vendor, + (jint)record->dip.vendor_id_source, + (jint)record->dip.product, + (jint)record->dip.version, + record->dip.primary_record, + more_results); + } else { + // we don't have a wrapper for this uuid, send as raw data + jint record_data_size = record->hdr.user1_ptr_len; + ScopedLocalRef record_data( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(record_data_size)); + if (!record_data.get()) return; + + sCallbackEnv->SetByteArrayRegion(record_data.get(), 0, record_data_size, + (jbyte*)record->hdr.user1_ptr); + sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpRecordFoundCallback, + (jint)status, addr.get(), uuid.get(), + record_data_size, record_data.get()); + } + } // End of for-loop +} + +static jint sdpCreateMapMasRecordNative(JNIEnv* env, jobject obj, + jstring name_str, jint mas_id, jint scn, + jint l2cap_psm, jint version, + jint msg_types, jint features) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return -1; + + bluetooth_sdp_record record = {}; // Must be zero initialized + record.mas.hdr.type = SDP_TYPE_MAP_MAS; + + const char* service_name = NULL; + if (name_str != NULL) { + service_name = env->GetStringUTFChars(name_str, NULL); + record.mas.hdr.service_name = (char*)service_name; + record.mas.hdr.service_name_length = strlen(service_name); + } else { + record.mas.hdr.service_name = NULL; + record.mas.hdr.service_name_length = 0; + } + record.mas.hdr.rfcomm_channel_number = scn; + record.mas.hdr.l2cap_psm = l2cap_psm; + record.mas.hdr.profile_version = version; + + record.mas.mas_instance_id = mas_id; + record.mas.supported_features = features; + record.mas.supported_message_types = msg_types; + + int handle = -1; + int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Create record failed: %d", ret); + } else { + ALOGD("SDP Create record success - handle: %d", handle); + } + + if (service_name) env->ReleaseStringUTFChars(name_str, service_name); + return handle; +} + +static jint sdpCreateMapMnsRecordNative(JNIEnv* env, jobject obj, + jstring name_str, jint scn, + jint l2cap_psm, jint version, + jint features) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return -1; + + bluetooth_sdp_record record = {}; // Must be zero initialized + record.mns.hdr.type = SDP_TYPE_MAP_MNS; + + const char* service_name = NULL; + if (name_str != NULL) { + service_name = env->GetStringUTFChars(name_str, NULL); + record.mns.hdr.service_name = (char*)service_name; + record.mns.hdr.service_name_length = strlen(service_name); + } else { + record.mns.hdr.service_name = NULL; + record.mns.hdr.service_name_length = 0; + } + record.mns.hdr.rfcomm_channel_number = scn; + record.mns.hdr.l2cap_psm = l2cap_psm; + record.mns.hdr.profile_version = version; + + record.mns.supported_features = features; + + int handle = -1; + int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Create record failed: %d", ret); + } else { + ALOGD("SDP Create record success - handle: %d", handle); + } + + if (service_name) env->ReleaseStringUTFChars(name_str, service_name); + return handle; +} + +static jint sdpCreatePbapPceRecordNative(JNIEnv* env, jobject obj, + jstring name_str, jint version) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return -1; + + bluetooth_sdp_record record = {}; // Must be zero initialized + record.pce.hdr.type = SDP_TYPE_PBAP_PCE; + + const char* service_name = NULL; + if (name_str != NULL) { + service_name = env->GetStringUTFChars(name_str, NULL); + record.pce.hdr.service_name = (char*)service_name; + record.pce.hdr.service_name_length = strlen(service_name); + } else { + record.pce.hdr.service_name = NULL; + record.pce.hdr.service_name_length = 0; + } + record.pce.hdr.profile_version = version; + + int handle = -1; + int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Create record failed: %d", ret); + } else { + ALOGD("SDP Create record success - handle: %d", handle); + } + + if (service_name) env->ReleaseStringUTFChars(name_str, service_name); + return handle; +} + +static jint sdpCreatePbapPseRecordNative(JNIEnv* env, jobject obj, + jstring name_str, jint scn, + jint l2cap_psm, jint version, + jint supported_repositories, + jint features) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return -1; + + bluetooth_sdp_record record = {}; // Must be zero initialized + record.pse.hdr.type = SDP_TYPE_PBAP_PSE; + + const char* service_name = NULL; + if (name_str != NULL) { + service_name = env->GetStringUTFChars(name_str, NULL); + record.pse.hdr.service_name = (char*)service_name; + record.pse.hdr.service_name_length = strlen(service_name); + } else { + record.pse.hdr.service_name = NULL; + record.pse.hdr.service_name_length = 0; + } + record.pse.hdr.rfcomm_channel_number = scn; + record.pse.hdr.l2cap_psm = l2cap_psm; + record.pse.hdr.profile_version = version; + + record.pse.supported_features = features; + record.pse.supported_repositories = supported_repositories; + + int handle = -1; + int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Create record failed: %d", ret); + } else { + ALOGD("SDP Create record success - handle: %d", handle); + } + + if (service_name) env->ReleaseStringUTFChars(name_str, service_name); + return handle; +} + +static jint sdpCreateOppOpsRecordNative(JNIEnv* env, jobject obj, + jstring name_str, jint scn, + jint l2cap_psm, jint version, + jbyteArray supported_formats_list) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return -1; + + bluetooth_sdp_record record = {}; // Must be zero initialized + record.ops.hdr.type = SDP_TYPE_OPP_SERVER; + + const char* service_name = NULL; + if (name_str != NULL) { + service_name = env->GetStringUTFChars(name_str, NULL); + record.ops.hdr.service_name = (char*)service_name; + record.ops.hdr.service_name_length = strlen(service_name); + } else { + record.ops.hdr.service_name = NULL; + record.ops.hdr.service_name_length = 0; + } + record.ops.hdr.rfcomm_channel_number = scn; + record.ops.hdr.l2cap_psm = l2cap_psm; + record.ops.hdr.profile_version = version; + + int formats_list_len = 0; + jbyte* formats_list = env->GetByteArrayElements(supported_formats_list, NULL); + if (formats_list != NULL) { + formats_list_len = env->GetArrayLength(supported_formats_list); + if (formats_list_len > SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH) { + formats_list_len = SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH; + } + memcpy(record.ops.supported_formats_list, formats_list, formats_list_len); + } + + record.ops.supported_formats_list_len = formats_list_len; + + int handle = -1; + int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Create record failed: %d", ret); + } else { + ALOGD("SDP Create record success - handle: %d", handle); + } + + if (service_name) env->ReleaseStringUTFChars(name_str, service_name); + if (formats_list) + env->ReleaseByteArrayElements(supported_formats_list, formats_list, 0); + return handle; +} + +static jint sdpCreateSapsRecordNative(JNIEnv* env, jobject obj, + jstring name_str, jint scn, + jint version) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return -1; + + bluetooth_sdp_record record = {}; // Must be zero initialized + record.sap.hdr.type = SDP_TYPE_SAP_SERVER; + + const char* service_name = NULL; + if (name_str != NULL) { + service_name = env->GetStringUTFChars(name_str, NULL); + record.mas.hdr.service_name = (char*)service_name; + record.mas.hdr.service_name_length = strlen(service_name); + } else { + record.mas.hdr.service_name = NULL; + record.mas.hdr.service_name_length = 0; + } + record.mas.hdr.rfcomm_channel_number = scn; + record.mas.hdr.profile_version = version; + + int handle = -1; + int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Create record failed: %d", ret); + } else { + ALOGD("SDP Create record success - handle: %d", handle); + } + + if (service_name) env->ReleaseStringUTFChars(name_str, service_name); + return handle; +} + +static jboolean sdpRemoveSdpRecordNative(JNIEnv* env, jobject obj, + jint record_id) { + ALOGD("%s", __func__); + if (!sBluetoothSdpInterface) return false; + + int ret = sBluetoothSdpInterface->remove_sdp_record(record_id); + if (ret != BT_STATUS_SUCCESS) { + ALOGE("SDP Remove record failed: %d", ret); + return false; + } + + ALOGD("SDP Remove record success - handle: %d", record_id); + return true; +} + +static void cleanupNative(JNIEnv* env, jobject object) { + const bt_interface_t* btInf = getBluetoothInterface(); + + if (btInf == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothSdpInterface != NULL) { + ALOGW("Cleaning up Bluetooth SDP Interface..."); + sBluetoothSdpInterface->deinit(); + sBluetoothSdpInterface = NULL; + } + + if (sCallbacksObj != NULL) { + ALOGW("Cleaning up Bluetooth SDP object"); + env->DeleteGlobalRef(sCallbacksObj); + sCallbacksObj = NULL; + } +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNative", "()V", (void*)initializeNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"sdpSearchNative", "([B[B)Z", (void*)sdpSearchNative}, + {"sdpCreateMapMasRecordNative", "(Ljava/lang/String;IIIIII)I", + (void*)sdpCreateMapMasRecordNative}, + {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I", + (void*)sdpCreateMapMnsRecordNative}, + {"sdpCreatePbapPceRecordNative", "(Ljava/lang/String;I)I", + (void*)sdpCreatePbapPceRecordNative}, + {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I", + (void*)sdpCreatePbapPseRecordNative}, + {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I", + (void*)sdpCreateOppOpsRecordNative}, + {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I", + (void*)sdpCreateSapsRecordNative}, + {"sdpRemoveSdpRecordNative", "(I)Z", (void*)sdpRemoveSdpRecordNative}}; + +int register_com_android_bluetooth_sdp(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/bluetooth/sdp/SdpManager", + sMethods, NELEM(sMethods)); +} +} diff --git a/android/app/jni/com_android_bluetooth_vc.cpp b/android/app/jni/com_android_bluetooth_vc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce760c22065755737e88d5a158166095c7d38134 --- /dev/null +++ b/android/app/jni/com_android_bluetooth_vc.cpp @@ -0,0 +1,268 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ + +#define LOG_TAG "BluetoothVolumeControlServiceJni" + +#define LOG_NDEBUG 0 + +#include +#include + +#include "base/logging.h" +#include "com_android_bluetooth.h" +#include "hardware/bt_vc.h" + +using bluetooth::vc::ConnectionState; +using bluetooth::vc::VolumeControlCallbacks; +using bluetooth::vc::VolumeControlInterface; + +namespace android { +static jmethodID method_onConnectionStateChanged; +static jmethodID method_onVolumeStateChanged; +static jmethodID method_onGroupVolumeStateChanged; + +static VolumeControlInterface* sVolumeControlInterface = nullptr; +static std::shared_timed_mutex interface_mutex; + +static jobject mCallbacksObj = nullptr; +static std::shared_timed_mutex callbacks_mutex; + +class VolumeControlCallbacksImpl : public VolumeControlCallbacks { + public: + ~VolumeControlCallbacksImpl() = default; + void OnConnectionState(ConnectionState state, + const RawAddress& bd_addr) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, + (jint)state, addr.get()); + } + + void OnVolumeStateChanged(const RawAddress& bd_addr, uint8_t volume, + bool mute) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + ScopedLocalRef addr( + sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); + if (!addr.get()) { + LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state"; + return; + } + + sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), + (jbyte*)&bd_addr); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeStateChanged, + (jint)volume, (jboolean)mute, addr.get()); + } + + void OnGroupVolumeStateChanged(int group_id, uint8_t volume, + bool mute) override { + LOG(INFO) << __func__; + + std::shared_lock lock(callbacks_mutex); + CallbackEnv sCallbackEnv(__func__); + if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; + + sCallbackEnv->CallVoidMethod(mCallbacksObj, + method_onGroupVolumeStateChanged, (jint)volume, + (jboolean)mute, group_id); + } +}; + +static VolumeControlCallbacksImpl sVolumeControlCallbacks; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_onConnectionStateChanged = + env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); + + method_onVolumeStateChanged = + env->GetMethodID(clazz, "onVolumeStateChanged", "(IZ[B)V"); + + method_onGroupVolumeStateChanged = + env->GetMethodID(clazz, "onGroupVolumeStateChanged", "(IZI)V"); + + LOG(INFO) << __func__ << ": succeeds"; +} + +static void initNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sVolumeControlInterface != nullptr) { + LOG(INFO) << "Cleaning up VolumeControl Interface before initializing..."; + sVolumeControlInterface->Cleanup(); + sVolumeControlInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + LOG(INFO) << "Cleaning up VolumeControl callback object"; + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } + + if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + LOG(ERROR) << "Failed to allocate Global Ref for Volume control Callbacks"; + return; + } + + sVolumeControlInterface = + (VolumeControlInterface*)btInf->get_profile_interface(BT_PROFILE_VC_ID); + if (sVolumeControlInterface == nullptr) { + LOG(ERROR) << "Failed to get Bluetooth Volume Control Interface"; + return; + } + + sVolumeControlInterface->Init(&sVolumeControlCallbacks); +} + +static void cleanupNative(JNIEnv* env, jobject object) { + std::unique_lock interface_lock(interface_mutex); + std::unique_lock callbacks_lock(callbacks_mutex); + + const bt_interface_t* btInf = getBluetoothInterface(); + if (btInf == nullptr) { + LOG(ERROR) << "Bluetooth module is not loaded"; + return; + } + + if (sVolumeControlInterface != nullptr) { + sVolumeControlInterface->Cleanup(); + sVolumeControlInterface = nullptr; + } + + if (mCallbacksObj != nullptr) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = nullptr; + } +} + +static jboolean connectVolumeControlNative(JNIEnv* env, jobject object, + jbyteArray address) { + LOG(INFO) << __func__; + std::shared_lock lock(interface_mutex); + + if (!sVolumeControlInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Bluetooth Volume Control Interface"; + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sVolumeControlInterface->Connect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static jboolean disconnectVolumeControlNative(JNIEnv* env, jobject object, + jbyteArray address) { + LOG(INFO) << __func__; + std::shared_lock lock(interface_mutex); + + if (!sVolumeControlInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Bluetooth Volume Control Interface"; + return JNI_FALSE; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sVolumeControlInterface->Disconnect(*tmpraw); + env->ReleaseByteArrayElements(address, addr, 0); + return JNI_TRUE; +} + +static void setVolumeNative(JNIEnv* env, jobject object, jbyteArray address, + jint volume) { + if (!sVolumeControlInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Bluetooth Volume Control Interface"; + return; + } + + jbyte* addr = env->GetByteArrayElements(address, nullptr); + if (!addr) { + jniThrowIOException(env, EINVAL); + return; + } + + RawAddress* tmpraw = (RawAddress*)addr; + sVolumeControlInterface->SetVolume(*tmpraw, volume); + env->ReleaseByteArrayElements(address, addr, 0); +} + +static void setVolumeGroupNative(JNIEnv* env, jobject object, jint group_id, + jint volume) { + if (!sVolumeControlInterface) { + LOG(ERROR) << __func__ + << ": Failed to get the Bluetooth Volume Control Interface"; + return; + } + + sVolumeControlInterface->SetVolume(group_id, volume); +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void*)cleanupNative}, + {"connectVolumeControlNative", "([B)Z", (void*)connectVolumeControlNative}, + {"disconnectVolumeControlNative", "([B)Z", + (void*)disconnectVolumeControlNative}, + {"setVolumeNative", "([BI)V", (void*)setVolumeNative}, + {"setVolumeGroupNative", "(II)V", (void*)setVolumeGroupNative}, +}; + +int register_com_android_bluetooth_vc(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "com/android/bluetooth/vc/VolumeControlNativeInterface", sMethods, + NELEM(sMethods)); +} +} // namespace android diff --git a/android/app/jni/jni_logging.h b/android/app/jni/jni_logging.h new file mode 100644 index 0000000000000000000000000000000000000000..c2b9bd97d123e832c5a67af1d6f14c1e00d8986b --- /dev/null +++ b/android/app/jni/jni_logging.h @@ -0,0 +1,31 @@ +/* + * Copyright 2019 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. + */ +#pragma once + +#include + +/* Logging macros imported from android_runtime/Log.h + * + * Logs an exception. If the exception is omitted or NULL, logs the current + * exception from the JNI environment, if any. + */ +#define LOG_EX(env, priority, tag, ...) \ + jniLogException(env, ANDROID_##priority, tag, ##__VA_ARGS__) +#define LOGV_EX(env, ...) LOG_EX(env, LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__) +#define LOGD_EX(env, ...) LOG_EX(env, LOG_DEBUG, LOG_TAG, ##__VA_ARGS__) +#define LOGI_EX(env, ...) LOG_EX(env, LOG_INFO, LOG_TAG, ##__VA_ARGS__) +#define LOGW_EX(env, ...) LOG_EX(env, LOG_WARN, LOG_TAG, ##__VA_ARGS__) +#define LOGE_EX(env, ...) LOG_EX(env, LOG_ERROR, LOG_TAG, ##__VA_ARGS__) diff --git a/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapContract.java b/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapContract.java new file mode 100644 index 0000000000000000000000000000000000000000..5040bdc57eee9b2e610b040ed6fea209fd4e6d39 --- /dev/null +++ b/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapContract.java @@ -0,0 +1,1224 @@ +/* + * Copyright (C) 2013 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.mapapi; + +import android.content.ContentResolver; +import android.net.Uri; + + +/** + * This class defines the minimum sets of data needed for a client to + * implement to claim support for the Bluetooth Message Access Profile. + * Access to three data sets are needed: + *
    + *
  • Message data set containing lists of messages.
  • + *
  • Account data set containing info on the existing accounts, and whether to expose + * these accounts. The content of the account data set is often sensitive information, + * hence care must be taken, not to reveal any personal information nor passwords. + * The accounts in this data base will be exposed in the settings menu, where the user + * is able to enable and disable the EXPOSE_FLAG, and thereby provide access to an + * account from another device, without any password protection the e-mail client + * might provide.
  • + *
  • Folder data set with the folder structure for the messages. Each message is linked to an + * entry in this data set.
  • + *
  • Conversation data set with the thread structure of the messages. Each message is linked + * to an entry in this data set.
  • + *
+ * + * To enable that the Bluetooth Message Access Server can detect the content provider implementing + * this interface, the {@code provider} tag for the Bluetooth related content provider must + * have an intent-filter like the following in the manifest: + *
<provider  android:authorities="[PROVIDER AUTHORITY]"
+ *             android:exported="true"
+ *             android:enabled="true"
+ *             android:permission="android.permission.BLUETOOTH_MAP">
+ *   ...
+ *      <intent-filter>
+ *          <action android:name="android.content.action.BLEUETOOT_MAP_PROVIDER" />
+ *       </intent-filter>
+ *   ...
+ *   </provider>
+ * [PROVIDER AUTHORITY] shall be the providers authority value which implements this
+ * contract. Only a single authority shall be used. The android.permission.BLUETOOTH_MAP
+ * permission is needed for the provider.
+ */
+public final class BluetoothMapContract {
+    /**
+     * Constructor - should not be used
+     */
+    private BluetoothMapContract() {
+      /* class should not be instantiated */
+    }
+
+    /**
+     * Provider interface that should be used as intent-filter action in the provider section
+     * of the manifest file.
+     */
+    public static final String PROVIDER_INTERFACE_EMAIL =
+            "android.bluetooth.action.BLUETOOTH_MAP_PROVIDER";
+    public static final String PROVIDER_INTERFACE_IM =
+            "android.bluetooth.action.BLUETOOTH_MAP_IM_PROVIDER";
+    /**
+     * The Bluetooth Message Access profile allows a remote BT-MAP client to trigger
+     * an update of a folder for a specific e-mail account, register for reception
+     * of new messages from the server.
+     *
+     * Additionally the Bluetooth Message Access profile allows a remote BT-MAP client
+     * to push a message to a folder - e.g. outbox or draft. The Bluetooth profile
+     * implementation will place a new message in one of these existing folders through
+     * the content provider.
+     *
+     * ContentProvider.call() is used for these purposes, and the METHOD_UPDATE_FOLDER
+     * method name shall trigger an update of the specified folder for a specified
+     * account.
+     *
+     * This shall be a non blocking call simply starting the update, and the update should
+     * both send and receive messages, depending on what makes sense for the specified
+     * folder.
+     * Bundle extra parameter will carry two INTEGER (long) values:
+     *   EXTRA_UPDATE_ACCOUNT_ID containing the account_id
+     *   EXTRA_UPDATE_FOLDER_ID containing the folder_id of the folder to update
+     *
+     * The status for send complete of messages shall be reported by updating the sent-flag
+     * and e.g. for outbox messages, move them to the sent folder in the message table of the
+     * content provider and trigger a change notification to any attached content observer.
+     */
+    public static final String METHOD_UPDATE_FOLDER = "UpdateFolder";
+    public static final String EXTRA_UPDATE_ACCOUNT_ID = "UpdateAccountId";
+    public static final String EXTRA_UPDATE_FOLDER_ID = "UpdateFolderId";
+
+    /**
+     * The Bluetooth Message Access profile allows a remote BT-MAP Client to update
+     * the owners presence and chat state
+     *
+     * ContentProvider.call() is used for these purposes, and the METHOD_SET_OWNER_STATUS
+     * method name shall trigger a change in owner/users presence or chat properties for an
+     * account or conversation.
+     *
+     * This shall be a non blocking call simply setting the properties, and the change should
+     * be sent to the remote server/users, depending on what property is changed.
+     * Bundle extra parameter will carry following values:
+     *   EXTRA_ACCOUNT_ID containing the account_id
+     *   EXTRA_PRESENCE_STATE containing the presence state of the owner account
+     *   EXTRA_PRESENCE_STATUS containing the presence status text from the owner
+     *   EXTRA_LAST_ACTIVE containing the last activity time stamp of the owner account
+     *   EXTRA_CHAT_STATE containing the chat state of a specific conversation
+     *   EXTRA_CONVERSATION_ID containing the conversation that is changed
+     */
+    public static final String METHOD_SET_OWNER_STATUS = "SetOwnerStatus";
+    public static final String EXTRA_ACCOUNT_ID = "AccountId"; // Is this needed
+    public static final String EXTRA_PRESENCE_STATE = "PresenceState";
+    public static final String EXTRA_PRESENCE_STATUS = "PresenceStatus";
+    public static final String EXTRA_LAST_ACTIVE = "LastActive";
+    public static final String EXTRA_CHAT_STATE = "ChatState";
+    public static final String EXTRA_CONVERSATION_ID = "ConversationId";
+
+    /**
+     * The Bluetooth Message Access profile can inform the messaging application of the Bluetooth
+     * state, whether is is turned 'on' or 'off'
+     *
+     * ContentProvider.call() is used for these purposes, and the METHOD_SET_BLUETOOTH_STATE
+     * method name shall trigger a change in owner/users presence or chat properties for an
+     * account or conversation.
+     *
+     * This shall be a non blocking call simply setting the properties.
+     *
+     * Bundle extra parameter will carry following values:
+     *   EXTRA_BLUETOOTH_STATE containing the state of the Bluetooth connectivity
+     */
+    public static final String METHOD_SET_BLUETOOTH_STATE = "SetBtState";
+    public static final String EXTRA_BLUETOOTH_STATE = "BluetoothState";
+
+    /**
+     * These column names are used as last path segment of the URI (getLastPathSegment()).
+     * Access to a specific row in the tables is done by using the where-clause, hence
+     * support for .../#id if not needed for the Email clients.
+     * The URI format for accessing the tables are as follows:
+     *   content://ProviderAuthority/TABLE_ACCOUNT
+     *   content://ProviderAuthority/account_id/TABLE_MESSAGE
+     *   content://ProviderAuthority/account_id/TABLE_FOLDER
+     *   content://ProviderAuthority/account_id/TABLE_CONVERSATION
+     *   content://ProviderAuthority/account_id/TABLE_CONVOCONTACT
+     **/
+
+    /**
+     * Build URI representing the given Accounts data-set in a
+     * Bluetooth provider. When queried, the direct URI for the account
+     * with the given accountID is returned.
+     */
+    public static Uri buildAccountUri(String authority) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(TABLE_ACCOUNT)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Account data-set with specific Id in a
+     * Bluetooth provider. When queried, the direct URI for the account
+     * with the given accountID is returned.
+     */
+    public static Uri buildAccountUriwithId(String authority, String accountId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(TABLE_ACCOUNT)
+                .appendPath(accountId)
+                .build();
+    }
+
+    /**
+     * Build URI representing the entire Message table in a
+     * Bluetooth provider.
+     */
+    public static Uri buildMessageUri(String authority) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(TABLE_MESSAGE)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Message data-set in a
+     * Bluetooth provider. When queried, the URI for the Messages
+     * with the given accountID is returned.
+     */
+    public static Uri buildMessageUri(String authority, String accountId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(accountId)
+                .appendPath(TABLE_MESSAGE)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Message data-set with specific messageId in a
+     * Bluetooth provider. When queried, the direct URI for the account
+     * with the given accountID is returned.
+     */
+    public static Uri buildMessageUriWithId(String authority, String accountId, String messageId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(accountId)
+                .appendPath(TABLE_MESSAGE)
+                .appendPath(messageId)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Message data-set in a
+     * Bluetooth provider. When queried, the direct URI for the folder
+     * with the given accountID is returned.
+     */
+    public static Uri buildFolderUri(String authority, String accountId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(accountId)
+                .appendPath(TABLE_FOLDER)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Message data-set in a
+     * Bluetooth provider. When queried, the direct URI for the conversation
+     * with the given accountID is returned.
+     */
+    public static Uri buildConversationUri(String authority, String accountId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(accountId)
+                .appendPath(TABLE_CONVERSATION)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Contact data-set in a
+     * Bluetooth provider. When queried, the direct URI for the contacts
+     * with the given accountID is returned.
+     */
+    public static Uri buildConvoContactsUri(String authority) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(TABLE_CONVOCONTACT)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Contact data-set in a
+     * Bluetooth provider. When queried, the direct URI for the contacts
+     * with the given accountID is returned.
+     */
+    public static Uri buildConvoContactsUri(String authority, String accountId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(accountId)
+                .appendPath(TABLE_CONVOCONTACT)
+                .build();
+    }
+
+    /**
+     * Build URI representing the given Contact data-set in a
+     * Bluetooth provider. When queried, the direct URI for the contact
+     * with the given contactID and accountID is returned.
+     */
+    public static Uri buildConvoContactsUriWithId(String authority, String accountId,
+            String contactId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(accountId)
+                .appendPath(TABLE_CONVOCONTACT)
+                .appendPath(contactId)
+                .build();
+    }
+
+    /**
+     *  @hide
+     */
+    public static final String TABLE_ACCOUNT = "Account";
+    public static final String TABLE_MESSAGE = "Message";
+    public static final String TABLE_MESSAGE_PART = "Part";
+    public static final String TABLE_FOLDER = "Folder";
+    public static final String TABLE_CONVERSATION = "Conversation";
+    public static final String TABLE_CONVOCONTACT = "ConvoContact";
+
+
+    /**
+     * Mandatory folders for the Bluetooth message access profile.
+     * The email client shall at least implement the following root folders.
+     * E.g. as a mapping for them such that the naming will match the underlying
+     * matching folder ID's.
+     */
+    public static final String FOLDER_NAME_INBOX = "inbox";
+    public static final String FOLDER_NAME_SENT = "sent";
+    public static final String FOLDER_NAME_OUTBOX = "outbox";
+    public static final String FOLDER_NAME_DRAFT = "draft";
+    public static final String FOLDER_NAME_DELETED = "deleted";
+    public static final String FOLDER_NAME_OTHER = "other";
+
+    /**
+     * Folder IDs to be used with Instant Messaging virtual folders
+     */
+    public static final long FOLDER_ID_OTHER = 0;
+    public static final long FOLDER_ID_INBOX = 1;
+    public static final long FOLDER_ID_SENT = 2;
+    public static final long FOLDER_ID_DRAFT = 3;
+    public static final long FOLDER_ID_OUTBOX = 4;
+    public static final long FOLDER_ID_DELETED = 5;
+
+
+    /**
+     * To push RFC2822 encoded messages into a folder and read RFC2822 encoded messages from
+     * a folder, the openFile() interface will be used as follows:
+     * Open a file descriptor to a message.
+     * Two modes supported for read: With and without attachments.
+     * One mode exist for write and the actual content will be with or without
+     * attachments.
+     *
+     * mode will be "r" for read and "w" for write, never "rw".
+     *
+     * URI format:
+     * The URI scheme is as follows.
+     * For reading messages with attachments:
+     *   content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId
+     *   Note: This shall be an offline operation, including only message parts and attachments
+     *         already downloaded to the device.
+     *
+     * For reading messages without attachments:
+     *   content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId/FILE_MSG_NO_ATTACHMENTS
+     *   Note: This shall be an offline operation, including only message parts already
+     *         downloaded to the device.
+     *
+     * For downloading and reading messages with attachments:
+     *   content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId/FILE_MSG_DOWNLOAD
+     *   Note: This shall download the message content and all attachments if possible,
+     *         else throw an IOException.
+     *
+     * For downloading and reading messages without attachments:
+     *   content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId/FILE_MSG_DOWNLOAD_NO_ATTACHMENTS
+     *   Note: This shall download the message content if possible, else throw an IOException.
+     *
+     * When reading from the file descriptor, the content provider shall return a stream
+     * of bytes containing a RFC2822 encoded message, as if the message was send to an email
+     * server.
+     *
+     * When a byte stream is written to the file descriptor, the content provider shall
+     * decode the RFC2822 encoded data and insert the message into the TABLE_MESSAGE at the ID
+     * supplied in URI - additionally the message content shall be stored in the underlying
+     * data base structure as if the message was received from an email server. The Message ID
+     * will be created using a insert on the TABLE_MESSAGE prior to calling openFile().
+     * Hence the procedure for inserting a message is:
+     *  - uri/msgId = insert(uri, value: folderId=xxx)
+     *  - fd = openFile(uri/msgId)
+     *  - fd.write (RFC2822 encoded data)
+     *
+     *  The Bluetooth Message Access Client might not know what to put into the From:
+     *  header nor have a valid time stamp, hence the content provider shall check
+     *  if the From: and Date: headers have been set by the message written, else
+     *  it must fill in appropriate values.
+     */
+    public static final String FILE_MSG_NO_ATTACHMENTS = "NO_ATTACHMENTS";
+    public static final String FILE_MSG_DOWNLOAD = "DOWNLOAD";
+    public static final String FILE_MSG_DOWNLOAD_NO_ATTACHMENTS = "DOWNLOAD_NO_ATTACHMENTS";
+
+    /**
+     * Account Table
+     * The columns needed to supply account information.
+     * The e-mail client app may choose to expose all e-mails as being from the same account,
+     * but it is not recommended, as this would be a violation of the Bluetooth specification.
+     * The Bluetooth Message Access settings activity will provide the user the ability to
+     * change the FLAG_EXPOSE values for each account in this table.
+     * The Bluetooth Message Access service will read the values when Bluetooth is turned on,
+     * and again on every notified change through the content observer interface.
+     */
+    public interface AccountColumns {
+
+        /**
+         * The unique ID for a row.
+         * 

Type: INTEGER (long)

+ */ + String _ID = "_id"; + + /** + * The account name to display to the user on the device when selecting whether + * or not to share the account over Bluetooth. + * + * The account display name should not reveal any sensitive information e.g. email- + * address, as it will be added to the Bluetooth SDP record, which can be read by + * any Bluetooth enabled device. (Access to any account content is only provided to + * authenticated devices). It is recommended that if the email client uses the email + * address as account name, then the address should be obfuscated (i.e. replace "@" + * with ".") + *

Type: TEXT

+ * read-only + */ + String ACCOUNT_DISPLAY_NAME = "account_display_name"; + + /** + * Expose this account to other authenticated Bluetooth devices. If the expose flag + * is set, this account will be listed as an available account to access from another + * Bluetooth device. + * + * This is a read/write flag, that can be set either from within the E-mail client + * UI or the Bluetooth settings menu. + * + * It is recommended to either ask the user whether to expose the account, or set this + * to "show" as default. + * + * This setting shall not be used to enforce whether or not an account should be shared + * or not if the account is bound by an administrative security policy. In this case + * the email app should not list the account at all if it is not to be sharable over BT. + * + *

Type: INTEGER (boolean) hide = 0, show = 1

+ */ + String FLAG_EXPOSE = "flag_expose"; + + + /** + * The account unique identifier representing this account. For most IM clients this will be + * the fully qualified user name to which an invite message can be sent, from another use. + * + * e.g.: "map_test_user_12345@gmail.com" - for a Hangouts account + * + * This value will only be visible to authenticated Bluetooth devices, and will be + * transmitted using an encrypted link. + *

Type: TEXT

+ * read-only + */ + String ACCOUNT_UCI = "account_uci"; + + + /** + * The Bluetooth SIG maintains a list of assigned numbers(text strings) for IM clients. + * If your client/account has such a string, this is the place to return it. + * If supported by both devices, the presence of this prefix will make it possible to + * respond to a message by making a voice-call, using the same account information. + * (The call will be made using the HandsFree profile) + * https://www.bluetooth.org/en-us/specification/assigned-numbers/uniform-caller-identifiers + * + * e.g.: "hgus" - for Hangouts + * + *

Type: TEXT

+ * read-only + */ + String ACCOUNT_UCI_PREFIX = "account_uci_PREFIX"; + + } + + /** + * Message Data Parts Table + * The columns needed to contain the actual data of the messageparts in IM messages. + * Each "part" has its own row and represent a single mime-part in a multipart-mime + * formatted message. + * + */ + public interface MessagePartColumns { + + /** + * The unique ID for a row. + *

Type: INTEGER (long)

+ * read-only + */ + String _ID = "_id"; + // FIXME add message parts for IM attachments + /** + * is this a text part yes/no? + *

Type: TEXT

+ * read-only + */ + String TEXT = "text"; + + /** + * The charset used in the content if it is text or 8BIT if it is + * binary data + * + *

Type: TEXT

+ * read-only + */ + String CHARSET = "charset"; + + /** + * The filename representing the data file of the raw data in the database + * If this is empty, then it must be text and part of the message body. + * This is the name that the data will have when it is included as attachment + * + *

Type: TEXT

+ * read-only + */ + + String FILENAME = "filename"; + + /** + * Identifier for the content in the data. This can be used to + * refer directly to the data in the body part. + * + *

Type: TEXT

+ * read-only + */ + + String CONTENT_ID = "cid"; + + /** + * The raw data in either text format or binary format + * + *

Type: BLOB

+ * read-only + */ + String RAW_DATA = "raw_data"; + + } + + /** + * The actual message table containing all messages. + * Content that must support filtering using WHERE clauses: + * - To, From, Cc, Bcc, Date, ReadFlag, PriorityFlag, folder_id, account_id + * Additional content that must be supplied: + * - Subject, AttachmentFlag, LoadedState, MessageSize, AttachmentSize + * Content that must support update: + * - FLAG_READ and FOLDER_ID (FOLDER_ID is used to move a message to deleted) + * Additional insert of a new message with the following values shall be supported: + * - FOLDER_ID + * + * When doing an insert on this table, the actual content of the message (subject, + * date etc) written through file-i/o takes precedence over the inserted values and should + * overwrite them. + */ + public interface MessageColumns extends EmailMessageColumns { + + /** + * The unique ID for a row. + *

Type: INTEGER (long)

+ */ + String _ID = "_id"; + + /** + * The date the message was received as a unix timestamp + * (miliseconds since 00:00:00 UTC 1/1-1970). + * + *

Type: INTEGER (long)

+ * read-only + */ + String DATE = "date"; + + //TODO REMOVE WHEN Parts Table is in place + /** + * Message body. Used by Instant Messaging + *

Type: TEXT

+ * read-only. + */ + String BODY = "body"; + + /** + * Message subject. + *

Type: TEXT

+ * read-only. + */ + String SUBJECT = "subject"; + + /** + * Message Read flag + *

Type: INTEGER (boolean) unread = 0, read = 1

+ * read/write + */ + String FLAG_READ = "flag_read"; + + /** + * Message Priority flag + *

Type: INTEGER (boolean) normal priority = 0, high priority = 1

+ * read-only + */ + String FLAG_HIGH_PRIORITY = "high_priority"; + + /** + * Reception state - the amount of the message that have been loaded from the server. + *

Type: TEXT see RECEPTION_STATE_* constants below

+ * read-only + */ + String RECEPTION_STATE = "reception_state"; + + /** + * Delivery state - the amount of the message that have been loaded from the server. + *

Type: TEXT see DELIVERY_STATE_* constants below

+ * read-only + */ + String DEVILERY_STATE = "delivery_state"; + + /** To be able to filter messages with attachments, we need this flag. + *

Type: INTEGER (boolean) no attachment = 0, attachment = 1

+ * read-only + */ + String FLAG_ATTACHMENT = "flag_attachment"; + + /** The overall size in bytes of the attachments of the message. + *

Type: INTEGER

+ */ + String ATTACHMENT_SIZE = "attachment_size"; + + /** The mine type of the attachments for the message. + *

Type: TEXT

+ * read-only + */ + String ATTACHMENT_MINE_TYPES = "attachment_mime_types"; + + /** The overall size in bytes of the message including any attachments. + * This value is informative only and should be the size an email client + * would display as size for the message. + *

Type: INTEGER

+ * read-only + */ + String MESSAGE_SIZE = "message_size"; + + /** Indicates that the message or a part of it is protected by a DRM scheme. + *

Type: INTEGER (boolean) no DRM = 0, DRM protected = 1

+ * read-only + */ + String FLAG_PROTECTED = "flag_protected"; + + /** + * A comma-delimited list of FROM addresses in RFC2822 format. + * The list must be compatible with Rfc822Tokenizer.tokenize(); + *

Type: TEXT

+ * read-only + */ + String FROM_LIST = "from_list"; + + /** + * A comma-delimited list of TO addresses in RFC2822 format. + * The list must be compatible with Rfc822Tokenizer.tokenize(); + *

Type: TEXT

+ * read-only + */ + String TO_LIST = "to_list"; + + /** + * The unique ID for a row in the folder table in which this message belongs. + *

Type: INTEGER (long)

+ * read/write + */ + String FOLDER_ID = "folder_id"; + + /** + * The unique ID for a row in the account table which owns this message. + *

Type: INTEGER (long)

+ * read-only + */ + String ACCOUNT_ID = "account_id"; + + /** + * The ID identify the thread/conversation a message belongs to. + * If no thread id is available, set value to "-1" + *

Type: INTEGER (long)

+ * read-only + */ + String THREAD_ID = "thread_id"; + + /** + * The Name of the thread/conversation a message belongs to. + *

Type: TEXT

+ * read-only + */ + String THREAD_NAME = "thread_name"; + } + + public interface EmailMessageColumns { + + + /** + * A comma-delimited list of CC addresses in RFC2822 format. + * The list must be compatible with Rfc822Tokenizer.tokenize(); + *

Type: TEXT

+ * read-only + */ + String CC_LIST = "cc_list"; + + /** + * A comma-delimited list of BCC addresses in RFC2822 format. + * The list must be compatible with Rfc822Tokenizer.tokenize(); + *

Type: TEXT

+ * read-only + */ + String BCC_LIST = "bcc_list"; + + /** + * A comma-delimited list of REPLY-TO addresses in RFC2822 format. + * The list must be compatible with Rfc822Tokenizer.tokenize(); + *

Type: TEXT

+ * read-only + */ + String REPLY_TO_LIST = "reply_to_List"; + + + } + + /** + * Indicates the complete message has been delivered to the recipient. + */ + public static final String DELIVERY_STATE_DELIVERED = "delivered"; + /** + * Indicates that the complete message has been sent from the MSE to the remote network. + */ + public static final String DELIVERY_STATE_SENT = "sent"; + + /** + * Indicates that the message, including any attachments, has been received from the + * server to the device. + */ + public static final String RECEPTION_STATE_COMPLETE = "complete"; + /** + * Indicates the message is partially received from the email server. + */ + public static final String RECEPTION_STATE_FRACTIONED = "fractioned"; + /** + * Indicates that only a notification about the message have been received. + */ + public static final String RECEPTION_STATE_NOTIFICATION = "notification"; + + /** + * Message folder structure + * MAP enforces use of a folder structure with mandatory folders: + * - inbox, outbox, sent, deleted, draft + * User defined folders are supported. + * The folder table must provide filtering (use of WHERE clauses) of the following entries: + * - account_id (linking the folder to an e-mail account) + * - parent_id (linking the folders individually) + * The folder table must have a folder name for each entry, and the mandatory folders + * MUST exist for each account_id. The folders may be empty. + * Use the FOLDER_NAME_xxx constants for the mandatory folders. Their names must + * not be translated into other languages, as the folder browsing is string based, and + * many Bluetooth Message Clients will use these strings to navigate to the folders. + */ + public interface FolderColumns { + + /** + * The unique ID for a row. + *

Type: INTEGER (long)

+ * read-only + */ + String _ID = "_id"; + + /** + * The folder display name to present to the user. + *

Type: TEXT

+ * read-only + */ + String NAME = "name"; + + /** + * The _id-key to the account this folder refers to. + *

Type: INTEGER (long)

+ * read-only + */ + String ACCOUNT_ID = "account_id"; + + /** + * The _id-key to the parent folder. -1 for root folders. + *

Type: INTEGER (long)

+ * read-only + */ + String PARENT_FOLDER_ID = "parent_id"; + } + + /** + * Message conversation structure. Enables use of a conversation structure for messages across + * folders, further binding contacts to conversations. + * Content that must be supplied: + * - Name, LastActivity, ReadStatus, VersionCounter + * Content that must support update: + * - READ_STATUS, LAST_ACTIVITY and VERSION_COUNTER (VERSION_COUNTER used to validity of _ID) + * Additional insert of a new conversation with the following values shall be supported: + * - FOLDER_ID + * When querying this table, the cursor returned must contain one row for each contact member + * in a thread. + * For filter/search parameters attributes to the URI will be used. The following columns must + * support filtering: + * - ConvoContactColumns.NAME + * - ConversationColumns.THREAD_ID + * - ConversationColumns.LAST_ACTIVITY + * - ConversationColumns.READ_STATUS + */ + public interface ConversationColumns extends ConvoContactColumns { + + /** + * The unique ID for a row. + *

Type: INTEGER (long)

+ * read-only + */ +// Should not be needed anymore public static final String _ID = "_id"; + + /** + * The unique ID for a Thread. + *

Type: INTEGER (long)

+ * read-only + */ + String THREAD_ID = "thread_id"; + + /** + * The unique ID for a row. + *

Type: INTEGER (long)

+ * read-only + */ +// TODO: IS THIS NECESSARY - or do we need the thread ID to hold thread Id from message +// or can we be sure we are in control and can use the _ID and put that in the message DB + //public static final String THREAD_ID = "thread_id"; + + /** + * The type of conversation, see {@link ConversationType} + *

Type: TEXT

+ * read-only + */ +// TODO: IS THIS NECESSARY - no conversation type is available in the latest, +// guess it can be found from number of contacts in the conversation + //public static final String TYPE = "type"; + + /** + * The name of the conversation, e.g. group name in case of group chat + *

Type: TEXT

+ * read-only + */ + String THREAD_NAME = "thread_name"; + + /** + * The time stamp of the last activity in the conversation as a unix timestamp + * (miliseconds since 00:00:00 UTC 1/1-1970) + *

Type: INTEGER (long)

+ * read-only + */ + String LAST_THREAD_ACTIVITY = "last_thread_activity"; + + /** + * The status on the conversation, either 'read' or 'unread' + *

Type: INTEGER (boolean) unread = 0, read = 1

+ * read/write + */ + String READ_STATUS = "read_status"; + + /** + * A counter that keep tack of version of the table content, count up on ID reuse + *

Type: INTEGER (long)

+ * read-only + */ +// TODO: IS THIS NECESSARY - skal den ligge i databasen? + // CB: If we need it, it must be in the database, or initialized with a random value at + // BT-ON + // UPDATE: TODO: Change to the last_activity time stamp (as a long value). This will + // provide the information needed for BT clients - currently unused + String VERSION_COUNTER = "version_counter"; + + /** + * A short description of the latest activity on conversation - typically + * part of the last message. + *

Type: TEXT

+ * read-only + */ + String SUMMARY = "convo_summary"; + + + } + + /** + * MAP enables access to contacts for the conversation + * The conversation table must provide filtering (using WHERE clauses) of following entries: + * - convo_id linking contacts to conversations + * - x_bt_uid linking contacts to PBAP contacts + * The conversation contact table must have a convo_id and a name for each entry. + */ + public interface ConvoContactColumns extends ChatStatusColumns, PresenceColumns { + /** + * The unique ID for a contact in Conversation + *

Type: INTEGER (long)

+ * read-only + */ +// Should not be needed anymore public static final String _ID = "_id"; + + /** + * The ID of the conversation the contact is part of. + *

Type: INTEGER (long)

+ * read-only + */ + String CONVO_ID = "convo_id"; + + /** + * The name of contact in instant message application + *

Type: TEXT

+ * read-only + */ + String NAME = "name"; + + /** + * The nickname of contact in instant message group chat conversation. + *

Type: TEXT

+ * read-only + */ + String NICKNAME = "nickname"; + + + /** + * The unique ID for all Bluetooth contacts available through PBAP. + *

Type: INTEGER (long)

+ * read-only + */ + String X_BT_UID = "x_bt_uid"; + + /** + * The unique ID for the contact within the domain of the interfacing service. + * (UCI: Unique Call Identity) + * It is expected that a message send to this ID will reach the recipient regardless + * through which interface the message is send. + * For E-mail this will be the e-mail address, for Google+ this will be the e-mail address + * associated with the contact account. + * This ID + *

Type: TEXT

+ * read-only + */ + String UCI = "x_bt_uci"; + } + + /** + * The name of query parameter used to filter on recipient + */ + public static final String FILTER_RECIPIENT_SUBSTRING = "rec_sub_str"; + + /** + * The name of query parameter used to filter on originator + */ + public static final String FILTER_ORIGINATOR_SUBSTRING = "org_sub_str"; + + /** + * The name of query parameter used to filter on read status. + * - true - return only threads with all messages marked as read + * - false - return only threads with one or more unread messages + * - omitted as query parameter - do not filter on read status + */ + public static final String FILTER_READ_STATUS = "read"; + + /** + * Time in ms since epoch. For conversations this will be for last activity + * as a unix timestamp (miliseconds since 00:00:00 UTC 1/1-1970) + */ + public static final String FILTER_PERIOD_BEGIN = "t_begin"; + + /** + * Time in ms since epoch. For conversations this will be for last activity + * as a unix timestamp (miliseconds since 00:00:00 UTC 1/1-1970) + */ + public static final String FILTER_PERIOD_END = "t_end"; + + /** + * Filter for a specific ThreadId + */ + public static final String FILTER_THREAD_ID = "thread_id"; + + + public interface ChatState { + int UNKNOWN = 0; + int INACITVE = 1; + int ACITVE = 2; + int COMPOSING = 3; + int PAUSED = 4; + int GONE = 5; + } + + /** + * Instant Messaging contact chat state information + * MAP enables access to contacts chat state for the instant messaging application + * The chat state table must provide filtering (use of WHERE clauses) of the following entries: + * - contact_id (linking chat state to contacts) + * - thread_id (linking chat state to conversations and messages) + * The presence table must have a contact_id for each entry. + */ + public interface ChatStatusColumns { + +// /** +// * The contact ID of a instant messaging contact. +// *

Type: TEXT

+// * read-only +// */ +// public static final String CONTACT_ID = "contact_id"; +// +// /** +// * The thread id for a conversation. +// *

Type: INTEGER (long)

+// * read-only +// */ +// public static final String CONVO_ID = "convo_id"; + + /** + * The chat state of contact in conversation, see {@link ChatState} + *

Type: INTERGER

+ * read-only + */ + String CHAT_STATE = "chat_state"; + +// /** +// * The geo location of the contact +// *

Type: TEXT

+// * read-only +// */ +//// TODO: IS THIS NEEDED - not in latest specification +// public static final String GEOLOC = "geoloc"; + + /** + * The time stamp of the last time this contact was active in the conversation + *

Type: INTEGER (long)

+ * read-only + */ + String LAST_ACTIVE = "last_active"; + + } + + public interface PresenceState { + int UNKNOWN = 0; + int OFFLINE = 1; + int ONLINE = 2; + int AWAY = 3; + int DO_NOT_DISTURB = 4; + int BUSY = 5; + int IN_A_MEETING = 6; + } + + /** + * Instant Messaging contact presence information + * MAP enables access to contacts presences information for the instant messaging application + * The presence table must provide filtering (use of WHERE clauses) of the following entries: + * - contact_id (linking contacts to presence) + * The presence table must have a contact_id for each entry. + */ + public interface PresenceColumns { + +// /** +// * The contact ID of a instant messaging contact. +// *

Type: TEXT

+// * read-only +// */ +// public static final String CONTACT_ID = "contact_id"; + + /** + * The presence state of contact, see {@link PresenceState} + *

Type: INTERGER

+ * read-only + */ + String PRESENCE_STATE = "presence_state"; + + /** + * The priority of contact presence + *

Type: INTERGER

+ * read-only + */ +// TODO: IS THIS NEEDED - not in latest specification + String PRIORITY = "priority"; + + /** + * The last status text from contact + *

Type: TEXT

+ * read-only + */ + String STATUS_TEXT = "status_text"; + + /** + * The time stamp of the last time the contact was online + *

Type: INTEGER (long)

+ * read-only + */ + String LAST_ONLINE = "last_online"; + + } + + + /** + * A projection of all the columns in the Message table + */ + public static final String[] BT_MESSAGE_PROJECTION = new String[]{ + MessageColumns._ID, + MessageColumns.DATE, + MessageColumns.SUBJECT, + //TODO REMOVE WHEN Parts Table is in place + MessageColumns.BODY, + MessageColumns.MESSAGE_SIZE, + MessageColumns.FOLDER_ID, + MessageColumns.FLAG_READ, + MessageColumns.FLAG_PROTECTED, + MessageColumns.FLAG_HIGH_PRIORITY, + MessageColumns.FLAG_ATTACHMENT, + MessageColumns.ATTACHMENT_SIZE, + MessageColumns.FROM_LIST, + MessageColumns.TO_LIST, + MessageColumns.CC_LIST, + MessageColumns.BCC_LIST, + MessageColumns.REPLY_TO_LIST, + MessageColumns.RECEPTION_STATE, + MessageColumns.DEVILERY_STATE, + MessageColumns.THREAD_ID + }; + + public static final String[] BT_INSTANT_MESSAGE_PROJECTION = new String[]{ + MessageColumns._ID, + MessageColumns.DATE, + MessageColumns.SUBJECT, + MessageColumns.MESSAGE_SIZE, + MessageColumns.FOLDER_ID, + MessageColumns.FLAG_READ, + MessageColumns.FLAG_PROTECTED, + MessageColumns.FLAG_HIGH_PRIORITY, + MessageColumns.FLAG_ATTACHMENT, + MessageColumns.ATTACHMENT_SIZE, + MessageColumns.ATTACHMENT_MINE_TYPES, + MessageColumns.FROM_LIST, + MessageColumns.TO_LIST, + MessageColumns.RECEPTION_STATE, + MessageColumns.DEVILERY_STATE, + MessageColumns.THREAD_ID, + MessageColumns.THREAD_NAME + }; + + /** + * A projection of all the columns in the Account table + */ + public static final String[] BT_ACCOUNT_PROJECTION = new String[]{ + AccountColumns._ID, AccountColumns.ACCOUNT_DISPLAY_NAME, AccountColumns.FLAG_EXPOSE, + }; + + /** + * A projection of all the columns in the Account table + * TODO: Is this the way to differentiate + */ + public static final String[] BT_IM_ACCOUNT_PROJECTION = new String[]{ + AccountColumns._ID, + AccountColumns.ACCOUNT_DISPLAY_NAME, + AccountColumns.FLAG_EXPOSE, + AccountColumns.ACCOUNT_UCI, + AccountColumns.ACCOUNT_UCI_PREFIX + }; + + /** + * A projection of all the columns in the Folder table + */ + public static final String[] BT_FOLDER_PROJECTION = new String[]{ + FolderColumns._ID, + FolderColumns.NAME, + FolderColumns.ACCOUNT_ID, + FolderColumns.PARENT_FOLDER_ID + }; + + + /** + * A projection of all the columns in the Conversation table + */ + public static final String[] BT_CONVERSATION_PROJECTION = new String[]{ + /* Thread information */ + ConversationColumns.THREAD_ID, + ConversationColumns.THREAD_NAME, + ConversationColumns.READ_STATUS, + ConversationColumns.LAST_THREAD_ACTIVITY, + ConversationColumns.VERSION_COUNTER, + ConversationColumns.SUMMARY, + /* Contact information */ + ConversationColumns.UCI, + ConversationColumns.NAME, + ConversationColumns.NICKNAME, + ConversationColumns.CHAT_STATE, + ConversationColumns.LAST_ACTIVE, + ConversationColumns.X_BT_UID, + ConversationColumns.PRESENCE_STATE, + ConversationColumns.STATUS_TEXT, + ConversationColumns.PRIORITY + }; + + /** + * A projection of the Contact Info and Presence columns in the Contact Info in table + */ + public static final String[] BT_CONTACT_CHATSTATE_PRESENCE_PROJECTION = new String[]{ + ConvoContactColumns.UCI, + ConvoContactColumns.CONVO_ID, + ConvoContactColumns.NAME, + ConvoContactColumns.NICKNAME, + ConvoContactColumns.X_BT_UID, + ConvoContactColumns.CHAT_STATE, + ConvoContactColumns.LAST_ACTIVE, + ConvoContactColumns.PRESENCE_STATE, + ConvoContactColumns.PRIORITY, + ConvoContactColumns.STATUS_TEXT, + ConvoContactColumns.LAST_ONLINE + }; + + /** + * A projection of the Contact Info the columns in Contacts Info table + */ + public static final String[] BT_CONTACT_PROJECTION = new String[]{ + ConvoContactColumns.UCI, + ConvoContactColumns.CONVO_ID, + ConvoContactColumns.X_BT_UID, + ConvoContactColumns.NAME, + ConvoContactColumns.NICKNAME + }; + + + /** + * A projection of all the columns in the Chat Status table + */ + public static final String[] BT_CHATSTATUS_PROJECTION = new String[]{ + ChatStatusColumns.CHAT_STATE, ChatStatusColumns.LAST_ACTIVE, + }; + + /** + * A projection of all the columns in the Presence table + */ + public static final String[] BT_PRESENCE_PROJECTION = new String[]{ + PresenceColumns.PRESENCE_STATE, + PresenceColumns.PRIORITY, + PresenceColumns.STATUS_TEXT, + PresenceColumns.LAST_ONLINE + }; + +} diff --git a/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapEmailProvider.java b/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapEmailProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..29e2f0de6395a1a9a90bf69f427ba36a7d22297d --- /dev/null +++ b/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapEmailProvider.java @@ -0,0 +1,728 @@ +/* + * Copyright (C) 2013 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.mapapi; + +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.content.pm.ProviderInfo; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * A base implementation of the BluetoothMapEmailContract. + * A base class for a ContentProvider that allows access to Email messages from a Bluetooth + * device through the Message Access Profile. + */ +public abstract class BluetoothMapEmailProvider extends ContentProvider { + + private static final String TAG = "BluetoothMapEmailProvider"; + private static final boolean D = true; + + private static final int MATCH_ACCOUNT = 1; + private static final int MATCH_MESSAGE = 2; + private static final int MATCH_FOLDER = 3; + + protected ContentResolver mResolver; + + private Uri CONTENT_URI = null; + private String mAuthority; + private UriMatcher mMatcher; + + + private PipeReader mPipeReader = new PipeReader(); + private PipeWriter mPipeWriter = new PipeWriter(); + + /** + * Write the content of a message to a stream as MIME encoded RFC-2822 data. + * @param accountId the ID of the account to which the message belong + * @param messageId the ID of the message to write to the stream + * @param includeAttachment true if attachments should be included + * @param download true if any missing part of the message shall be downloaded + * before written to the stream. The download flag will determine + * whether or not attachments shall be downloaded or only the message content. + * @param out the FileOurputStream to write to. + * @throws IOException + */ + protected abstract void WriteMessageToStream(long accountId, long messageId, + boolean includeAttachment, boolean download, FileOutputStream out) throws IOException; + + /** + * @return the CONTENT_URI exposed. This will be used to send out notifications. + */ + protected abstract Uri getContentUri(); + + /** + * Implementation is provided by the parent class. + */ + @Override + public void attachInfo(Context context, ProviderInfo info) { + mAuthority = info.authority; + + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + mMatcher.addURI(mAuthority, BluetoothMapContract.TABLE_ACCOUNT, MATCH_ACCOUNT); + mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_FOLDER, MATCH_FOLDER); + mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_MESSAGE, MATCH_MESSAGE); + + // Sanity check our setup + if (!info.exported) { + throw new SecurityException("Provider must be exported"); + } + // Enforce correct permissions are used + if (!android.Manifest.permission.BLUETOOTH_MAP.equals(info.writePermission)) { + throw new SecurityException( + "Provider must be protected by " + android.Manifest.permission.BLUETOOTH_MAP); + } + mResolver = context.getContentResolver(); + super.attachInfo(context, info); + } + + + /** + * Interface to write a stream of data to a pipe. Use with + * {@link ContentProvider#openPipeHelper}. + */ + public interface PipeDataReader { + /** + * Called from a background thread to stream data from a pipe. + * Note that the pipe is blocking, so this thread can block on + * reads for an arbitrary amount of time if the client is slow + * at writing. + * + * @param input The pipe where data should be read. This will be + * closed for you upon returning from this function. + * @param uri The URI whose data is to be written. + * @param mimeType The desired type of data to be written. + * @param opts Options supplied by caller. + * @param args Your own custom arguments. + */ + void readDataFromPipe(ParcelFileDescriptor input, Uri uri, String mimeType, Bundle opts, + T args); + } + + public class PipeReader implements PipeDataReader { + /** + * Read the data from the pipe and generate a message. + * Use the message to do an update of the message specified by the URI. + */ + @Override + public void readDataFromPipe(ParcelFileDescriptor input, Uri uri, String mimeType, + Bundle opts, Cursor args) { + Log.v(TAG, "readDataFromPipe(): uri=" + uri.toString()); + FileInputStream fIn = null; + try { + fIn = new FileInputStream(input.getFileDescriptor()); + long messageId = Long.valueOf(uri.getLastPathSegment()); + long accountId = Long.valueOf(getAccountId(uri)); + UpdateMimeMessageFromStream(fIn, accountId, messageId); + } catch (IOException e) { + Log.w(TAG, "IOException: ", e); + /* TODO: How to signal the error to the calling entity? Had expected + readDataFromPipe + * to throw IOException? + */ + } finally { + try { + if (fIn != null) { + fIn.close(); + } + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + } + + /** + * Read a MIME encoded RFC-2822 fileStream and update the message content. + * The Date and/or From headers may not be present in the MIME encoded + * message, and this function shall add appropriate values if the headers + * are missing. From should be set to the owner of the account. + * + * @param input the file stream to read data from + * @param accountId the accountId + * @param messageId ID of the message to update + */ + protected abstract void UpdateMimeMessageFromStream(FileInputStream input, long accountId, + long messageId) throws IOException; + + public class PipeWriter implements PipeDataWriter { + /** + * Generate a message based on the cursor, and write the encoded data to the stream. + */ + + @Override + public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, + Bundle opts, Cursor c) { + if (D) { + Log.d(TAG, "writeDataToPipe(): uri=" + uri.toString() + " - getLastPathSegment() = " + + uri.getLastPathSegment()); + } + + FileOutputStream fout = null; + + try { + fout = new FileOutputStream(output.getFileDescriptor()); + + boolean includeAttachments = true; + boolean download = false; + List segments = uri.getPathSegments(); + long messageId = Long.parseLong(segments.get(2)); + long accountId = Long.parseLong(getAccountId(uri)); + if (segments.size() >= 4) { + String format = segments.get(3); + if (format.equalsIgnoreCase(BluetoothMapContract.FILE_MSG_NO_ATTACHMENTS)) { + includeAttachments = false; + } else if (format.equalsIgnoreCase( + BluetoothMapContract.FILE_MSG_DOWNLOAD_NO_ATTACHMENTS)) { + includeAttachments = false; + download = true; + } else if (format.equalsIgnoreCase(BluetoothMapContract.FILE_MSG_DOWNLOAD)) { + download = true; + } + } + + WriteMessageToStream(accountId, messageId, includeAttachments, download, fout); + } catch (IOException e) { + Log.w(TAG, e); + /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe + * to throw IOException? + */ + } finally { + try { + fout.flush(); + } catch (IOException e) { + Log.w(TAG, "IOException: ", e); + } + try { + fout.close(); + } catch (IOException e) { + Log.w(TAG, "IOException: ", e); + } + } + } + } + + /** + * This function shall be called when any Account database content have changed + * to Notify any attached observers. + * @param accountId the ID of the account that changed. Null is a valid value, + * if accountId is unknown or multiple accounts changed. + */ + protected void onAccountChanged(String accountId) { + Uri newUri = null; + + if (mAuthority == null) { + return; + } + if (accountId == null) { + newUri = BluetoothMapContract.buildAccountUri(mAuthority); + } else { + newUri = BluetoothMapContract.buildAccountUriwithId(mAuthority, accountId); + } + if (D) { + Log.d(TAG, "onAccountChanged() accountId = " + accountId + " URI: " + newUri); + } + mResolver.notifyChange(newUri, null); + } + + /** + * This function shall be called when any Message database content have changed + * to notify any attached observers. + * @param accountId Null is a valid value, if accountId is unknown, but + * recommended for increased performance. + * @param messageId Null is a valid value, if multiple messages changed or the + * messageId is unknown, but recommended for increased performance. + */ + protected void onMessageChanged(String accountId, String messageId) { + Uri newUri = null; + + if (mAuthority == null) { + return; + } + + if (accountId == null) { + newUri = BluetoothMapContract.buildMessageUri(mAuthority); + } else { + if (messageId == null) { + newUri = BluetoothMapContract.buildMessageUri(mAuthority, accountId); + } else { + newUri = BluetoothMapContract.buildMessageUriWithId(mAuthority, accountId, + messageId); + } + } + if (D) { + Log.d(TAG, "onMessageChanged() accountId = " + accountId + " messageId = " + messageId + + " URI: " + newUri); + } + mResolver.notifyChange(newUri, null); + } + + /** + * Not used, this is just a dummy implementation. + */ + @Override + public String getType(Uri uri) { + return "Email"; + } + + /** + * Open a file descriptor to a message. + * Two modes supported for read: With and without attachments. + * One mode exist for write and the actual content will be with or without + * attachments. + * + * Mode will be "r" or "w". + * + * URI format: + * The URI scheme is as follows. + * For messages with attachments: + * content://com.android.mail.bluetoothprovider/Messages/msgId# + * + * For messages without attachments: + * content://com.android.mail.bluetoothprovider/Messages/msgId#/NO_ATTACHMENTS + * + * UPDATE: For write. + * First create a message in the DB using insert into the message DB + * Then open a file handle to the #id + * write the data to a stream created from the fileHandle. + * + * @param uri the URI to open. ../Messages/#id + * @param mode the mode to use. The following modes exist: - UPDATE do not work - use URI + * - "read_with_attachments" - to read an e-mail including any attachments + * - "read_no_attachments" - to read an e-mail excluding any attachments + * - "write" - to add a mime encoded message to the database. This write + * should not trigger the message to be send. + * @return the ParcelFileDescriptor + * @throws FileNotFoundException + */ + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + final long callingId = Binder.clearCallingIdentity(); + if (D) { + Log.d(TAG, "openFile(): uri=" + uri.toString() + " - getLastPathSegment() = " + + uri.getLastPathSegment()); + } + try { + /* To be able to do abstraction of the file IO, we simply ignore the URI at this + * point and let the read/write function implementations parse the URI. */ + if (mode.equals("w")) { + return openInversePipeHelper(uri, null, null, null, mPipeReader); + } else { + return openPipeHelper(uri, null, null, null, mPipeWriter); + } + } catch (IOException e) { + Log.w(TAG, e); + } finally { + Binder.restoreCallingIdentity(callingId); + } + return null; + } + + /** + * A helper function for implementing {@link #openFile}, for + * creating a data pipe and background thread allowing you to stream + * data back from the client. This function returns a new + * ParcelFileDescriptor that should be returned to the caller (the caller + * is responsible for closing it). + * + * @param uri The URI whose data is to be written. + * @param mimeType The desired type of data to be written. + * @param opts Options supplied by caller. + * @param args Your own custom arguments. + * @param func Interface implementing the function that will actually + * stream the data. + * @return Returns a new ParcelFileDescriptor holding the read side of + * the pipe. This should be returned to the caller for reading; the caller + * is responsible for closing it when done. + */ + private ParcelFileDescriptor openInversePipeHelper(final Uri uri, final String mimeType, + final Bundle opts, final T args, final PipeDataReader func) + throws FileNotFoundException { + try { + final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); + + AsyncTask task = new AsyncTask() { + @Override + protected Object doInBackground(Object... params) { + func.readDataFromPipe(fds[0], uri, mimeType, opts, args); + try { + fds[0].close(); + } catch (IOException e) { + Log.w(TAG, "Failure closing pipe", e); + } + return null; + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null); + + return fds[1]; + } catch (IOException e) { + throw new FileNotFoundException("failure making pipe"); + } + } + + /** + * The MAP specification states that a delete request from MAP client is a folder shift to the + * 'deleted' folder. + * Only use case of delete() is when transparency is requested for push messages, then + * message should not remain in sent folder and therefore must be deleted + */ + @Override + public int delete(Uri uri, String where, String[] selectionArgs) { + if (D) { + Log.d(TAG, "delete(): uri=" + uri.toString()); + } + int result = 0; + + String table = uri.getPathSegments().get(1); + if (table == null) { + throw new IllegalArgumentException("Table missing in URI"); + } + // the id of the entry to be deleted from the database + String messageId = uri.getLastPathSegment(); + if (messageId == null) { + throw new IllegalArgumentException("Message ID missing in update values!"); + } + + + String accountId = getAccountId(uri); + if (accountId == null) { + throw new IllegalArgumentException("Account ID missing in update values!"); + } + + final long callingId = Binder.clearCallingIdentity(); + try { + if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) { + return deleteMessage(accountId, messageId); + } else { + if (D) { + Log.w(TAG, "Unknown table name: " + table); + } + return result; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + /** + * This function deletes a message. + * @param accountId the ID of the Account + * @param messageId the ID of the message to delete. + * @return the number of messages deleted - 0 if the message was not found. + */ + protected abstract int deleteMessage(String accountId, String messageId); + + /** + * Insert is used to add new messages to the data base. + * Insert message approach: + * - Insert an empty message to get an _id with only a folder_id + * - Open the _id for write + * - Write the message content + * (When the writer completes, this provider should do an update of the message) + */ + @Override + public Uri insert(Uri uri, ContentValues values) { + String table = uri.getLastPathSegment(); + if (table == null) { + throw new IllegalArgumentException("Table missing in URI"); + } + String accountId = getAccountId(uri); + Long folderId = values.getAsLong(BluetoothMapContract.MessageColumns.FOLDER_ID); + if (folderId == null) { + throw new IllegalArgumentException("FolderId missing in ContentValues"); + } + + String id; // the id of the entry inserted into the database + final long callingId = Binder.clearCallingIdentity(); + Log.d(TAG, "insert(): uri=" + uri.toString() + " - getLastPathSegment() = " + + uri.getLastPathSegment()); + try { + if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) { + id = insertMessage(accountId, folderId.toString()); + if (D) { + Log.i(TAG, "insert() ID: " + id); + } + return Uri.parse(uri.toString() + "/" + id); + } else { + Log.w(TAG, "Unknown table name: " + table); + return null; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + + /** + * Inserts an empty message into the Message data base in the specified folder. + * This is done before the actual message content is written by fileIO. + * @param accountId the ID of the account + * @param folderId the ID of the folder to create a new message in. + * @return the message id as a string + */ + protected abstract String insertMessage(String accountId, String folderId); + + /** + * Utility function to build a projection based on a projectionMap. + * + * "btColumnName" -> "emailColumnName as btColumnName" for each entry. + * + * This supports SQL statements in the emailColumnName entry. + * @param projection + * @param projectionMap + * @return the converted projection + */ + protected String[] convertProjection(String[] projection, Map projectionMap) { + String[] newProjection = new String[projection.length]; + for (int i = 0; i < projection.length; i++) { + newProjection[i] = projectionMap.get(projection[i]) + " as " + projection[i]; + } + return newProjection; + } + + /** + * This query needs to map from the data used in the e-mail client to BluetoothMapContract + * type of data. + */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + final long callingId = Binder.clearCallingIdentity(); + try { + String accountId = null; + switch (mMatcher.match(uri)) { + case MATCH_ACCOUNT: + return queryAccount(projection, selection, selectionArgs, sortOrder); + case MATCH_FOLDER: + accountId = getAccountId(uri); + return queryFolder(accountId, projection, selection, selectionArgs, sortOrder); + case MATCH_MESSAGE: + accountId = getAccountId(uri); + return queryMessage(accountId, projection, selection, selectionArgs, sortOrder); + default: + throw new UnsupportedOperationException("Unsupported Uri " + uri); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + /** + * Query account information. + * This function shall return only exposable e-mail accounts. Hence shall not + * return accounts that has policies suggesting not to be shared them. + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return a cursor to the accounts that are subject to exposure over BT. + */ + protected abstract Cursor queryAccount(String[] projection, String selection, + String[] selectionArgs, String sortOrder); + + /** + * Filter out the non usable folders and ensure to name the mandatory folders + * inbox, outbox, sent, deleted and draft. + * @param accountId + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return + */ + protected abstract Cursor queryFolder(String accountId, String[] projection, String selection, + String[] selectionArgs, String sortOrder); + + /** + * For the message table the selection (where clause) can only include the following columns: + * date: less than, greater than and equals + * flagRead: = 1 or = 0 + * flagPriority: = 1 or = 0 + * folder_id: the ID of the folder only equals + * toList: partial name/address search + * ccList: partial name/address search + * bccList: partial name/address search + * fromList: partial name/address search + * Additionally the COUNT and OFFSET shall be supported. + * @param accountId the ID of the account + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return a cursor to query result + */ + protected abstract Cursor queryMessage(String accountId, String[] projection, String selection, + String[] selectionArgs, String sortOrder); + + /** + * update() + * Messages can be modified in the following cases: + * - the folder_key of a message - hence the message can be moved to a new folder, + * but the content cannot be modified. + * - the FLAG_READ state can be changed. + * The selection statement will always be selection of a message ID, when updating a message, + * hence this function will be called multiple times if multiple messages must be updated + * due to the nature of the Bluetooth Message Access profile. + */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + + String table = uri.getLastPathSegment(); + if (table == null) { + throw new IllegalArgumentException("Table missing in URI"); + } + if (selection != null) { + throw new IllegalArgumentException( + "selection shall not be used, ContentValues shall contain the data"); + } + + final long callingId = Binder.clearCallingIdentity(); + if (D) { + Log.w(TAG, "update(): uri=" + uri.toString() + " - getLastPathSegment() = " + + uri.getLastPathSegment()); + } + try { + if (table.equals(BluetoothMapContract.TABLE_ACCOUNT)) { + String accountId = values.getAsString(BluetoothMapContract.AccountColumns._ID); + if (accountId == null) { + throw new IllegalArgumentException("Account ID missing in update values!"); + } + Integer exposeFlag = + values.getAsInteger(BluetoothMapContract.AccountColumns.FLAG_EXPOSE); + if (exposeFlag == null) { + throw new IllegalArgumentException("Expose flag missing in update values!"); + } + return updateAccount(accountId, exposeFlag); + } else if (table.equals(BluetoothMapContract.TABLE_FOLDER)) { + return 0; // We do not support changing folders + } else if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) { + String accountId = getAccountId(uri); + Long messageId = values.getAsLong(BluetoothMapContract.MessageColumns._ID); + if (messageId == null) { + throw new IllegalArgumentException("Message ID missing in update values!"); + } + Long folderId = values.getAsLong(BluetoothMapContract.MessageColumns.FOLDER_ID); + Boolean flagRead = + values.getAsBoolean(BluetoothMapContract.MessageColumns.FLAG_READ); + return updateMessage(accountId, messageId, folderId, flagRead); + } else { + if (D) { + Log.w(TAG, "Unknown table name: " + table); + } + return 0; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + /** + * Update an entry in the account table. Only the expose flag will be + * changed through this interface. + * @param accountId the ID of the account to change. + * @param flagExpose the updated value. + * @return the number of entries changed - 0 if account not found or value cannot be changed. + */ + protected abstract int updateAccount(String accountId, int flagExpose); + + /** + * Update an entry in the message table. + * @param accountId ID of the account to which the messageId relates + * @param messageId the ID of the message to update + * @param folderId the new folder ID value to set - ignore if null. + * @param flagRead the new flagRead value to set - ignore if null. + * @return + */ + protected abstract int updateMessage(String accountId, Long messageId, Long folderId, + Boolean flagRead); + + + @Override + public Bundle call(String method, String arg, Bundle extras) { + final long callingId = Binder.clearCallingIdentity(); + if (D) { + Log.d(TAG, "call(): method=" + method + " arg=" + arg + "ThreadId: " + + Thread.currentThread().getId()); + } + + try { + if (method.equals(BluetoothMapContract.METHOD_UPDATE_FOLDER)) { + long accountId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_ACCOUNT_ID, -1); + if (accountId == -1) { + Log.w(TAG, "No account ID in CALL"); + return null; + } + long folderId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_FOLDER_ID, -1); + if (folderId == -1) { + Log.w(TAG, "No folder ID in CALL"); + return null; + } + int ret = syncFolder(accountId, folderId); + if (ret == 0) { + return new Bundle(); + } + return null; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + return null; + } + + /** + * Trigger a sync of the specified folder. + * @param accountId the ID of the account that owns the folder + * @param folderId the ID of the folder. + * @return 0 at success + */ + protected abstract int syncFolder(long accountId, long folderId); + + /** + * Need this to suppress warning in unit tests. + */ + @Override + public void shutdown() { + // Don't call super.shutdown(), which emits a warning... + } + + /** + * Extract the BluetoothMapContract.AccountColumns._ID from the given URI. + */ + public static String getAccountId(Uri uri) { + final List segments = uri.getPathSegments(); + if (segments.size() < 1) { + throw new IllegalArgumentException("No AccountId pressent in URI: " + uri); + } + return segments.get(0); + } +} diff --git a/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapIMProvider.java b/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapIMProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..e0e3a5e74cdb4b29d82b4b3b586f7d8a0ee20818 --- /dev/null +++ b/android/app/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapIMProvider.java @@ -0,0 +1,707 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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.mapapi; + +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.content.pm.ProviderInfo; +import android.database.Cursor; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.util.Log; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A base implementation of the BluetoothMapContract. + * A base class for a ContentProvider that allows access to Instant messages from a Bluetooth + * device through the Message Access Profile. + */ +public abstract class BluetoothMapIMProvider extends ContentProvider { + + private static final String TAG = "BluetoothMapIMProvider"; + private static final boolean D = true; + + private static final int MATCH_ACCOUNT = 1; + private static final int MATCH_MESSAGE = 3; + private static final int MATCH_CONVERSATION = 4; + private static final int MATCH_CONVOCONTACT = 5; + + protected ContentResolver mResolver; + + private Uri CONTENT_URI = null; + private String mAuthority; + private UriMatcher mMatcher; + + /** + * @return the CONTENT_URI exposed. This will be used to send out notifications. + */ + protected abstract Uri getContentUri(); + + /** + * Implementation is provided by the parent class. + */ + @Override + public void attachInfo(Context context, ProviderInfo info) { + mAuthority = info.authority; + + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + mMatcher.addURI(mAuthority, BluetoothMapContract.TABLE_ACCOUNT, MATCH_ACCOUNT); + mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_MESSAGE, MATCH_MESSAGE); + mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_CONVERSATION, + MATCH_CONVERSATION); + mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_CONVOCONTACT, + MATCH_CONVOCONTACT); + + // Sanity check our setup + if (!info.exported) { + throw new SecurityException("Provider must be exported"); + } + // Enforce correct permissions are used + if (!android.Manifest.permission.BLUETOOTH_MAP.equals(info.writePermission)) { + throw new SecurityException( + "Provider must be protected by " + android.Manifest.permission.BLUETOOTH_MAP); + } + if (D) { + Log.d(TAG, "attachInfo() mAuthority = " + mAuthority); + } + + mResolver = context.getContentResolver(); + super.attachInfo(context, info); + } + + /** + * This function shall be called when any Account database content have changed + * to Notify any attached observers. + * @param accountId the ID of the account that changed. Null is a valid value, + * if accountId is unknown or multiple accounts changed. + */ + protected void onAccountChanged(String accountId) { + Uri newUri = null; + + if (mAuthority == null) { + return; + } + if (accountId == null) { + newUri = BluetoothMapContract.buildAccountUri(mAuthority); + } else { + newUri = BluetoothMapContract.buildAccountUriwithId(mAuthority, accountId); + } + + if (D) { + Log.d(TAG, "onAccountChanged() accountId = " + accountId + " URI: " + newUri); + } + mResolver.notifyChange(newUri, null); + } + + /** + * This function shall be called when any Message database content have changed + * to notify any attached observers. + * @param accountId Null is a valid value, if accountId is unknown, but + * recommended for increased performance. + * @param messageId Null is a valid value, if multiple messages changed or the + * messageId is unknown, but recommended for increased performance. + */ + protected void onMessageChanged(String accountId, String messageId) { + Uri newUri = null; + + if (mAuthority == null) { + return; + } + if (accountId == null) { + newUri = BluetoothMapContract.buildMessageUri(mAuthority); + } else { + if (messageId == null) { + newUri = BluetoothMapContract.buildMessageUri(mAuthority, accountId); + } else { + newUri = BluetoothMapContract.buildMessageUriWithId(mAuthority, accountId, + messageId); + } + } + if (D) { + Log.d(TAG, "onMessageChanged() accountId = " + accountId + " messageId = " + messageId + + " URI: " + newUri); + } + mResolver.notifyChange(newUri, null); + } + + + /** + * This function shall be called when any Message database content have changed + * to notify any attached observers. + * @param accountId Null is a valid value, if accountId is unknown, but + * recommended for increased performance. + * @param contactId Null is a valid value, if multiple contacts changed or the + * contactId is unknown, but recommended for increased performance. + */ + protected void onContactChanged(String accountId, String contactId) { + Uri newUri = null; + + if (mAuthority == null) { + return; + } + if (accountId == null) { + newUri = BluetoothMapContract.buildConvoContactsUri(mAuthority); + } else { + if (contactId == null) { + newUri = BluetoothMapContract.buildConvoContactsUri(mAuthority, accountId); + } else { + newUri = BluetoothMapContract.buildConvoContactsUriWithId(mAuthority, accountId, + contactId); + } + } + if (D) { + Log.d(TAG, "onContactChanged() accountId = " + accountId + " contactId = " + contactId + + " URI: " + newUri); + } + mResolver.notifyChange(newUri, null); + } + + /** + * Not used, this is just a dummy implementation. + * TODO: We might need to something intelligent here after introducing IM + */ + @Override + public String getType(Uri uri) { + return "InstantMessage"; + } + + /** + * The MAP specification states that a delete request from MAP client is a folder shift to the + * 'deleted' folder. + * Only use case of delete() is when transparency is requested for push messages, then + * message should not remain in sent folder and therefore must be deleted + */ + @Override + public int delete(Uri uri, String where, String[] selectionArgs) { + if (D) { + Log.d(TAG, "delete(): uri=" + uri.toString()); + } + int result = 0; + + String table = uri.getPathSegments().get(1); + if (table == null) { + throw new IllegalArgumentException("Table missing in URI"); + } + // the id of the entry to be deleted from the database + String messageId = uri.getLastPathSegment(); + if (messageId == null) { + throw new IllegalArgumentException("Message ID missing in update values!"); + } + + String accountId = getAccountId(uri); + if (accountId == null) { + throw new IllegalArgumentException("Account ID missing in update values!"); + } + + final long callingId = Binder.clearCallingIdentity(); + try { + if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) { + return deleteMessage(accountId, messageId); + } else { + if (D) { + Log.w(TAG, "Unknown table name: " + table); + } + return result; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + /** + * This function deletes a message. + * @param accountId the ID of the Account + * @param messageId the ID of the message to delete. + * @return the number of messages deleted - 0 if the message was not found. + */ + protected abstract int deleteMessage(String accountId, String messageId); + + /** + * Insert is used to add new messages to the data base. + * Insert message approach: + * - Insert an empty message to get an _id with only a folder_id + * - Open the _id for write + * - Write the message content + * (When the writer completes, this provider should do an update of the message) + */ + @Override + public Uri insert(Uri uri, ContentValues values) { + String table = uri.getLastPathSegment(); + if (table == null) { + throw new IllegalArgumentException("Table missing in URI"); + } + + String accountId = getAccountId(uri); + if (accountId == null) { + throw new IllegalArgumentException("Account ID missing in URI"); + } + + // TODO: validate values? + + String id; // the id of the entry inserted into the database + final long callingId = Binder.clearCallingIdentity(); + Log.d(TAG, "insert(): uri=" + uri.toString() + " - getLastPathSegment() = " + + uri.getLastPathSegment()); + try { + if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) { + id = insertMessage(accountId, values); + if (D) { + Log.i(TAG, "insert() ID: " + id); + } + return Uri.parse(uri.toString() + "/" + id); + } else { + Log.w(TAG, "Unknown table name: " + table); + return null; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + + /** + * Inserts an empty message into the Message data base in the specified folder. + * This is done before the actual message content is written by fileIO. + * @param accountId the ID of the account + * @param folderId the ID of the folder to create a new message in. + * @return the message id as a string + */ + protected abstract String insertMessage(String accountId, ContentValues values); + + /** + * Utility function to build a projection based on a projectionMap. + * + * "btColumnName" -> "imColumnName as btColumnName" for each entry. + * + * This supports SQL statements in the column name entry. + * @param projection + * @param projectionMap + * @return the converted projection + */ + protected String[] convertProjection(String[] projection, Map projectionMap) { + String[] newProjection = new String[projection.length]; + for (int i = 0; i < projection.length; i++) { + newProjection[i] = projectionMap.get(projection[i]) + " as " + projection[i]; + } + return newProjection; + } + + /** + * This query needs to map from the data used in the e-mail client to + * BluetoothMapContract type of data. + */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + final long callingId = Binder.clearCallingIdentity(); + try { + String accountId = null; + if (D) { + Log.w(TAG, "query(): uri =" + mAuthority + " uri=" + uri.toString()); + } + + switch (mMatcher.match(uri)) { + case MATCH_ACCOUNT: + return queryAccount(projection, selection, selectionArgs, sortOrder); + case MATCH_MESSAGE: + // TODO: Extract account from URI + accountId = getAccountId(uri); + return queryMessage(accountId, projection, selection, selectionArgs, sortOrder); + case MATCH_CONVERSATION: + accountId = getAccountId(uri); + String value; + String searchString = + uri.getQueryParameter(BluetoothMapContract.FILTER_ORIGINATOR_SUBSTRING); + Long periodBegin = null; + value = uri.getQueryParameter(BluetoothMapContract.FILTER_PERIOD_BEGIN); + if (value != null) { + periodBegin = Long.parseLong(value); + } + Long periodEnd = null; + value = uri.getQueryParameter(BluetoothMapContract.FILTER_PERIOD_END); + if (value != null) { + periodEnd = Long.parseLong(value); + } + Boolean read = null; + value = uri.getQueryParameter(BluetoothMapContract.FILTER_READ_STATUS); + if (value != null) { + read = value.equalsIgnoreCase("true"); + } + Long threadId = null; + value = uri.getQueryParameter(BluetoothMapContract.FILTER_THREAD_ID); + if (value != null) { + threadId = Long.parseLong(value); + } + return queryConversation(accountId, threadId, read, periodEnd, periodBegin, + searchString, projection, sortOrder); + case MATCH_CONVOCONTACT: + accountId = getAccountId(uri); + long contactId = 0; + return queryConvoContact(accountId, contactId, projection, selection, + selectionArgs, sortOrder); + default: + throw new UnsupportedOperationException("Unsupported Uri " + uri); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + /** + * Query account information. + * This function shall return only exposable e-mail accounts. Hence shall not + * return accounts that has policies suggesting not to be shared them. + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return a cursor to the accounts that are subject to exposure over BT. + */ + protected abstract Cursor queryAccount(String[] projection, String selection, + String[] selectionArgs, String sortOrder); + + /** + * For the message table the selection (where clause) can only include the following columns: + * date: less than, greater than and equals + * flagRead: = 1 or = 0 + * flagPriority: = 1 or = 0 + * folder_id: the ID of the folder only equals + * toList: partial name/address search + * fromList: partial name/address search + * Additionally the COUNT and OFFSET shall be supported. + * @param accountId the ID of the account + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return a cursor to query result + */ + protected abstract Cursor queryMessage(String accountId, String[] projection, String selection, + String[] selectionArgs, String sortOrder); + + /** + * For the Conversation table the selection (where clause) can only include + * the following columns: + * _id: the ID of the conversation only equals + * name: partial name search + * last_activity: less than, greater than and equals + * version_counter: updated IDs are regenerated + * Additionally the COUNT and OFFSET shall be supported. + * @param accountId the ID of the account + * @param threadId the ID of the conversation + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return a cursor to query result + */ +// abstract protected Cursor queryConversation(Long threadId, String[] projection, +// String selection, String[] selectionArgs, String sortOrder); + + /** + * Query for conversations with contact information. The expected result is a cursor pointing + * to one row for each contact in a conversation. + * E.g.: + * ThreadId | ThreadName | ... | ContactName | ContactPrecence | ... | + * 1 | "Bowling" | ... | Hans | 1 | ... | + * 1 | "Bowling" | ... | Peter | 2 | ... | + * 2 | "" | ... | Peter | 2 | ... | + * 3 | "" | ... | Hans | 1 | ... | + * + * @param accountId the ID of the account + * @param threadId filter on a single threadId - null if no filtering is needed. + * @param read filter on a read status: + * null: no filtering on read is needed. + * true: return only threads that has NO unread messages. + * false: return only threads that has unread messages. + * @param periodEnd last_activity time stamp of the the newest thread to include in the + * result. + * @param periodBegin last_activity time stamp of the the oldest thread to include in the + * result. + * @param searchString if not null, include only threads that has contacts that matches the + * searchString as part of the contact name or nickName. + * @param projection A list of the columns that is needed in the result + * @param sortOrder the sort order + * @return a Cursor representing the query result. + */ + protected abstract Cursor queryConversation(String accountId, Long threadId, Boolean read, + Long periodEnd, Long periodBegin, String searchString, String[] projection, + String sortOrder); + + /** + * For the ConvoContact table the selection (where clause) can only include the + * following columns: + * _id: the ID of the contact only equals + * convo_id: id of conversation contact is part of + * name: partial name search + * x_bt_uid: the ID of the bt uid only equals + * chat_state: active, inactive, gone, composing, paused + * last_active: less than, greater than and equals + * presence_state: online, do_not_disturb, away, offline + * priority: level of priority 0 - 100 + * last_online: less than, greater than and equals + * @param accountId the ID of the account + * @param contactId the ID of the contact + * @param projection + * @param selection + * @param selectionArgs + * @param sortOrder + * @return a cursor to query result + */ + protected abstract Cursor queryConvoContact(String accountId, Long contactId, + String[] projection, String selection, String[] selectionArgs, String sortOrder); + + /** + * update() + * Messages can be modified in the following cases: + * - the folder_key of a message - hence the message can be moved to a new folder, + * but the content cannot be modified. + * - the FLAG_READ state can be changed. + * Conversations can be modified in the following cases: + * - the read status - changing between read, unread + * - the last activity - the time stamp of last message sent of received in the conversation + * ConvoContacts can be modified in the following cases: + * - the chat_state - chat status of the contact in conversation + * - the last_active - the time stamp of last action in the conversation + * - the presence_state - the time stamp of last time contact online + * - the status - the status text of the contact available in a conversation + * - the last_online - the time stamp of last time contact online + * The selection statement will always be selection of a message ID, when updating a message, + * hence this function will be called multiple times if multiple messages must be updated + * due to the nature of the Bluetooth Message Access profile. + */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + + String table = uri.getLastPathSegment(); + if (table == null) { + throw new IllegalArgumentException("Table missing in URI"); + } + if (selection != null) { + throw new IllegalArgumentException( + "selection shall not be used, ContentValues " + "shall contain the data"); + } + + final long callingId = Binder.clearCallingIdentity(); + if (D) { + Log.w(TAG, "update(): uri=" + uri.toString() + " - getLastPathSegment() = " + + uri.getLastPathSegment()); + } + try { + if (table.equals(BluetoothMapContract.TABLE_ACCOUNT)) { + String accountId = values.getAsString(BluetoothMapContract.AccountColumns._ID); + if (accountId == null) { + throw new IllegalArgumentException("Account ID missing in update values!"); + } + Integer exposeFlag = + values.getAsInteger(BluetoothMapContract.AccountColumns.FLAG_EXPOSE); + if (exposeFlag == null) { + throw new IllegalArgumentException("Expose flag missing in update values!"); + } + return updateAccount(accountId, exposeFlag); + } else if (table.equals(BluetoothMapContract.TABLE_FOLDER)) { + return 0; // We do not support changing folders + } else if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) { + String accountId = getAccountId(uri); + if (accountId == null) { + throw new IllegalArgumentException("Account ID missing in update values!"); + } + Long messageId = values.getAsLong(BluetoothMapContract.MessageColumns._ID); + if (messageId == null) { + throw new IllegalArgumentException("Message ID missing in update values!"); + } + Long folderId = values.getAsLong(BluetoothMapContract.MessageColumns.FOLDER_ID); + Boolean flagRead = + values.getAsBoolean(BluetoothMapContract.MessageColumns.FLAG_READ); + return updateMessage(accountId, messageId, folderId, flagRead); + } else if (table.equals(BluetoothMapContract.TABLE_CONVERSATION)) { + return 0; // We do not support changing conversation + } else if (table.equals(BluetoothMapContract.TABLE_CONVOCONTACT)) { + return 0; // We do not support changing contacts + } else { + if (D) { + Log.w(TAG, "Unknown table name: " + table); + } + return 0; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + /** + * Update an entry in the account table. Only the expose flag will be + * changed through this interface. + * @param accountId the ID of the account to change. + * @param flagExpose the updated value. + * @return the number of entries changed - 0 if account not found or value cannot be changed. + */ + protected abstract int updateAccount(String accountId, Integer flagExpose); + + /** + * Update an entry in the message table. + * @param accountId ID of the account to which the messageId relates + * @param messageId the ID of the message to update + * @param folderId the new folder ID value to set - ignore if null. + * @param flagRead the new flagRead value to set - ignore if null. + * @return + */ + protected abstract int updateMessage(String accountId, Long messageId, Long folderId, + Boolean flagRead); + + /** + * Utility function to Creates a ContentValues object based on a modified valuesSet. + * To be used after changing the keys and optionally values of a valueSet obtained + * from a ContentValues object received in update(). + * @param valueSet the values as received in the contentProvider + * @param keyMap the key map + * @return a new ContentValues object with the keys replaced as specified in the + * keyMap + */ + protected ContentValues createContentValues(Set> valueSet, + Map keyMap) { + ContentValues values = new ContentValues(valueSet.size()); + for (Entry ent : valueSet) { + String key = keyMap.get(ent.getKey()); // Convert the key name + Object value = ent.getValue(); + if (value == null) { + values.putNull(key); + } else if (ent.getValue() instanceof Boolean) { + values.put(key, (Boolean) value); + } else if (ent.getValue() instanceof Byte) { + values.put(key, (Byte) value); + } else if (ent.getValue() instanceof byte[]) { + values.put(key, (byte[]) value); + } else if (ent.getValue() instanceof Double) { + values.put(key, (Double) value); + } else if (ent.getValue() instanceof Float) { + values.put(key, (Float) value); + } else if (ent.getValue() instanceof Integer) { + values.put(key, (Integer) value); + } else if (ent.getValue() instanceof Long) { + values.put(key, (Long) value); + } else if (ent.getValue() instanceof Short) { + values.put(key, (Short) value); + } else if (ent.getValue() instanceof String) { + values.put(key, (String) value); + } else { + throw new IllegalArgumentException("Unknown data type in content value"); + } + } + return values; + } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + final long callingId = Binder.clearCallingIdentity(); + if (D) { + Log.w(TAG, "call(): method=" + method + " arg=" + arg + "ThreadId: " + + Thread.currentThread().getId()); + } + int ret = -1; + try { + if (method.equals(BluetoothMapContract.METHOD_UPDATE_FOLDER)) { + long accountId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_ACCOUNT_ID, -1); + if (accountId == -1) { + Log.w(TAG, "No account ID in CALL"); + return null; + } + long folderId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_FOLDER_ID, -1); + if (folderId == -1) { + Log.w(TAG, "No folder ID in CALL"); + return null; + } + ret = syncFolder(accountId, folderId); + } else if (method.equals(BluetoothMapContract.METHOD_SET_OWNER_STATUS)) { + int presenceState = extras.getInt(BluetoothMapContract.EXTRA_PRESENCE_STATE); + String presenceStatus = + extras.getString(BluetoothMapContract.EXTRA_PRESENCE_STATUS); + long lastActive = extras.getLong(BluetoothMapContract.EXTRA_LAST_ACTIVE); + int chatState = extras.getInt(BluetoothMapContract.EXTRA_CHAT_STATE); + String convoId = extras.getString(BluetoothMapContract.EXTRA_CONVERSATION_ID); + ret = setOwnerStatus(presenceState, presenceStatus, lastActive, chatState, convoId); + + } else if (method.equals(BluetoothMapContract.METHOD_SET_BLUETOOTH_STATE)) { + boolean bluetoothState = + extras.getBoolean(BluetoothMapContract.EXTRA_BLUETOOTH_STATE); + ret = setBluetoothStatus(bluetoothState); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + if (ret == 0) { + return new Bundle(); + } + return null; + } + + /** + * Trigger a sync of the specified folder. + * @param accountId the ID of the account that owns the folder + * @param folderId the ID of the folder. + * @return 0 at success + */ + protected abstract int syncFolder(long accountId, long folderId); + + /** + * Set the properties that should change presence or chat state of owner + * e.g. when the owner is active on a BT client device but not on the BT server device + * where the IM application is installed, it should still be possible to show an active status. + * @param presenceState should follow the contract specified values + * @param presenceStatus string the owners current status + * @param lastActive time stamp of the owners last activity + * @param chatState should follow the contract specified values + * @param convoId ID to the conversation to change + * @return 0 at success + */ + protected abstract int setOwnerStatus(int presenceState, String presenceStatus, long lastActive, + int chatState, String convoId); + + /** + * Notify the application of the Bluetooth state + * @param bluetoothState 'on' of 'off' + * @return 0 at success + */ + protected abstract int setBluetoothStatus(boolean bluetoothState); + + + /** + * Need this to suppress warning in unit tests. + */ + @Override + public void shutdown() { + // Don't call super.shutdown(), which emits a warning... + } + + /** + * Extract the BluetoothMapContract.AccountColumns._ID from the given URI. + */ + public static String getAccountId(Uri uri) { + final List segments = uri.getPathSegments(); + if (segments.size() < 1) { + throw new IllegalArgumentException("No AccountId pressent in URI: " + uri); + } + return segments.get(0); + } +} diff --git a/android/app/res/drawable/bt_incomming_file_notification.xml b/android/app/res/drawable/bt_incomming_file_notification.xml new file mode 100644 index 0000000000000000000000000000000000000000..ed45b3bce301a3952a8885886971c7938e3d88d6 --- /dev/null +++ b/android/app/res/drawable/bt_incomming_file_notification.xml @@ -0,0 +1,24 @@ + + + + diff --git a/android/app/res/drawable/ic_accept.xml b/android/app/res/drawable/ic_accept.xml new file mode 100644 index 0000000000000000000000000000000000000000..5ed8b4eeb72b434038f2398a21564644096841f8 --- /dev/null +++ b/android/app/res/drawable/ic_accept.xml @@ -0,0 +1,24 @@ + + + + diff --git a/android/app/res/drawable/ic_decline.xml b/android/app/res/drawable/ic_decline.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c7d83a5e50bca0f5c103f62fda822909ea865b5 --- /dev/null +++ b/android/app/res/drawable/ic_decline.xml @@ -0,0 +1,24 @@ + + + + diff --git a/android/app/res/layout/auth.xml b/android/app/res/layout/auth.xml new file mode 100644 index 0000000000000000000000000000000000000000..7d1a200e14cce915deb5f76a149765a51c3b2f1c --- /dev/null +++ b/android/app/res/layout/auth.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + diff --git a/android/app/res/layout/bluetooth_map_settings.xml b/android/app/res/layout/bluetooth_map_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f256a021869c9ee6834d9b1519eff5dbed77e512 --- /dev/null +++ b/android/app/res/layout/bluetooth_map_settings.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + diff --git a/android/app/res/layout/bluetooth_map_settings_account_group.xml b/android/app/res/layout/bluetooth_map_settings_account_group.xml new file mode 100644 index 0000000000000000000000000000000000000000..7bbfca473ef1b050e937430ffff57302911ac1ca --- /dev/null +++ b/android/app/res/layout/bluetooth_map_settings_account_group.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/res/layout/bluetooth_map_settings_account_item.xml b/android/app/res/layout/bluetooth_map_settings_account_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6fbf8a77adb6a5e1066cdb5a09ede4fc51757de --- /dev/null +++ b/android/app/res/layout/bluetooth_map_settings_account_item.xml @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/android/app/res/layout/bluetooth_transfer_item.xml b/android/app/res/layout/bluetooth_transfer_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..50b4ec8bbb68d591e3096e4f4bf0dbab43a5da2d --- /dev/null +++ b/android/app/res/layout/bluetooth_transfer_item.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + diff --git a/android/app/res/layout/bluetooth_transfers_page.xml b/android/app/res/layout/bluetooth_transfers_page.xml new file mode 100644 index 0000000000000000000000000000000000000000..10e6c98b60598050468cd29097099e7f374d9392 --- /dev/null +++ b/android/app/res/layout/bluetooth_transfers_page.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/android/app/res/layout/bt_enabling_progress.xml b/android/app/res/layout/bt_enabling_progress.xml new file mode 100644 index 0000000000000000000000000000000000000000..02cfe4d78d89af2987d43ebfb2e1eafafb28a1b4 --- /dev/null +++ b/android/app/res/layout/bt_enabling_progress.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + diff --git a/android/app/res/layout/confirm_dialog.xml b/android/app/res/layout/confirm_dialog.xml new file mode 100644 index 0000000000000000000000000000000000000000..36afd34efc44ef0b66c917b40ae85a6fc574f043 --- /dev/null +++ b/android/app/res/layout/confirm_dialog.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/android/app/res/layout/file_transfer.xml b/android/app/res/layout/file_transfer.xml new file mode 100644 index 0000000000000000000000000000000000000000..4dea02d4d8acf719eced666737c85dfde09afee1 --- /dev/null +++ b/android/app/res/layout/file_transfer.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/res/layout/incoming_dialog.xml b/android/app/res/layout/incoming_dialog.xml new file mode 100644 index 0000000000000000000000000000000000000000..3f183d4cf0b496eb8abce1b47376a1a5e0f22ae1 --- /dev/null +++ b/android/app/res/layout/incoming_dialog.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/res/layout/no_transfers.xml b/android/app/res/layout/no_transfers.xml new file mode 100644 index 0000000000000000000000000000000000000000..ee30c31abe2a5ee81e58cb6b1ff82f8707f837dd --- /dev/null +++ b/android/app/res/layout/no_transfers.xml @@ -0,0 +1,23 @@ + + + + diff --git a/android/app/res/layout/testactivity_main.xml b/android/app/res/layout/testactivity_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..211d6372ae4c09902b3d0a0efa1cb8079c1d48e8 --- /dev/null +++ b/android/app/res/layout/testactivity_main.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + diff --git a/android/app/res/menu/receivedfilescontextfinished.xml b/android/app/res/menu/receivedfilescontextfinished.xml new file mode 100644 index 0000000000000000000000000000000000000000..79bfa3ff7616833bfd2b2068f534cccc61f795c3 --- /dev/null +++ b/android/app/res/menu/receivedfilescontextfinished.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/android/app/res/menu/transferhistory.xml b/android/app/res/menu/transferhistory.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ed408944d51c66ad29b419697f845642f808b2c --- /dev/null +++ b/android/app/res/menu/transferhistory.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/android/app/res/menu/transferhistorycontextfinished.xml b/android/app/res/menu/transferhistorycontextfinished.xml new file mode 100644 index 0000000000000000000000000000000000000000..3a6029af4130ac50b952d8ec19f54a273fa96aa6 --- /dev/null +++ b/android/app/res/menu/transferhistorycontextfinished.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/android/app/res/mipmap-anydpi/bt_share.xml b/android/app/res/mipmap-anydpi/bt_share.xml new file mode 100644 index 0000000000000000000000000000000000000000..c99e5609eae21caf06e303d3f175ca8048772a49 --- /dev/null +++ b/android/app/res/mipmap-anydpi/bt_share.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/android/app/res/raw/silent.wav b/android/app/res/raw/silent.wav new file mode 100755 index 0000000000000000000000000000000000000000..d6d93ef9c3f20208ca56341ad38c9c7b892021e7 Binary files /dev/null and b/android/app/res/raw/silent.wav differ diff --git a/android/app/res/values-af/config.xml b/android/app/res/values-af/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-af/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-af/strings.xml b/android/app/res/values-af/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3a7ca9a7bf194890b495eb0828457e90ca110183 --- /dev/null +++ b/android/app/res/values-af/strings.xml @@ -0,0 +1,137 @@ + + + + + "Maak toegang aflaaibestuurder oop." + "Gee aan die program toegang tot die BluetoothShare-bestuurder en om dit te gebruik om lêers oor te dra." + "Voeg Bluetooth-toesteltoegang by aanvaarlys." + "Laat die program toe om \'n Bluetooth-toestel tydelik by aanvaarlys te voeg, sodat daardie toestel lêers na hierdie toestel kan stuur sonder die gebruiker se bevestiging." + "Bluetooth" + "Onbekende toestel" + "Onbekend" + "Vliegtuigmodus" + "Jy kan nie Bluetooth in vliegtuigmodus gebruik nie." + + "Om Bluetooth-dienste te kan gebruik, moet jy eers Bluetooth aanskakel." + "Skakel nou Bluetooth aan?" + "Kanselleer" + "Skakel aan" + "Lêeroordrag" + "Aanvaar inkomende lêer?" + "Wys af" + "Aanvaar" + "OK" + "Daar was \'n uittelling terwyl \'n inkomende lêer van \"%1$s\" aanvaar is" + "Inkomende lêer" + "%1$s is gereed om %2$s te stuur" + "Bluetooth-deling: Ontvang %1$s" + "Bluetooth-deling: Het \"%1$s\" ontvang" + "Bluetooth-deling: Lêer %1$s nie ontvang nie" + "Bluetooth-deling: Stuur %1$s" + "Bluetooth-deling: %1$s gestuur" + "100% voltooi" + "Bluetooth-deling: Lêer %1$s nie gestuur nie" + "Lêeroordrag" + "Van: \"%1$s\"" + "Lêer: %1$s" + "Lêergrootte: %1$s" + + "Ontvang tans lêer…" + "Stop" + "Versteek" + "Van" + "Lêernaam" + "Grootte" + "Lêer nie ontvang nie" + "Lêer: %1$s" + "Rede: %1$s" + "OK" + "Lêer ontvang" + "Open" + "Aan: \"%1$s\"" + "Lêertipe: %1$s (%2$s)" + "Stuur tans lêer…" + "Lêer gestuur" + "OK" + "Die lêer is nie na \"%1$s\" gestuur nie." + "Lêer: %1$s" + "Maak toe" + "OK" + "Onbekende lêer" + "Daar is geen program om hierdie tipe lêer te hanteer nie. \n" + "Geen lêers nie" + "Die lêer bestaan nie. \n" + "Wag asseblief…" + "Skakel tans Bluetooth aan…" + "Die lêer sal ontvang word. Kontroleer vordering in die Kennisgewings-paneel." + "Die lêer kan nie ontvang word nie." + "Opgehou om lêer van \"%1$s\" te ontvang" + "Stuur lêer na \"%1$s\"" + "Stuur %1$s lêers na \"%2$s\"" + "Opgehou om lêer na \"%1$s\" te stuur" + "Daar is te min spasie in die USB-berging om die lêer te stoor." + "Daar is te min spasie op die SD-kaart om die lêer te stoor." + "Spasie nodig: %1$s" + "Te veel versoeke word verwerk. Probeer later weer." + "Lêeroordrag nog nie begin nie." + "Lêeroordrag is voortdurend." + "Lêeroordrag is suksesvol voltooi." + "Inhoud word nie ondersteun nie." + "Oordrag deur teikentoestel verbied." + "Oordrag gekanselleer deur die gebruiker." + "Bergingsprobleem." + "Geen USB-berging nie." + "Geen SD-kaart nie. Sit \'n SD-kaart in om oorgedraagde lêers te stoor." + "Verbinding onsuksesvol." + "Versoek kan nie korrek hanteer word nie." + "Onbekende fout" + "Ontvang deur Bluetooth" + "Bluetooth-deling" + "%1$s volledig ontvang." + "%1$s klaar gestuur." + "Inkomende oordragte" + "Uitgaande oordragte" + "Oordraggeskiedenis is leeg." + "Alle items sal uit die lys verwyder word." + "Bluetooth-deling: Gestuurde lêers" + "Bluetooth-deling: Ontvangde lêers" + + %1$d onsuksesvol. + %1$d onsuksesvol. + + + %1$d suksesvol, %2$s + %1$d suksesvol, %2$s + + "Vee lys uit" + "Open" + "Verwyder uit lys" + "Vee uit" + "Wat Speel" + "Stoor" + "Kanselleer" + "Kies die rekeninge wat jy deur Bluetooth wil deel. Jy moet steeds enige toegang tot die rekeninge aanvaar wanneer jy koppel." + "Gleuwe oor:" + "Programikoon" + "Bluetooth-boodskapdeelinstellings" + "Kan nie rekening kies nie. 0 gleuwe oor" + "Bluetooth-oudio is gekoppel" + "Bluetooth-oudio is ontkoppel" + "Bluetooth-oudio" + "Lêers groter as 4 GB kan nie oorgedra word nie" + "Koppel aan Bluetooth" + diff --git a/android/app/res/values-af/strings_pbap.xml b/android/app/res/values-af/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..785d05ff62af6a2b3247ae82bef217a369ef15bf --- /dev/null +++ b/android/app/res/values-af/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Tipe-sessiesleutel vir %1$s" + "Bluetooth-sessiesleutel benodig" + "Die tyd het verstryk om verbinding met %1$s te aanvaar" + "Die invoer-sessiesleutel met %1$s het uitgetel" + "Obex-stawingsversoek" + "Sessiesleutel" + "Tik sessiesleutel vir %1$s" + "Motortoebehore" + "Onbekende naam" + "My naam" + "000000" + "Bluetooth-kontakdeling" + diff --git a/android/app/res/values-af/strings_pbap_client.xml b/android/app/res/values-af/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-af/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-af/strings_sap.xml b/android/app/res/values-af/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c9b5e4ea319b9f49d01269f765ebfdf116f0186 --- /dev/null +++ b/android/app/res/values-af/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth-SIM-toegang" + "Bluetooth-SIM-toegang" + "Versoek kliënt om te ontkoppel?" + "Wag tans dat kliënt ontkoppel" + "Ontkoppel" + "Dwing ontkoppeling" + diff --git a/android/app/res/values-af/test_strings.xml b/android/app/res/values-af/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..5db4907bdac3a7e30fd8f061022e76886894cdc5 --- /dev/null +++ b/android/app/res/values-af/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Voeg rekord in" + "Bevestig rekord" + "Ack-rekord" + "Vee alle rekords uit" + "OK" + "Vee rekord uit" + "Begin TCP-bediener" + "Stel TCP-bediener in kennis" + diff --git a/android/app/res/values-am/config.xml b/android/app/res/values-am/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-am/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-am/strings.xml b/android/app/res/values-am/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b62670293b0726bc19049811611cc00d407f9d0e --- /dev/null +++ b/android/app/res/values-am/strings.xml @@ -0,0 +1,137 @@ + + + + + "አውርድ አደራጅን ድረስ።" + "የብሉቱዝ አጋራ አስተዳዳሪውን ለመድረስ እና ፋይሎች እንዲያስተላልፉ ለመጠቀም ለመተግበሪያው ይፈቅዳል።" + "የብሉቱዝ መሣሪያ መዳረሻን በመቀበያ ዝርዝር ያስገቡ።" + "መተግበሪያው አንድ የብሉቱዝ መሣሪያ በጊዜያዊነት በመቀበያ ዝርዝር ውስጥ እንዲያስገባ ይፈቅድለታል፣ ይህም መሣሪያው ያለተጠቃሚው ማረጋገጫ ፋይሎችን ወደዚህኛው መሣሪያ እንዲልክ ያስችለዋል።" + "ብሉቱዝ" + "ያልታወቀ መሣሪያ" + "ያልታወቀ" + "የአውሮፕላን ሁነታ" + "ብሉቱዝበአውሮፕላን ሁነትመጠቀም አይችሉም።" + + "የብሉቱዝ አገልግሎት ለመጠቀም፣ መጀመሪያ ብሉቱዝን ያብሩ።" + "ብሉቱዝ አሁን ይብራ?" + "ይቅር" + "አብራ" + "ፋይልሰደዳ" + "ገቢ ፋይልን ይቀበሉ?" + "አትቀበል" + "ተቀበል" + "እሺ" + "ከ \"%1$s\" ገቢ መልዕክት ፋይል እየተቀበለ ሳለ ጊዜ አልቆ ነበር።" + "ገቢ ፋይል" + "%1$s %2$sን ለመላክ ዝግጁ ነው" + "ብሉቱዝ ማጋሪያ፡ %1$s እየተቀበለ" + "ብሉቱዝ ማጋሪያ፡ %1$s ደርሷል" + "ብሉቱዝ ማጋሪያ፡ ፋይል%1$s አልደረሰም" + "ብሉቱዝ ማጋሪያ፡ %1$s እየላከ" + "ብሉቱዝ ማጋሪያ፡ %1$s ልኳል" + "100% ተጠናቋል።" + "ብሉቱዝ ማጋሪያ፡ ፋይል %1$s አልተላከም" + "ፋይል ሰደዳ" + "ከ: \"%1$s\"" + "ፋይል: %1$s" + "የፋይል መጠን፡%1$s" + + "ፋይል በመቀበል ላይ...." + "ቁም" + "ደብቅ" + "ከ" + "የፋይል ስም" + "መጠን" + "ፋይል አልደረሰም" + "ፋይል: %1$s" + "ምክንያት: %1$s" + "እሺ" + "ፋይል ደርሷል" + "ክፈት" + "ለ: \"%1$s\"" + "የፋይል ዓይነት: %1$s (%2$s)" + "ፋይል በመላክ ላይ..." + "ፋይል ተልኳል" + "እሺ" + "ፋይሉ ወደ \"%1$s\" አልተላከም ነበር።" + "ፋይል: %1$s" + "ዝጋ" + "እሺ" + "ያልታወቀ ፋይል" + "ይህን ዓይነቱን ፋይል ለማስተናገድ የሚችል መተግበሪያ የለም:: \n" + "ምንም ፋይሎች የሉም፡፡" + "ፋይል የለም:: \n" + "እባክዎ ይጠብቁ…" + "ብሉቱዝ በማብራት ላይ..." + "ፋይሉ ይደርሳል።በማሳወቂያ ውስን ቦታ ውስጥ ሂደቱን ይመልከቱ።" + "ፋይሉን መቀበል አይቻልም::" + "ከ\"%1$s\" ፋይል መቀበል አቁሟል" + "ፋይል ወደ \"%1$s\" በመላክ ላይ" + "%1$s ፋይሎችን ወደ \"%2$s\" በመላክ ላይ" + "ለ \"%1$s\" ፋይል መላክ አቁሟል" + "በUSB ማከማቻ ላይ ፋይል ለማስቀመጥ በቂ ቦታ የለም።" + "በኤስዲ ካርዱ ላይ ፋይሉን ለማስቀመጥ በቂ ቦታ የለም።" + "የሚያስፈልግ ቦታ፡ %1$s" + "እጅግ ብዙ ጥየቃዎች ተካሂደዋል። ትንሽ ቆይተው እንደገና ይሞክሩ።" + "የፋይል ዝውውር ገና አልተጀመረም::" + "የፋይል ዝውውር በመካሄድ ላይ ነው::" + "የፋይል ዝውውሩ በተሳካ ሁኔታ ተጠናቋል::" + "ይዘቱ አይደገፍም።" + "ይህ ዝውውር በታለመው መሣሪያ የተከለከለ ነው።" + "ዝውውር በተጠቃሚ ተትቷል::" + "የማከማቻ ጉዳይ" + "ምንም የዩኤስቢ ማከማቻ የለም።" + "ምንም ኤስዲ ካርድ የለም። የተዘዋወሩ ፋይሎችን ለማስቀመጥ ኤስዲ ካርድ ያስገቡ።" + "ተያያዥ አልተሳካም።" + "ጥየቃውን በትክክል መያዝ አይቻልም።" + "ያልታወቀ ስህተት" + "ብሉቱዝ ተቀብሏል" + "የብሉቱዝ ማጋሪያ" + "%1$s ተቀብሎ ተጠናቋል።" + "%1$s ልኮ ተጠናቋል።" + "ወደ ውስጥ ማስተላለፍ" + "ወደ ውጪ ማስተላለፍ" + "የዝውውር ታሪክ ባዶ ነው።" + "ሁሉም ዓይነቶች ከዝርዝር ውስጥ ይሰረዛሉ።" + "ብሉቱዝ ማጋሪያ፡ የተላኩ ፋይሎች" + "ብሉቱዝ ማጋሪያ፡ የደረሱ ፋይሎች" + + %1$d አልተሳኩም። + %1$d አልተሳኩም። + + + %1$d ተሳክተዋል፣ %2$s + %1$d ተሳክተዋል፣ %2$s + + "ዝርዝር አጽዳ" + "ክፈት" + "ከዝርዝር አጽዳ" + "አጽዳ" + "አሁን እየተጫወተ ያለ" + "አስቀምጥ" + "ይቅር" + "በብሉቱዝ በኩል ማጋራት የሚፈልጓቸውን መለያዎች ይምረጡ። አሁንም በሚገናኙበት ወቅት ማንኛቸውም የመለያዎቹ መዳረሻ መፍቀድ አለብዎት።" + "የቀሩ መክተቻዎች፦" + "የመተግበሪያ አዶ" + "የብሉቱዝ የመልዕክት ማጋሪያ ቅንብሮች" + "መለያን መምረጥ አይቻልም። 0 መክተቻዎች ይቀራሉ" + "የብሉቱዝ ኦዲዮ ተገናኝቷል" + "የብሉቱዝ ኦዲዮ ግንኙነት ተቋርጧል" + "የብሉቱዝ ኦዲዮ" + "ከ4 ጊባ በላይ የሆኑ ፋይሎች ሊዛወሩ አይችሉም" + "ከብሉቱዝ ጋር ተገናኝ" + diff --git a/android/app/res/values-am/strings_pbap.xml b/android/app/res/values-am/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..3f65c5749dfa7d14d02dc5682ca1c25b43bb379c --- /dev/null +++ b/android/app/res/values-am/strings_pbap.xml @@ -0,0 +1,16 @@ + + + " ለ%1$s የክፍለ ጊዜ ቁልፍንተይብ" + "ብሉቱዝ" + "ከ%1$s ተያየዥነት ለመቀበል ጊዜው አለቀ።" + "ከ%1$s ጋር የክፍለጊዜ ግቤት ጊዜው አለቆ ነበር።" + "Obex ማረጋገጫ ጥየቃ" + "የክፍለጊዜ ቁልፍ" + " ለ%1$s የክፍለ ጊዜ ቁልፍንተይብ" + "የመኪና መሣሪያ" + "ያልታወቀ ስም" + "የእኔ ስም" + "000000" + "ብሉቱዝ የእውቂያ መጋራት" + diff --git a/android/app/res/values-am/strings_pbap_client.xml b/android/app/res/values-am/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-am/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-am/strings_sap.xml b/android/app/res/values-am/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..87e717af032cf51a5241ea253f0205b7ae025940 --- /dev/null +++ b/android/app/res/values-am/strings_sap.xml @@ -0,0 +1,10 @@ + + + "የብሉቱዝ ሲም መዳረሻ" + "የብሉቱዝ ሲም መዳረሻ" + "ደንበኛ ግንኙነት እንዲያላቅቅ ይጠየቅ?" + "ደንበኛው ግንኙነት እስኪያላቅቅ ድረስ በመጠበቅ ላይ" + "ግንኙነት አቋርጥ" + "በግድ አላቅቅ" + diff --git a/android/app/res/values-am/test_strings.xml b/android/app/res/values-am/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..33f0c4dc521926fdca61702701a07dc7f85f8216 --- /dev/null +++ b/android/app/res/values-am/test_strings.xml @@ -0,0 +1,13 @@ + + + "ብሉቱዝ" + "መዝገብ አስገባ" + "መዝገብ አረጋግጥ" + "Ack መዝገብ" + "ሁሉንም መዝገብ ሰርዝ" + "እሺ" + "መዝገብ ሰርዝ" + "TCP አገልጋይን ጀምር" + "TCP አገልጋይን አሳውቅ" + diff --git a/android/app/res/values-ar/config.xml b/android/app/res/values-ar/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ar/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ar/strings.xml b/android/app/res/values-ar/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..9de395465c091b6968bf32551e447629556a3734 --- /dev/null +++ b/android/app/res/values-ar/strings.xml @@ -0,0 +1,145 @@ + + + + + "الدخول إلى إدارة التنزيل." + "‏للسماح للتطبيق بالدخول إلى إدارة BluetoothShare واستخدامها لنقل الملفات." + "إضافة جهاز يتضمّن بلوتوث إلى القائمة المسموح بها." + "للسماح للتطبيق مؤقتًا بإضافة جهاز يتضمّن بلوتوث إلى القائمة المسموح بها، وهو ما يتيح لذلك الجهاز إرسال ملفات إلى هذا الجهاز بدون تأكيد المستخدم." + "بلوتوث" + "جهاز غير معروف" + "غير معروف" + "وضع الطيران" + "لا يمكنك استخدام البلوتوث في وضع الطيران." + + "لإستخدام خدمات البلوتوث، يجب تفعيل البلوتوث أولاً." + "هل تريد تفعيل البلوتوث الآن؟" + "إلغاء" + "تفعيل" + "نقل الملف" + "هل تقبل الملف الوارد؟" + "رفض" + "قبول" + "حسنًا" + "انتهت المهلة أثناء قبول ملف وراد من \"%1$s\"" + "ملف وارد" + "%1$s جاهز لإرسال %2$s" + "مشاركة البلوتوث: يتم استلام %1$s" + "مشاركة البلوتوث: تم استلام %1$s" + "مشاركة البلوتوث: لم يتم استلام الملف %1$s" + "مشاركة البلوتوث: يتم إرسال %1$s" + "مشاركة البلوتوث: تم إرسال %1$s" + "اكتمل بنسبة 100%" + "مشاركة البلوتوث: لم يتم إرسال الملف %1$s" + "نقل الملف" + "من: \"%1$s\"" + "الملف: %1$s" + "حجم الملف: %1$s" + + "جارٍ استلام الملف..." + "إيقاف" + "إخفاء" + "من" + "اسم الملف" + "الحجم" + "لم يتم استلام الملف" + "الملف: %1$s" + "السبب: %1$s" + "حسنًا" + "تم استلام الملف" + "فتح" + "إلى: \"%1$s\"" + "نوع الملف: %1$s (%2$s)" + "جارٍ إرسال الملف..." + "تم إرسال الملف" + "حسنًا" + "لم يتم إرسال الملف إلى \"%1$s\"." + "الملف: %1$s" + "إغلاق" + "حسنًا" + "ملف غير معروف" + "ليس هناك تطبيق لمعالجة هذا النوع من الملفات. \n" + "ليست هناك أي ملفات" + "الملف غير موجود. \n" + "يرجى الانتظار…" + "جارٍ تفعيل البلوتوث..." + "سيتم استلام الملف. تحقق من التقدم في لوحة الإشعارات." + "لا يمكن تلقي الملف." + "تم إيقاف استلام الملف من \"%1$s\"" + "إرسال الملف إلى \"%1$s\"" + "إرسال %1$s من الملفات إلى \"%2$s\"" + "تم إيقاف إرسال الملف إلى \"%1$s\"" + "‏ليس هناك مساحة كافية على وحدة تخزين USB لحفظ الملف." + "‏ليس هناك مساحة كافية على بطاقة SD لحفظ الملف." + "المساحة اللازمة: %1$s" + "تتم حاليًا معالجة طلبات كثيرة جدًا. حاول مرة أخرى لاحقًا." + "لم يبدأ نقل الملف بعد." + "نقل الملف مستمر." + "أكملت نقل الملف بنجاح." + "المحتوى غير معتمد." + "تم حظر النقل بواسطة الجهاز الهدف." + "تم إلغاء النقل بواسطة المستخدم." + "مشكلة في وحدة التخزين." + "‏لا تتوفّر وحدة تخزين USB." + "‏لا تتوفّر بطاقة SD. يمكنك إدخال بطاقة SD لحفظ الملفات المنقولة." + "لم يتم الاتصال بنجاح." + "لا يمكن معالجة الطلب بشكل صحيح." + "خطأ غير معروف." + "ملفات بلوتوث المستلَمة" + "مشاركة البلوتوث" + "اكتمل استلام %1$s." + "اكتمل إرسال %1$s." + "عمليات النقل الواردة" + "عمليات النقل الصادرة" + "سجلّ النقل فارغ." + "سيتم محو جميع العناصر من القائمة." + "مشاركة البلوتوث: الملفات المرسلة" + "مشاركة البلوتوث: الملفات المستلمة" + + تعذّر إرسال %1$d من العناصر. + تعذّر إرسال عنصرين (%1$d). + تعذّر إرسال %1$d عناصر. + تعذّر إرسال %1$d عنصرًا. + تعذّر إرسال %1$d من العناصر. + تعذّر إرسال %1$d عنصر. + + + ‏نجح إرسال %1$d من العناصر، %2$s + ‏نجح إرسال عنصرين (%1$d)، %2$s + ‏نجح إرسال %1$d عناصر، %2$s + ‏نجح إرسال %1$d عنصرًا، %2$s + ‏نجح إرسال %1$d من العناصر، %2$s + ‏نجح إرسال %1$d عنصر، %2$s + + "محو القائمة" + "فتح" + "محو من القائمة" + "محو" + "التعرّف التلقائي على الموسيقى" + "حفظ" + "إلغاء" + "حدد الحسابات التي تريد مشاركتها عبر البلوتوث. لا يزال يتعين عليك قبول أي دخول إلى الحسابات أثناء الاتصال." + "المنافذ المتبقية:" + "رمز التطبيق" + "إعدادات مشاركة الرسائل عبر بلوتوث" + "يتعذر اختيار الحساب. عدد المنافذ المتبقية: ۰" + "تم الاتصال بالبث الصوتي عبر البلوتوث." + "انقطع الاتصال بالبث الصوتي عبر البلوتوث." + "بث صوتي عبر البلوتوث" + "يتعذّر نقل الملفات التي يزيد حجمها عن 4 غيغابايت" + "الاتصال ببلوتوث" + diff --git a/android/app/res/values-ar/strings_pbap.xml b/android/app/res/values-ar/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..80ad744f9ab9025b114b866d0429c145a824f84c --- /dev/null +++ b/android/app/res/values-ar/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "‏اكتب مفتاح الجلسة لـ %1$s" + "يلزم مفتاح جلسة البلوتوث" + "‏انتهت مهلة قبول الاتصال مع %1$s" + "‏انتهت مهلة إدخال مفتاح الجلسة مع %1$s" + "‏طلب مصادقة Obex" + "مفتاح الجلسة" + "‏اكتب مفتاح الجلسة لـ %1$s" + "مجموعة أدوات السيارة" + "اسم غير معروف" + "اسمي" + "000000" + "مشاركة جهات الاتصال عبر البلوتوث" + diff --git a/android/app/res/values-ar/strings_pbap_client.xml b/android/app/res/values-ar/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ar/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ar/strings_sap.xml b/android/app/res/values-ar/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..29b4edbfc1172cdf5a8e46b329ee4041d6cbf321 --- /dev/null +++ b/android/app/res/values-ar/strings_sap.xml @@ -0,0 +1,10 @@ + + + "‏الوصول إلى SIM بلوتوث" + "‏الوصول إلى SIM بلوتوث" + "هل تريد مطالبة العميل بإلغاء الربط؟" + "في انتظار إلغاء الربط بواسطة العميل" + "قطع الاتصال" + "فرض إلغاء الربط" + diff --git a/android/app/res/values-ar/test_strings.xml b/android/app/res/values-ar/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c640f4aa4774a0f44ab62649ebb7d3ea3cc6a1af --- /dev/null +++ b/android/app/res/values-ar/test_strings.xml @@ -0,0 +1,13 @@ + + + "بلوتوث" + "إدراج سجل" + "تأكيد السجل" + "‏سجل Ack" + "حذف السجل كله" + "حسنًا" + "حذف السجل" + "‏بدء الخادم TCP" + "‏إعلام الخادم TCP" + diff --git a/android/app/res/values-as/config.xml b/android/app/res/values-as/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-as/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..bc71ebda20bca92be915300ea73e44b503c4473e --- /dev/null +++ b/android/app/res/values-as/strings.xml @@ -0,0 +1,137 @@ + + + + + "ডাউনল’ড মেনেজাৰ ব্যৱহাৰ কৰিব পাৰে।" + "এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।" + "ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।" + "এপ্‌টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।" + "ব্লুটুথ" + "অজ্ঞাত ডিভাইচ" + "অজ্ঞাত" + "এয়াৰপ্লে’ন ম’ড" + "আপুনি এয়াৰপ্লেইন ম\'ডত ব্লুটুথ ব্যৱহাৰ কৰিব নোৱাৰে।" + + "ব্লুটুথ সেৱা ব্যৱহাৰ কৰাৰ আগতে ইয়াক অন কৰিবই লাগিব।" + "এতিয়াই ব্লুটুথ অন কৰিবনে?" + "বাতিল কৰক" + "অন কৰক" + "ফাইল স্থানান্তৰণ" + "অন্তৰ্গামী ফাইল গ্ৰহণ কৰিবনে?" + "প্ৰত্যাখ্যান কৰক" + "গ্ৰহণ কৰক" + "ঠিক" + "\"%1$s\"ৰ পৰা লাভ কৰা ফাইলটো গ্ৰহণ কৰোঁতে সময় ওকলিছে" + "অন্তৰ্গামী ফাইল" + "%1$s %2$s পঠাবলৈ সাজু" + "ব্লুটুথ শ্বেয়াৰ: %1$s লাভ কৰি থকা হৈছে" + "ব্লুটুথ শ্বেয়াৰ: %1$s লাভ কৰা হ’ল" + "ব্লুটুথ শ্বেয়াৰ: ফাইল %1$s লাভ কৰা নহ\'ল" + "ব্লুটুথ শ্বেয়াৰ: %1$s প্ৰেৰণ কৰি থকা হৈছে" + "ব্লুটুথ শ্বেয়াৰ: %1$s প্ৰেৰণ কৰা হ’ল" + "১০০% সম্পূৰ্ণ হ’ল" + "ব্লুটুথ শ্বেয়াৰ: ফাইল %1$s প্ৰেৰণ কৰা নহ\'ল" + "ফাইল স্থানান্তৰণ" + "প্ৰেৰক: \"%1$s\"" + "ফাইল: %1$s" + "ফাইলৰ আকাৰ: %1$s" + + "ফাইল লাভ কৰি থকা হৈছে…" + "বন্ধ কৰক" + "লুকুৱাওক" + "প্ৰেৰক" + "ফাইলৰ নাম" + "আকাৰ" + "ফাইল লাভ কৰা নহ\'ল" + "ফাইল: %1$s" + "কাৰণ: %1$s" + "ঠিক" + "ফাইল লাভ কৰা হ’ল" + "খোলক" + "প্ৰতি: \"%1$s\"" + "ফাইলৰ প্ৰকাৰ: %1$s (%2$s)" + "ফাইল প্ৰেৰণ কৰি থকা হৈছে…" + "ফাইল প্ৰেৰণ কৰা হ’ল" + "ঠিক" + "\"%1$s\"লৈ ফাইলটো পঠিয়াব পৰা নগ\'ল।" + "ফাইল: %1$s" + "বন্ধ কৰক" + "ঠিক" + "অজ্ঞাত ফাইল" + "এইধৰণৰ ফাইল পৰিচালনা কৰিবলৈ কোনো এপ্ নাই। \n" + "কোনো ফাইল নাই" + "ফাইলটো নাই। \n" + "অনুগ্ৰহ কৰি অপেক্ষা কৰক…" + "ব্লুটুথ অন কৰি থকা হৈছে…" + "ফাইলটো লাভ কৰা হ\'ব। জাননী পেনেলত অগ্ৰগতি নিৰীক্ষণ কৰক।" + "ফাইলটো লাভ কৰিব নোৱাৰি।" + "\"%1$s\"ৰ ফাইল লাভ কৰা বন্ধ কৰা হ’ল" + "\"%1$s\"লৈ ফাইল প্ৰেৰণ কৰি থকা হৈছে" + "\"%2$s\"লৈ %1$sটা ফাইল পঠিয়াই থকা হৈছে" + "\"%1$s\"লৈ ফাইল পঠিওৱা বন্ধ কৰা হ’ল" + "ফাইলটো ছেভ কৰিব পৰাকৈ ইউএছবি ষ্ট’ৰেজত পৰ্যাপ্ত খালী ঠাই নাই।" + "ফাইলটো ছেভ কৰিব পৰাকৈ এছডি কাৰ্ডখনত পৰ্যাপ্ত খালী ঠাই নাই।" + "ইমান খালী ঠাইৰ দৰকাৰ: %1$s" + "বহুত বেছি অনুৰোধৰ ওপৰত প্ৰক্ৰিয়া চলি আছে৷ পিছত আকৌ চেষ্টা কৰক৷" + "ফাইলৰ স্থানান্তৰণ এতিয়ালৈকে আৰম্ভ হোৱা নাই।" + "ফাইলৰ স্থানান্তৰণ চলি আছে।" + "ফাইলৰ স্থানান্তৰণৰ কাৰ্য সফলতাৰে সম্পন্ন কৰা হ’ল।" + "সমল সমৰ্থিত নহয়।" + "নিৰ্দিষ্ট কৰা ডিভাইচটোৱে স্থানান্তৰণ নিষিদ্ধ কৰিছে।" + "ব্যৱহাৰকাৰীয়ে স্থানান্তৰণ বাতিল কৰিছে।" + "সঞ্চয়াগাৰৰ সমস্যা।" + "কোনো USB সঞ্চয়াগাৰ নাই।" + "কোনো SD কাৰ্ড নাই। স্থানান্তৰ কৰা ফাইলসমূহ ছেভ কৰিবলৈ SD কাৰ্ড ভৰাওক।" + "সংযোগ কৰিব পৰা নগ\'ল।" + "অনুৰোধ সঠিকভাৱে পৰিচালনা কৰিব নোৱাৰি।" + "অজ্ঞাত আসোঁৱাহ।" + "ব্লুটুথ লাভ কৰা হ’ল" + "ব্লুটুথ শ্বেয়াৰ" + "%1$s লাভ কৰা কাৰ্য সম্পূৰ্ণ হ’ল।" + "%1$s প্ৰেৰণ কৰা কাম সম্পূৰ্ণ হ’ল" + "অন্তৰ্গামী স্থানান্তৰণসমূহ" + "বহিৰ্গামী স্থানান্তৰণসমূহ" + "স্থানান্তৰণৰ ইতিহাস খালী আছে।" + "সকলো সমল সূচীৰ পৰা মচা হ\'ব।" + "ব্লুটুথ শ্বেয়াৰ: প্ৰেৰণ কৰা ফাইলসমূহ" + "ব্লুটুথ শ্বেয়াৰ: লাভ কৰা ফাইলসমূহ" + + %1$dটা অসফল হ’ল। + %1$dটা অসফল হ’ল। + + + %1$dটা সফল হ’ল, %2$s + %1$dটা সফল হ’ল, %2$s + + "সূচী মচক" + "খোলক" + "সূচীৰ পৰা মচক" + "মচক" + "এতিয়া প্লে’ হৈ আছে" + "ছেভ কৰক" + "বাতিল কৰক" + "ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ অনুমতি দিবই লাগিব।" + "বাকী থকা শ্লটবোৰ:" + "এপ্লিকেশ্বন আইকন" + "ব্লুটুথৰ জৰিয়তে বাৰ্তা শ্বেয়াৰ কৰাৰ ছেটিংসমূহ" + "একাউণ্ট বাছনি কৰিব নোৱাৰি। ০টা শ্লটবোৰ বাকী আছে" + "ব্লুটুথ অডিঅ’ সংযুক্ত কৰা হ’ল" + "ব্লুটুথ অডিঅ\'ৰ সৈতে সংযোগ বিচ্ছিন্ন কৰা হ’ল" + "ব্লুটুথ অডিঅ\'" + "৪ জি. বি. তকৈ ডাঙৰ ফাইল স্থানান্তৰ কৰিব নোৱাৰি" + "ব্লুটুথৰ সৈতে সংযোগ কৰক" + diff --git a/android/app/res/values-as/strings_pbap.xml b/android/app/res/values-as/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..f59084ce0bba6df9d849bcc9318e100da5e45480 --- /dev/null +++ b/android/app/res/values-as/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$sৰ বাবে ছেশ্বনৰ চাবিটো টাইপ কৰক" + "ব্লুটুথ ছেশ্বনৰ চাবি দৰকাৰ" + "%1$sৰ সৈতে সংযোগৰ অনুৰোধ গ্ৰহণ কৰাৰ সময় ওকলিল" + "%1$sৰ সৈতে ছেশ্বনৰ চাবি ইনপুট কৰাৰ সময় ওকলিল" + "Obex সত্যাপনৰ অনুৰোধ" + "ছেশ্বনৰ চাবি" + "%1$sৰ বাবে ছেশ্বনৰ চাবিটো টাইপ কৰক" + "গাড়ীৰ কিট" + "অজ্ঞাত নাম" + "মোৰ নাম" + "০০০০০০" + "ব্লুটুথ সম্পৰ্ক শ্বেয়াৰ" + diff --git a/android/app/res/values-as/strings_pbap_client.xml b/android/app/res/values-as/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-as/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-as/strings_sap.xml b/android/app/res/values-as/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..1642cdf194c559aa0c9f6510e01fd58570b9b5b2 --- /dev/null +++ b/android/app/res/values-as/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ব্লুটুথৰ ছিম ব্যৱহাৰ" + "ব্লুটুথৰ ছিম ব্যৱহাৰ" + "সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টক অনুৰোধ কৰিবনে?" + "সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টৰ অপেক্ষা কৰি থকা হৈছে" + "বিচ্ছিন্ন কৰক" + "বলেৰে সংযোগ বিচ্ছিন্ন কৰক" + diff --git a/android/app/res/values-as/test_strings.xml b/android/app/res/values-as/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2efd87840e35d899062f0dc5182f289a7b2557a --- /dev/null +++ b/android/app/res/values-as/test_strings.xml @@ -0,0 +1,13 @@ + + + "ব্লুটুথ" + "ৰেকৰ্ড ভৰাওক" + "ৰেকৰ্ড নিশ্চিত কৰক" + "স্বীকৃত কৰা ৰেকৰ্ড" + "সকলো ৰেকৰ্ড মচক" + "ঠিক" + "ৰেকৰ্ড মচক" + "TCP ছাৰ্ভাৰ আৰম্ভ কৰক" + "TCP ছাৰ্ভাৰক জাননী দিয়ক" + diff --git a/android/app/res/values-az/config.xml b/android/app/res/values-az/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-az/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-az/strings.xml b/android/app/res/values-az/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..1e54827ee43e9e8d8da0d98bfb44648cbb05af90 --- /dev/null +++ b/android/app/res/values-az/strings.xml @@ -0,0 +1,137 @@ + + + + + "Endirmə menecerinə daxil olun." + "Tətbiq BluetoothPaylaşım menecerinə daxil ola və faylları ötürmək üçün ondan istifadə edə bilər." + "Bluetooth cihazı girişini qəbul siyahısına daxil edin." + "Tətbiqin Bluetooth cihazını müvəqqəti olaraq qəbul siyahısına daxil etməsinə imkan verir, bununla həmin cihaz istifadəçi təsdiqi olmadan bu cihaza fayllar göndərə biləcək." + "Bluetooth" + "Naməlum cihaz" + "Naməlum" + "Təyyarə rejimi" + "Təyyarə rejimində Bluetooth istifadə edə bilməzsiniz." + + "Bluetooth xidmətlərindən istifadə etmək üçün ilk öncə Bluetooth\'u yandırmalısınız." + "İndi Bluetooth\'u yandıraq?" + "Ləğv et" + "Yandır" + "Fayl transferi" + "Gələn fayl qəbul edilsin?" + "İmtina edin" + "Qəbul edirəm" + "OK" + "\"%1$s\" adlı istifadəçidən gələn faylı qəbul edərkən gecikmə baş verdi" + "Gələn fayl" + "%1$s %2$s faylını göndərməyə hazırdır" + "Bluetooth paylaşım: %1$s qəbul edilir" + "Bluetooth paylaşım: %1$s qəbul edildi" + "Bluetooth paylaşım: %1$s faylı qəbul edilmədi" + "Bluetooth paylaşım: %1$s göndərilir" + "Bluetooth paylaşım: %1$s göndərildi" + "100% tamamlandı" + "Bluetooth paylaşım: %1$s göndərilmədi" + "Fayl transferi" + "Kimdən: \" %1$s \"" + "Fayl: %1$s" + "Fayl ölçüsü: %1$s" + + "Fayl qəbulu edilir..." + "Dayandır" + "Gizlət" + "Kimdən" + "Fayl adı" + "Ölçü" + "Fayl qəbul edilmədi" + "Fayl: %1$s" + "Səbəb: %1$s" + "OK" + "Fayl qəbul edildi" + "Aç" + "Kimə: \"%1$s\"" + "Fayl tipi: %1$s (%2$s)" + "Fayl göndərilir..." + "Fayl göndərildi" + "OK" + "Fayl \"%1$s\" adlı istifadəçiyə göndərilmədi.." + "Fayl: %1$s" + "Qapadın" + "OK" + "Naməlum fayl" + "Bu fayl növünü idarə etmək üçün heç bir tətbiq yoxdur. \n" + "Fayl yoxdur" + "Fayl mövcud deyil. \n" + "Lütfən, gözləyin..." + "Bluetooth yandırılır..." + "Fayl qəbul ediləcək. Gedişatı Bildiriş panelində yoxlayın." + "Fayl qəbul edilə bilməz." + "\"%1$s\" tərəfindən faylın göndərilməsi dayandırıldı" + "\"%1$s\" adlı istifadəçiyə fayl göndərilir" + "\"%2$s\" adlı istifadəçiyə %1$s fayl göndərilir" + "\"%1$s\" adlı istifadəçiyə faylın göndərilməsi dayandırıldı" + "Faylı saxlamaq üçün USB yaddaşında kifayət qədər yer yoxdur." + "Faylı saxlamaq üçün SD kartda kifayət qədər yer yoxdur." + "Gərəkli: %1$s" + "Həddindən çox sorğu işlənilir. Sonra bir daha cəhd edin." + "Fayl transferi hələ başlamayıb." + "Fayl transferi davam edir." + "Fayl transferi uğurla sona çatdı." + "Kontent dəstəklənmir." + "Transfer hədəf cihaz tərəfindən qadağan edilib." + "Transfer istifadəçi tərəfindən ləğv edildi." + "Yaddaş problemi." + "USB yaddaş yoxdur." + "SD kart yoxdur. Köçürülən faylları yadda saxlamaq üçün SD kart daxil edin." + "Uğursuz bağlantı." + "Sorğu düzgün idarə edilə bilməz." + "Naməlum xəta." + "Bluetooth ilə qəbul edilənlər" + "Bluetooth Paylaşım" + "%1$s tam qəbul olundu." + "%1$s tam göndərildi." + "Gələn transferlər" + "Gedən transferlər" + "Transfer tarixi boşdur." + "Bütün məlumatlar siyahıdan silinəcək." + "Bluetooth paylaşım: Göndərilmiş fayllar" + "Bluetooth paylaşım: Qəbul edilən fayllar" + + %1$d uğursuz. + %1$d uğursuz. + + + %1$d uğurlu, %2$s + %1$d uğurlu, %2$s + + "Siyahını silin" + "Açın" + "Siyahıdan silin" + "Silin" + "Hazırda oxudulan" + "Yadda saxlayın" + "Ləğv edin" + "Bluetooth vasitəsilə paylaşmaq istədiyiniz hesabları seçin. Qoşulma zamanı hesablara olan istənilən girişi qəbul etməlisiniz." + "Qalmış slotlar:" + "Tətbiq ikonası" + "Bluetooth Mesaj Paylaşma Ayarları" + "Hesab seçmək olmur. 0 slot qalıb" + "Bluetooth audio bağlantısı yaradıldı" + "Bluetooth audio ilə bağlantı kəsildi" + "Bluetooth audio" + "4GB-dən böyük olan faylları köçürmək mümkün deyil" + "Bluetooth\'a qoşulun" + diff --git a/android/app/res/values-az/strings_pbap.xml b/android/app/res/values-az/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..d0e233402303b0ba951a398048cd8154557913e6 --- /dev/null +++ b/android/app/res/values-az/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s üçün seans açarı daxil edin" + "Bluetooth seans açarını tələb olunur" + "%1$s ilə bağlantı qəbul edərkən fasilə yarandı" + "%1$s ilə seans açarını daxil edərkən fasilə yarandı" + "Obex təsdiqləmə sorğusu" + "Seans Açarı" + "%1$s üçün seans şifrəsini daxil edin" + "Maşın dəsti" + "Naməlum ad" + "Mənim adım" + "000000" + "Bluetooth Kontakt paylaşımı" + diff --git a/android/app/res/values-az/strings_pbap_client.xml b/android/app/res/values-az/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-az/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-az/strings_sap.xml b/android/app/res/values-az/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..ef1643163fcb452d300167a0f7864764fdfdfcdd --- /dev/null +++ b/android/app/res/values-az/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM girişi" + "Bluetooth SIM Access" + "Müştəri ayırmaq tələb?" + "Ayırmaq üçün müştəri gözləyir" + "Bağlantını kəsin" + "Force ayırmaq" + diff --git a/android/app/res/values-az/test_strings.xml b/android/app/res/values-az/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..5150b3d44325fffa4955f0f944815436fbe179e3 --- /dev/null +++ b/android/app/res/values-az/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Qeyd daxil edin" + "Qeydi təsdiq edin" + "Ack qeydəalma" + "Bütün qeydləri silin" + "OK" + "Qeydi silin" + "TCP serverinə başlayın" + "TCP serverinə bildirin" + diff --git a/android/app/res/values-b+sr+Latn/config.xml b/android/app/res/values-b+sr+Latn/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-b+sr+Latn/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-b+sr+Latn/strings.xml b/android/app/res/values-b+sr+Latn/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7a5f39aac3e39c30478ed2e7d4e22148ba38f7e0 --- /dev/null +++ b/android/app/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,139 @@ + + + + + "Pristup menadžeru preuzimanja." + "Omogućava aplikaciji da pristupa menadžeru za deljenje preko Bluetooth-a i da ga koristi za prenos datoteka." + "Stavi pristup Bluetooth uređaja na listu prihvaćenih uređaja." + "Dozvoljava aplikaciji da privremeno stavi Bluetooth uređaj na listu prihvaćenih uređaja, što omogućava tom uređaju da šalje datoteke ovom uređaju bez odobrenja korisnika." + "Bluetooth" + "Nepoznati uređaj" + "Nepoznato" + "Režim rada u avionu" + "Ne možete da koristite Bluetooth u režimu rada u avionu." + + "Da biste mogli da koristite Bluetooth usluge, najpre morate da uključite Bluetooth." + "Želite li odmah da uključite Bluetooth?" + "Otkaži" + "Uključi" + "Prenos datoteke" + "Želite li da prihvatite dolaznu datoteku?" + "Odbij" + "Prihvati" + "Potvrdi" + "Došlo je do vremenskog ograničenja tokom prijema dolazne datoteke od „%1$s“" + "Dolazna datoteka" + "%1$s je spreman/na da pošalje %2$s" + "Bluetooth deljenje: prijem %1$s" + "Bluetooth deljenje: primljeno %1$s" + "Bluetooth deljenje: datoteka %1$s nije primljena" + "Bluetooth deljenje: slanje %1$s" + "Bluetooth deljenje: poslato %1$s" + "Dovršeno je 100%" + "Bluetooth deljenje: datoteka %1$s nije poslata" + "Prenos datoteke" + "Od: „%1$s“" + "Datoteka: %1$s" + "Veličina datoteke: %1$s" + + "Primanje datoteke..." + "Zaustavi" + "Sakrij" + "Od" + "Naziv datoteke" + "Veličina" + "Datoteka nije primljena" + "Datoteka: %1$s" + "Razlog: %1$s" + "Potvrdi" + "Datoteka je primljena" + "Otvori" + "Kome: „%1$s“" + "Tip datoteke: %1$s (%2$s)" + "Slanje datoteke..." + "Datoteka je poslata" + "Potvrdi" + "Datoteka nije poslata na %1$s." + "Datoteka: %1$s" + "Zatvori" + "Potvrdi" + "Nepoznata datoteka" + "Nema aplikacija za obradu ovog tipa datoteke. \n" + "Nema datoteke" + "Datoteka ne postoji. \n" + "Sačekajte…" + "Uključivanje Bluetooth-a…" + "Datoteka će biti primljena. Proveravajte tok na tabli sa obaveštenjima." + "Nije moguće primiti datoteku." + "Zaustavljen prijem datoteke od pošiljaoca „%1$s“" + "Slanje datoteke primaocu „%1$s“" + "Slanje%1$s datoteka primaocu „%2$s“" + "Zaustavljeno slanje primaocu „%1$s“" + "Nema dovoljno prostora u USB memoriji za čuvanje datoteke." + "Nema dovoljno prostora na SD kartici za čuvanje datoteke." + "Potreban prostor: %1$s" + "Previše zahteva se obrađuje. Probajte ponovo kasnije." + "Prenos datoteke još nije počeo." + "Prenos datoteke je u toku." + "Prenos datoteke je dovršen." + "Sadržaj nije podržan." + "Ciljni uređaj je zabranio prenos." + "Korisnik je otkazao prenos." + "Problem sa skladištem." + "Nema USB memorije." + "Nema SD kartice. Umetnite SD karticu da biste sačuvali prenete datoteke." + "Povezivanje nije uspelo." + "Nije moguće ispravno obraditi zahtev." + "Nepoznata greška." + "Primljeno preko Bluetooth-a" + "Deljenje preko Bluetooth-a" + "%1$s Primljeno u celosti." + "%1$s Slanje je dovršeno." + "Dolazni prenosi" + "Odlazni prenosi" + "Istorija prenosa je prazna." + "Sve stavke će biti izbrisane sa liste." + "Deljenje preko Bluetooth-a: poslate datoteke" + "Deljenje preko Bluetooth-a: primljene datoteke" + + %1$d neuspešna. + %1$d neuspešne. + %1$d neuspešnih. + + + %1$d uspešna, %2$s + %1$d uspešne, %2$s + %1$d uspešnih, %2$s + + "Obriši listu" + "Otvori" + "Obriši sa liste" + "Brisanje" + "Trenutno svira" + "Sačuvaj" + "Otkaži" + "Izaberite naloge koje želite da delite preko Bluetooth-a. I dalje morate da prihvatite bilo kakav pristup nalozima pri povezivanju." + "Preostalih mesta:" + "Ikona aplikacije" + "Podešavanja Bluetooth deljenja poruka" + "Nije moguće izabrati nalog. Nema preostalih mesta" + "Bluetooth audio je povezan" + "Veza sa Bluetooth audijom je prekinuta" + "Bluetooth audio" + "Ne mogu da se prenose datoteke veće od 4 GB" + "Poveži sa Bluetooth-om" + diff --git a/android/app/res/values-b+sr+Latn/strings_pbap.xml b/android/app/res/values-b+sr+Latn/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..3f29ff0a46cc4ff019dfcbe81383ffe5da3a53c3 --- /dev/null +++ b/android/app/res/values-b+sr+Latn/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Unesite ključ sesije za %1$s" + "Potreban je ključ sesije za Bluetooth" + "Isteklo je vreme za prihvatanje veze sa uređajem %1$s" + "Isteklo je vreme za unos ključa sesije pomoću %1$s" + "Zahtev za potvrdu identiteta preko Obex protokola" + "Ključ sesije" + "Unesite ključ sesije za %1$s" + "Oprema za automobil" + "Nepoznato ime" + "Moje ime" + "000000" + "Deljenje kontakata preko Bluetooth-a" + diff --git a/android/app/res/values-b+sr+Latn/strings_pbap_client.xml b/android/app/res/values-b+sr+Latn/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-b+sr+Latn/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-b+sr+Latn/strings_sap.xml b/android/app/res/values-b+sr+Latn/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..7e36fb93baf93f6f0da5173023b95d631eb6472d --- /dev/null +++ b/android/app/res/values-b+sr+Latn/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Pristup SIM kartici preko Bluetooth-a" + "Pristup SIM kartici preko Bluetooth-a" + "Želite li da pošaljete klijentu zahtev za prekid veze?" + "Čeka se da klijent prekine vezu" + "Prekini vezu" + "Prinudno prekini vezu" + diff --git a/android/app/res/values-b+sr+Latn/test_strings.xml b/android/app/res/values-b+sr+Latn/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..6a3691fe358d4e5200723bf94481899b9773d209 --- /dev/null +++ b/android/app/res/values-b+sr+Latn/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Umetni zapis" + "Potvrdi zapis" + "Ack zapis" + "Izbriši sve zapise" + "Potvrdi" + "Izbriši zapis" + "Pokreni TCP server" + "Obavesti TCP server" + diff --git a/android/app/res/values-be/config.xml b/android/app/res/values-be/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-be/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-be/strings.xml b/android/app/res/values-be/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7ac4be3de35b0a9f0daa0d33a0d92beccdd5f8f1 --- /dev/null +++ b/android/app/res/values-be/strings.xml @@ -0,0 +1,141 @@ + + + + + "Доступ да Менеджара спамповак." + "Дазваляе прыкладанням атрымліваць доступ да менеджэра BluetoothShare і выкарыстоўваць яго для перадачы файлаў." + "Уносьце ў белы спіс прылады з Bluetooth." + "Дазваляе праграме часова ўносіць у белы спіс прылады з Bluetooth, дазваляючы ім адпраўляць файлы на гэтую прыладу без пацвярджэння карыстальніка." + "Bluetooth" + "Невядомая прылада" + "Невядомы" + "Рэжым палёту" + "Вы не можаце выкарыстоўваць Bluetooth у рэжыме палёту." + + "Каб выкарыстоўваць перадачу праз Bluetooth, спачатку неабходна ўключыць Bluetooth." + "Уключыць Bluetooth зараз?" + "Скасаваць" + "Уключыць" + "Перадача файлаў" + "Прыняць уваходны файл?" + "Адхіліць" + "Прыняць" + "ОК" + "Тайм-аўт прыняцця ўваходнага файла ад адпраўніка \"%1$s\"" + "Уваходны файл" + "%1$s гатовы(-ая) адправіць %2$s" + "Перадача праз Bluetooth: атрыманне файла %1$s" + "Перадача праз Bluetooth: атрыманы файл %1$s" + "Перадача прз Bluetooth: файл %1$s не атрыманы" + "Перадача праз Bluetooth: адпраўка файла %1$s" + "Перадача праз Bluetooth: адпраўлены файл %1$s" + "Завершана 100%" + "Перадача праз Bluetooth: файл %1$s не адпраўлены" + "Перадача файлаў" + "Ад: \"%1$s\"" + "Файл: %1$s" + "Памер файла: %1$s" + + "Атрыманне файла..." + "Спыніць" + "Схаваць" + "Ад каго:" + "Назва файла" + "Памер" + "Файл не атрыманы" + "Файл: %1$s" + "Прычына: %1$s" + "ОК" + "Файл атрыманы" + "Адкрыць" + "Каму: \"%1$s\"" + "Тып файла: %1$s (%2$s)" + "Адпраўка файла..." + "Файл адпраўлены" + "ОК" + "Файл не адпраўлены атрымальніку \"%1$s\"." + "Файл: %1$s" + "Закрыць" + "ОК" + "Невядомы файл" + "Няма прыкладанняў для апрацоўкі файлаў гэтага тыпу. \n" + "няма файла" + "Файл не існуе. \n" + "Чакайце..." + "Уключэнне Bluetooth..." + "Файл будзе атрыманы. Сачыце за прагрэсам на панэлі апавяшчэнняў." + "Немагчыма атрымаць файл." + "Атрыманне файла ад адпраўніка \"%1$s\" спыненае" + "Адпраўка файла атрымальніку \"%1$s\"" + "Адпраўка файлаў (%1$s) атрымальніку \"%2$s\"" + "Адпраўка файла атрымальніку \"%1$s\" спыненая" + "У USB-сховішчы недастаткова месца, каб захаваць файл." + "На SD-карце недастаткова месца, каб захаваць файл." + "Спатрэбіцца месца: %1$s" + "Апрацоўваецца занадта шмат запытаў. Паспрабуйце пазней." + "Перадача файла яшчэ не пачалася" + "Ідзе перадача файлаў." + "Перадача файлаў паспяхова завершана." + "Змесціва не падтрымліваецца." + "Перадача забаронена мэтавай прыладай." + "Перадача адменена карыстальнiкам." + "Праблема ўнутранага сховiшча" + "Няма USB-сховішча." + "Няма SD-карты. Устаўце яе, каб захаваць перададзеныя файлы." + "Няўдалая спроба падключэння." + "Запыт не можа быць правільна апрацаваны" + "Невядомая памылка." + "Атрыманае праз Bluetooth" + "Абагульванне праз Bluetooth" + "Атрыманне завершанае: %1$s." + "Адпраўленне завершана: %1$s." + "Уваходныя перадачы" + "Выходныя перадачы" + "Гісторыя перадач пустая." + "Усе элементы будуць выдаленыя са спісу." + "Перадача праз Bluetooth: адпраўленыя файлы" + "Перадача праз Bluetooth: атрыманыя файлы" + + %1$d беспаспяховы. + %1$d беспаспяховыя. + %1$d беспаспяховых. + %1$d беспаспяховага. + + + %1$d паспяховы, %2$s + %1$d паспяховыя, %2$s + %1$d паспяховых, %2$s + %1$d паспяховага, %2$s + + "Ачысціць спіс" + "Адкрыць" + "Выдаліць са спісу" + "Ачысціць" + "Цяпер іграе" + "Захаваць" + "Скасаваць" + "Выберыце ўліковыя запісы, якія вы хочаце абагульваць па Bluetooth. Тым не менш, вам давядзецца асобна даваць кожны доступ пры падлучэнні." + "Засталося слотаў:" + "Значок праграмы" + "Налады агульнага доступу да паведамленняў па Bluetooth" + "Немагчыма выбраць уліковы запіс. Засталося 0 слотаў" + "Bluetooth-аўдыя падключана" + "Bluetooth-аўдыя адключана" + "Bluetooth-аўдыя" + "Немагчыма перадаць файлы, большыя за 4 ГБ" + "Падключыцца да Bluetooth" + diff --git a/android/app/res/values-be/strings_pbap.xml b/android/app/res/values-be/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..9b0fa31e552c7fd3c523bdd8177375f0c2199911 --- /dev/null +++ b/android/app/res/values-be/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Увядзіце ключ сеансу для %1$s" + "Патрэбны ключ для сеанса перадачы праз Bluetooth" + "Тайм-аўт прыёму злучэння з %1$s" + "Таўм-аўт уводу ключа сеансу з %1$s" + "Запыт на аўтэнтыфікацыю Obex" + "Ключ сесіі" + "Увядзіце ключ сеансу для %1$s" + "Carkit" + "Невядомае імя" + "Маё імя" + "000000" + "Абагульванне кантактаў праз Bluetooth" + diff --git a/android/app/res/values-be/strings_pbap_client.xml b/android/app/res/values-be/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-be/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-be/strings_sap.xml b/android/app/res/values-be/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..59d52ce8cd7553f1cf8e3c1ea664a9542b87a2c6 --- /dev/null +++ b/android/app/res/values-be/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Доступ да SIM па Bluetooth" + "Доступ да SIM па Bluetooth" + "Запытаць адлучэнне кліента?" + "Чаканне адлучэння кліента" + "Адключыць" + "Прымусовае адлучэнне" + diff --git a/android/app/res/values-be/test_strings.xml b/android/app/res/values-be/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..cd5736d94956193a211c671ac589f9b9b750c175 --- /dev/null +++ b/android/app/res/values-be/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Уставіць запіс" + "Пацвердзіць запіс" + "Запіс" + "Выдаліць усе запісы" + "ОК" + "Выдаліць запіс" + "Запусціць сервер TCP" + "Апавясцiць сервер TCP" + diff --git a/android/app/res/values-bg/config.xml b/android/app/res/values-bg/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-bg/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-bg/strings.xml b/android/app/res/values-bg/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..dc3a85543a4304159c8ccdf0581c83291ee89a2a --- /dev/null +++ b/android/app/res/values-bg/strings.xml @@ -0,0 +1,137 @@ + + + + + "Достъп до диспечера за изтегляне." + "Дава на приложението достъп и възможност за прехвърляне на файлове с диспечера за BluetoothShare." + "Достъп до устройство с Bluetooth, поставено в списъка за приемане." + "Разрешава на приложението временно да постави в списъка за приемане устройство с Bluetooth, позволявайки му да изпраща файлове до това устройство без потвърждение от потребителя." + "Bluetooth" + "Неизвестно устройство" + "Неизвестно" + "Самолетен режим" + "Не можете да използвате Bluetooth в самолетен режим." + + "Първо трябва да включите Bluetooth, за да използвате услугите му." + "Включване на Bluetooth сега?" + "Отказ" + "Включване" + "Прехвърляне на файл" + "Да се приеме ли входящият файл?" + "Отхвърляне" + "Приемане" + "OK" + "Времето за изчакване изтече при приемането на входящ файл от „%1$s“" + "Входящ файл" + "%1$s има готовност да изпрати „%2$s“" + "Споделяне чрез Bluetooth: %1$s се получава" + "Споделяне чрез Bluetooth: %1$s се получи" + "Споделяне чрез Bluetooth: Файлът %1$s не е получен" + "Споделяне чрез Bluetooth: %1$s се изпраща" + "Споделяне чрез Bluetooth: %1$s се изпрати" + "100% завършено" + "Споделяне чрез Bluetooth: Файлът %1$s не е изпратен" + "Прехвърляне на файл" + "От: „%1$s“" + "Файл: %1$s" + "Размер на файла: %1$s" + + "Файлът се получава..." + "Стоп" + "Скриване" + "От" + "Име на файла" + "Размер" + "Файлът не е получен" + "Файл: %1$s" + "Причина: %1$s" + "OK" + "Файлът е получен" + "Отваряне" + "До: „%1$s“" + "Файлов тип: %1$s (%2$s)" + "Файлът се изпраща..." + "Файлът е изпратен" + "OK" + "Файлът не бе изпратен до %1$s." + "Файл: %1$s" + "Затваряне" + "OK" + "Неизвестен файл" + "Няма приложение, което работи с този тип файлове. \n" + "Няма файл" + "Файлът не съществува. \n" + "Моля, изчакайте…" + "Bluetooth се включва..." + "Файлът ще бъде получен. Проверете хода в панела за известия." + "Файлът не може да бъде получен." + "Получаването на файл от „%1$s“ спря" + "Изпраща се файл до „%1$s“" + "%1$s файла се изпращат до „%2$s“" + "Изпращането на файл до „%1$s“ спря" + "В USB хранилището няма достатъчно място за запазване на файла." + "На SD картата няма достатъчно място за запазване на файла." + "Необходимо място: %1$s" + "Обработват се твърде много заявки. Опитайте отново по-късно." + "Прехвърлянето на файла още не е започнало." + "Извършва се прехвърляне на файл." + "Прехвърлянето на файла завърши успешно." + "Съдържанието не се поддържа." + "Прехвърлянето е забранено от целевото устройство." + "Прехвърлянето бе анулирано от потребителя." + "Проблем с хранилището." + "Няма USB хранилище." + "Няма SD карта. Поставете такава, за да запазите прехвърлените файлове." + "Връзката не е успешна." + "Заявката не може да бъде обработена правилно." + "Неизвестна грешка." + "Получено с Bluetooth" + "Споделяне през Bluetooth" + "%1$s – Получаването завърши." + "%1$s – Изпращането завърши." + "Входящи прехвърляния" + "Изходящи прехвърляния" + "Историята на прехвърлянията е празна." + "Всички елементи ще бъдат премахнати от списъка." + "Споделяне чрез Bluetooth: Изпратени файлове" + "Споделяне чрез Bluetooth: Получени файлове" + + Неуспешно: %1$d. + Неуспешно: %1$d. + + + Успешно: %1$d – %2$s + Успешно: %1$d – %2$s + + "Изчистване на списъка" + "Отваряне" + "Изчистване от списъка" + "Изчистване" + "Възпроизвеждано сега съдържание" + "Запазване" + "Отказ" + "Изберете профилите, които искате да споделите през Bluetooth. Пак трябва да приемете достъпа до тях при свързване." + "Оставащи слотове:" + "Икона на приложението" + "Настройки за споделяне на съобщения през Bluetooth" + "Не може да се избере профил. Остават 0 слота" + "Установена е аудиовръзка през Bluetooth" + "Аудиовръзката през Bluetooth е прекратена" + "Аудио през Bluetooth" + "Файловете с размер над 4 ГБ не могат да бъдат прехвърлени" + "Свързване с Bluetooth" + diff --git a/android/app/res/values-bg/strings_pbap.xml b/android/app/res/values-bg/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..8297f4013f5cf51bb6a5ac754e03d1174c4147a8 --- /dev/null +++ b/android/app/res/values-bg/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Въведете ключ за сесия за %1$s" + "Нужен е ключ за сесия с Bluetooth" + "Имаше пауза за приемане на връзка с/ъс %1$s" + "Имаше пауза за въвеждане на ключ за сесия с/ъс %1$s" + "Заявка за удостоверяване на Obex" + "Ключ за сесия" + "Въведете ключ за сесия за %1$s" + "Комплект за автомобил" + "Неизвестно име" + "Моето име" + "000000" + "Споделяне на контакта през Bluetooth" + diff --git a/android/app/res/values-bg/strings_pbap_client.xml b/android/app/res/values-bg/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-bg/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-bg/strings_sap.xml b/android/app/res/values-bg/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..1935a750d880afb6e1c58a2c9a01e3e4b598444a --- /dev/null +++ b/android/app/res/values-bg/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Достъп до SIM карти през Bluetooth" + "Достъп до SIM карти през Bluetooth" + "Искате ли да заявите клиентската програма да прекрати връзката?" + "Изчаква се клиентската програма да прекрати връзката" + "Прекратяване на връзката" + "Принудително прекратяване на връзката" + diff --git a/android/app/res/values-bg/test_strings.xml b/android/app/res/values-bg/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..601b07f7275e4d1a971aa15b6a929e017c0f9cad --- /dev/null +++ b/android/app/res/values-bg/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Вмъкване на запис" + "Потвърждение на записа" + "Запис на Ack" + "Изтриване на всички записи" + "OK" + "Изтриване на записа" + "Стартиране на TCP сървър" + "Известяване на TCP сървър" + diff --git a/android/app/res/values-bn/config.xml b/android/app/res/values-bn/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-bn/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-bn/strings.xml b/android/app/res/values-bn/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f0176a637d7d572a942fda09dc4f39f0609daf68 --- /dev/null +++ b/android/app/res/values-bn/strings.xml @@ -0,0 +1,137 @@ + + + + + "ডাউনলোড ম্যানেজার অ্যাক্সেস করুন।" + "অ্যাপ্লিকেশানটিকে BluetoothShare ম্যানেজার অ্যাক্সেস করতে ও ফাইলগুলি স্থানান্তর করতে এটি ব্যবহার করার অনুমতি দেয়।" + "অ্যাক্সেপ্ট করা হয়েছে এমন তালিকাভুক্ত ব্লুটুথ ডিভাইসের অ্যাক্সেস।" + "অ্যাপটিকে অস্থায়ীভাবে কোনও ব্লুটুথ ডিভাইসকে অ্যাক্সেপ্ট করা হয়েছে এমন তালিকায় অন্তর্ভুক্ত করার অনুমতি দেয়, যার ফলে ব্যবহারকারীর নিশ্চিতকরণ ছাড়াই ওই ডিভাইস থেকে এই ডিভাইসে ফাইল পাঠানো যায়।" + "ব্লুটুথ" + "অজানা ডিভাইস" + "অজানা" + "বিমান মোড" + "আপনি বিমান মোডে ব্লুটুথ ব্যবহার করতে পারবেন না।" + + "ব্লুটুথ পরিষেবাগুলি ব্যবহার করার জন্য আপনাকে অবশ্যই প্রথমে ব্লুটুথ চালু করতে হবে।" + "এখন ব্লুটুথ চালু করবেন?" + "বাতিল করুন" + "চালু করুন" + "ফাইল ট্রান্সফার" + "আগত ফাইল স্বীকার করবেন?" + "অস্বীকার করুন" + "স্বীকার করুন" + "ঠিক আছে" + "\"%1$s\" এর থেকে ইনকামিং ফাইল গ্রহণ করার সময় অতিবাহিত হয়ে গেছে।" + "আগত ফাইল" + "%1$s %2$s পাঠানোর জন্য প্রস্তুত" + "ব্লুটুথ share: %1$s প্রাপ্ত করা হচ্ছে" + "ব্লুটুথ share: %1$s প্রাপ্ত করা হয়েছে" + "ব্লুটুথ share: %1$s ফাইল প্রাপ্ত করা হয়নি" + "ব্লুটুথ share: %1$s পাঠানো হচ্ছে" + "ব্লুটুথ share: %1$s পাঠানো হয়েছে" + "১০০% সম্পূর্ণ" + "ব্লুটুথ share: %1$s ফাইল পাঠানো হয়নি" + "ফাইল ট্রান্সফার" + "প্রেরক: \"%1$s\"" + "ফাইল: %1$s" + "ফাইল আকার: %1$s" + + "ফাইল প্রাপ্ত হচ্ছে…" + "থামান" + "লুকান" + "প্রেরক" + "ফাইলের নাম" + "আকার" + "ফাইল প্রাপ্ত করা যায়নি" + "ফাইল: %1$s" + "কারণ: %1$s" + "ঠিক আছে" + "ফাইল প্রাপ্ত করা হয়েছে" + "খুলুন" + "প্রাপক: \"%1$s\"" + "ফাইল প্রকার: %1$s (%2$s)" + "ফাইল পাঠানো হচ্ছে..." + "ফাইল পাঠানো হয়েছে" + "ঠিক আছে" + "\"%1$s\" কে ফাইলটি পাঠানো হয়নি।" + "ফাইল: %1$s" + "বন্ধ করুন" + "ঠিক আছে" + "অজানা ফাইল" + "এই ধরণের ফাইল পরিচালনা করার জন্য কোনো অ্যাপ্লিকেশন নেই। \n" + "কোনো ফাইল নেই" + "ফাইলটির অস্তিত্ব নেই। \n" + "দয়া করে অপেক্ষা করুন..." + "ব্লুটুথ চালু করা হচ্ছে..." + "ফাইল প্রাপ্ত করা হবে। বিজ্ঞপ্তি প্যানেলে প্রগতি চেক করুন।" + "ফাইল প্রাপ্ত করা যাবে না।" + "\"%1$s\" এর থেকে ফাইল পাওয়া বন্ধ করা হয়েছে" + "\"%1$s\" কে ফাইল পাঠানো হচ্ছে" + "\"%2$s\" কে %1$sটি ফাইল পাঠানো হচ্ছে" + "\"%1$s\" কে ফাইল পাঠানো বন্ধ করা হয়েছে" + "ফাইল সেভ করার মতো USB স্টোরেজে পর্যাপ্ত স্পেস নেই।" + "ফাইল সেভ করার মতো এসডি কার্ডে পর্যাপ্ত স্পেস নেই।" + "জায়গা প্রয়োজন: %1$s" + "অনেকগুলি অনুরোধ প্রক্রিয়া করা হচ্ছে৷ পরে আবার চেষ্টা করুন৷" + "ফাইল ট্রান্সফার করা এখনও শুরু হয়নি।" + "ফাইল ট্রান্সফার করা চলছে।" + "ফাইল ট্রান্সফার সফলভাবে সম্পন্ন হয়েছে।" + "কন্টেন্ট সমর্থিত নয়।" + "টার্গেট ডিভাইস দ্বারা ট্রান্সফার নিষিদ্ধ করা হয়েছে।" + "ব্যবহারকারী দ্বারা ট্রান্সফার বাতিল করা হয়েছে।" + "স্টোরেজ সমস্যা।" + "কোনও ইউএসবি স্টোরেজ নেই।" + "কোনও এসডি কার্ড নেই। ট্রান্সফার করা ফাইলগুলি সেভ করতে এসডি কার্ড যোগ করুন।" + "সংযোগ অসফল।" + "অনুরোধ সঠিকভাবে পরিচালনা করা যাবে না।" + "অজানা ত্রুটি৷" + "ব্লুটুথ প্রাপ্তি" + "ব্লুটুথ শেয়ার" + "%1$s প্রাপ্ত করা সম্পূর্ণ।" + "%1$s পাঠানো সম্পূর্ণ।" + "অন্তরমূখী স্থানান্তরগুলি" + "বহির্গামী স্থানান্তরগুলি" + "ট্রান্সফার করার ইতিহাস খালি।" + "তালিকা থেকে সমস্ত আইটেম সাফ করা হবে।" + "ব্লুটুথ share: পাঠানো ফাইলগুলি" + "ব্লুটুথ share: প্রাপ্ত করা ফাইলগুলি" + + %1$dটি অসফল হয়েছে৷ + %1$dটি অসফল হয়েছে৷ + + + %1$dটি সফল হয়েছে, %2$s + %1$dটি সফল হয়েছে, %2$s + + "তালিকা সাফ করুন" + "খুলুন" + "তালিকা থেকে সাফ করুন" + "সাফ করুন" + "এখন চলছে" + "সেভ করুন" + "বাতিল করুন" + "আপনি ব্লুটুথ এর মাধ্যমে যে অ্যাকাউন্টগুলি শেয়ার করতে চান সেগুলি বেছে নিন। সংযোগের সময়ে আপনাকে এখনো অ্যাকাউন্টের যে কোনো অ্যাক্সেস গ্রহণ করতে হবে।" + "যে স্লটগুলি বাকি আছে:" + "অ্যাপ্লিকেশান আইকন" + "ব্লুটুথ মারফত মেসেজ শেয়ার করার সেটিংস" + "অ্যাকাউন্ট নির্বাচন করা যাচ্ছে না। ০টি স্লট বাকি আছে" + "ব্লুটুথ অডিও সংযুক্ত হয়েছে" + "ব্লুটুথ অডিওর সংযোগ বিচ্ছিন্ন হয়েছে" + "ব্লুটুথ অডিও" + "৪GB থেকে বড় ফটো ট্রান্সফার করা যাবে না" + "ব্লুটুথের সাথে কানেক্ট করুন" + diff --git a/android/app/res/values-bn/strings_pbap.xml b/android/app/res/values-bn/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..f97f21d79837381fa1380e598c9cf51bba4b96c0 --- /dev/null +++ b/android/app/res/values-bn/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s এর জন্য সেশন কী টাইপ করুন" + "ব্লুটুথ সেশন কী প্রয়োজন" + "%1$s এর মাধ্যমে সংযোগ স্বীকার করার সময় অতিবাহিত হয়ে গেছে" + "%1$s এর মাধ্যেমে সেশন কী ইনপুট করার সময় অতিবাহিত হয়ে গেছে" + "Obex যাচাইকরণ অনুরোধ" + "সেশন কী" + "%1$s এর জন্য সেশন কী টাইপ করুন" + "কারকিট" + "অজানা নাম" + "আমার নাম" + "০০০০০০" + "ব্লুটুথ পরিচিতির সাথে শেয়ার করা" + diff --git a/android/app/res/values-bn/strings_pbap_client.xml b/android/app/res/values-bn/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-bn/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-bn/strings_sap.xml b/android/app/res/values-bn/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..35329d8fd788a562cbc18109e51e27401df87bf4 --- /dev/null +++ b/android/app/res/values-bn/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ব্লুটুথ সিম -এ অ্যাক্সেস করুন" + "ব্লুটুথ সিম -এ অ্যাক্সেস করুন" + "ক্লায়েন্ট সংযোগ বিচ্ছিন্ন করার অনুরোধ জানাবেন?" + "সংযোগ বিচ্ছিন্ন করতে ক্লায়েন্টের জন্য অপেক্ষা করা" + "সংযোগ বিচ্ছিন্ন করুন" + "জোর করে সংযোগ বিচ্ছিন্ন করুন" + diff --git a/android/app/res/values-bn/test_strings.xml b/android/app/res/values-bn/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..65c5331022741cd69d47eabf30a3eae450999e0b --- /dev/null +++ b/android/app/res/values-bn/test_strings.xml @@ -0,0 +1,13 @@ + + + "ব্লুটুথ" + "রেকর্ড ঢোকান" + "রেকর্ড নিশ্চিত করুন" + "Ack রেকর্ড" + "সব রেকর্ড মুছে দিন" + "ঠিক আছে" + "রেকর্ড মুছে দিন" + "TCP সার্ভার শুরু করুন" + "TCP সার্ভার সূচিত করুন" + diff --git a/android/app/res/values-bs/config.xml b/android/app/res/values-bs/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-bs/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-bs/strings.xml b/android/app/res/values-bs/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c044d64cb8fc135f19710f817cfae455225208ef --- /dev/null +++ b/android/app/res/values-bs/strings.xml @@ -0,0 +1,139 @@ + + + + + "Pristupite upravitelju za preuzimanja." + "Dozvoljava aplikaciji da pristupa BluetoothShare upravitelju i koristi ga za prenošenje fajlova." + "Stavi pristup Bluetooth uređaja na listu prihvaćenih." + "Dozvoljava aplikaciji da privremeno stavi Bluetooth uređaj na listu prihvaćenih, čime mu se omogućava da šalje fajlove na ovaj uređaj bez potvrde korisnika." + "Bluetooth" + "Nepoznat uređaj" + "Nepoznato" + "Način rada u avionu" + "Ne možete koristiti Bluetooth u načinu rada u avionu." + + "Da biste koristili Bluetooth usluge, prvo morate uključiti Bluetooth." + "Želite uključiti Bluetooth sada?" + "Otkaži" + "Uključi" + "Prenošenje fajla" + "Prihvatiti dolazni fajl?" + "Odbij" + "Prihvati" + "Uredu" + "Isteklo je vrijeme prilikom prihvatanja dolaznog fajla koji šalje \"%1$s\"" + "Dolazni fajl" + "%1$s sada može poslati %2$s" + "Bluetooth dijeljenje: Prima se fajl %1$s" + "Bluetooth dijeljenje: Primljen fajl %1$s" + "Bluetooth dijeljenje: Fajl %1$s nije primljen" + "Bluetooth dijeljenje: Slanje fajla %1$s" + "Bluetooth dijeljenje: Poslan fajl %1$s" + "Dovršeno 100%" + "Bluetooth dijeljenje: Fajl %1$s nije poslan" + "Prenošenje fajla" + "Šalje: \"%1$s\"" + "Fajl: %1$s" + "Veličina fajla: %1$s" + + "Primanje fajla…" + "Zaustavi" + "Sakrij" + "Šalje" + "Naziv fajla" + "Veličina" + "Fajl nije primljen" + "Fajl: %1$s" + "Razlog: %1$s" + "Uredu" + "Fajl primljen" + "Otvori" + "Prima: \"%1$s\"" + "Vrsta fajla: %1$s (%2$s)" + "Slanje fajla…" + "Fajl poslan" + "Uredu" + "Fajl kojeg prima \"%1$s\" nije poslan." + "Fajl: %1$s" + "Zatvori" + "Uredu" + "Nepoznat fajl" + "Nema aplikacije za rukovanje ovom vrstom fajla. \n" + "Nema fajla" + "Fajl ne postoji. \n" + "Pričekajte…" + "Uključuje se Bluetooth…" + "Fajl će biti primljen. Pratite napredak u Ploči s obavještenjima." + "Nije moguće primiti fajl." + "Zaustavljeno primanje fajla kojeg šalje \"%1$s\"" + "Slanje fajla kojeg prima \"%1$s\"" + "Slanje %1$s fajl(ov)a, prima \"%2$s\"" + "Zaustavljeno slanje fajla kojeg prima \"%1$s\"" + "Na USB pohrani nema dovoljno prostora da se sačuva ovaj fajl." + "Na SD kartici nema dovoljno prostora da se sačuva ovaj fajl." + "Potrebni prostor: %1$s" + "Obrađuje se previše zahtjeva. Pokušajte ponovo kasnije." + "Prenošenje fajla još nije započelo." + "Prenošenje fajla je u toku." + "Prenošenje fajla je uspješno dovršeno." + "Sadržaj nije podržan." + "Ciljni uređaj je zabranio prenošenje." + "Korisnik je otkazao prenošenje." + "Problem s pohranom." + "Nema USB pohrane." + "Nema SD kartice. Umetnite SD karticu kako biste sačuvali prenesene fajlove." + "Povezivanje nije uspjelo." + "Nije moguće pravilno obraditi zahtjev." + "Nepoznata greška." + "Primljeno putem Bluetootha" + "Dijeljenje putem Bluetootha" + "%1$s Primanje završeno." + "%1$s Slanje dovršeno." + "Dolazna prenošenja" + "Odlazna prenošenja" + "Historija prijenosa je prazna." + "Sve stavke će biti izbrisane sa spiska." + "Bluetooth dijeljenje: Poslani fajlovi" + "Bluetooth dijeljenje: Primljeni fajlovi" + + %1$d neuspješan. + %1$d neuspješna. + %1$d neuspješnih. + + + %1$d uspješan, %2$s + %1$d uspješna, %2$s + %1$d uspješnih, %2$s + + "Obriši spisak" + "Otvori" + "Obriši sa spiska" + "Obriši" + "Trenutno se reproducira" + "Sačuvaj" + "Otkaži" + "Odaberite račune koje želite dijeliti preko Bluetootha. I dalje morate prihvatiti bilo koji pristup računima prilikom povezivanja." + "Preostalo utora:" + "Ikona aplikacije" + "Postavke za dijeljenje Bluetooth poruka" + "Nije moguće odabrati račun. Preostalo je 0 utora" + "Bluetooth audio je povezan" + "Bluetooth audio je isključen" + "Bluetooth Audio" + "Nije moguće prenijeti fajlove veće od 4 GB" + "Poveži se na Bluetooth" + diff --git a/android/app/res/values-bs/strings_pbap.xml b/android/app/res/values-bs/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..55bbe3c5acf38392a0df3b0fbc12da93c2027116 --- /dev/null +++ b/android/app/res/values-bs/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Unesite ključ sesije za uređaj %1$s" + "Neophodan je ključ za Bluetooth sesiju" + "Isteklo je vrijeme za prihvatanje veze sa uređajem %1$s" + "Isteklo je vrijeme za unošenje ključa sesije sa uređajem %1$s" + "Zahtjev za Obex autentifikaciju" + "Ključ sesije" + "Unesite ključ sesije za %1$s" + "Komplet za automobil" + "Nepoznato ime" + "Moje ime" + "000000" + "Dijeljenje kontakata putem Bluetootha" + diff --git a/android/app/res/values-bs/strings_pbap_client.xml b/android/app/res/values-bs/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-bs/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-bs/strings_sap.xml b/android/app/res/values-bs/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..ab906d46f342a800242daed3e9f2e2d76f69f027 --- /dev/null +++ b/android/app/res/values-bs/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth pristup SIM-u" + "Bluetooth pristup SIM-u" + "Tražiti od klijenta da prekine vezu?" + "Čeka se da klijent prekine vezu" + "Prekini vezu" + "Prisilno prekini vezu" + diff --git a/android/app/res/values-bs/test_strings.xml b/android/app/res/values-bs/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..58738174cb8ccda899f28d51bc8fbbe49af1c519 --- /dev/null +++ b/android/app/res/values-bs/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Umetni zapis" + "Potvrdi zapis" + "Evidencija Ack" + "Izbriši svu evidenciju" + "Uredu" + "Izbriši evidenciju" + "Pokreni TCP server" + "Obavijesti TCP server" + diff --git a/android/app/res/values-ca/config.xml b/android/app/res/values-ca/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ca/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ca/strings.xml b/android/app/res/values-ca/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7fba7076997f26d5c853124da2528cac3dd6857e --- /dev/null +++ b/android/app/res/values-ca/strings.xml @@ -0,0 +1,137 @@ + + + + + "Accediu al gestor de baixades." + "Permet que l\'aplicació accedeixi al gestor d\'ús compartit de Bluetooth i que l\'utilitzi per transferir fitxers." + "Afegeix l\'accés al dispositiu Bluetooth a la llista de permesos." + "Permet que l\'aplicació col·loqui temporalment en una llista de permesos un dispositiu Bluetooth, cosa que permet que el dispositiu enviï fitxers a aquest dispositiu sense la confirmació de l\'usuari." + "Bluetooth" + "Dispositiu desconegut" + "Desconegut" + "Mode d\'avió" + "No pots utilitzar el Bluetooth en mode d\'avió." + + "Per utilitzar serveis Bluetooth, primer cal que activeu el Bluetooth." + "Vols activar el Bluetooth ara?" + "Cancel·la" + "Activa" + "Transferència de fitxers" + "Acceptes el fitxer entrant?" + "Rebutja" + "Accepta" + "D\'acord" + "S\'ha esgotat el temps d\'espera mentre s\'acceptava un fitxer entrant de \"%1$s\"" + "Fitxer entrant" + "%1$s ja pot enviar %2$s" + "Bluetooth: s\'està rebent %1$s" + "Bluetooth: %1$s rebut" + "Bluetooth: %1$s no rebut" + "Bluetooth: s\'està enviant %1$s" + "Bluetooth: %1$s enviat" + "100% complet" + "Bluetooth: %1$s no enviat" + "Transferència de fitxers" + "De: \"%1$s\"" + "Fitxer: %1$s" + "Mida del fitxer: %1$s" + + "S\'està rebent un fitxer..." + "Atura" + "Amaga" + "De" + "Nom del fitxer" + "Mida" + "No s\'ha rebut el fitxer" + "Fitxer: %1$s" + "Motiu: %1$s" + "D\'acord" + "S\'ha rebut el fitxer" + "Obre" + "Per a: \"%1$s\"" + "Tipus de fitxer: %1$s (%2$s)" + "S\'està enviant el fitxer..." + "S\'ha enviat el fitxer" + "D\'acord" + "El fitxer no s\'ha enviat a \"%1$s\"." + "Fitxer: %1$s" + "Tanca" + "D\'acord" + "Fitxer desconegut" + "No hi ha cap aplicació per gestionar aquest tipus de fitxer. \n" + "Cap fitxer" + "El fitxer no existeix. \n" + "Espereu…" + "S\'està activant el Bluetooth..." + "El fitxer es rebrà. Comproveu-ne el progrés al tauler de notificacions." + "No es pot rebre el fitxer." + "S\'ha aturat la recepció del fitxer de \"%1$s\"" + "S\'està enviant el fitxer a \"%1$s\"" + "S\'estan enviant %1$s fitxers a \"%2$s\"" + "S\'ha aturat l\'enviament del fitxer a \"%1$s\"" + "No hi ha prou espai a l\'emmagatzematge USB per desar el fitxer." + "No hi ha prou espai a la targeta SD per desar el fitxer." + "Espai necessari: %1$s" + "S\'estan processant massa sol·licituds. Torneu-ho a provar més tard." + "Encara no s\'ha iniciat la transferència del fitxer." + "S\'està duent a terme la transferència de fitxers." + "La transferència de fitxers s\'ha completat correctament." + "El contingut no és compatible." + "El dispositiu de destinació no permet la transferència." + "L\'usuari ha cancel·lat la transferència." + "Problema d\'emmagatzematge." + "Sense emmagatzematge USB." + "No hi ha cap targeta SD. Insereix-ne una per desar els fitxers transferits." + "Connexió incorrecta." + "La sol·licitud no es pot processar correctament." + "Error desconegut." + "Rebut per Bluetooth" + "Compartir amb Bluetooth" + "Recepció completada (%1$s)" + "%1$s: enviament complet." + "Transferències d\'entrada" + "Transferències de sortida" + "L\'historial de transferències és buit." + "S\'esborraran tots els elements de la llista." + "Bluetooth: fitxers enviats" + "Bluetooth: fitxers rebuts" + + Incorrectes: %1$d. + Incorrectes: %1$d. + + + Correctes: %1$d, %2$s + Correctes: %1$d, %2$s + + "Esborra la llista" + "Obre" + "Esborra de la llista" + "Esborra" + "Reproducció actual" + "Desa" + "Cancel·la" + "Selecciona els comptes que vulguis compartir mitjançant el Bluetooth. Cal que acceptis l\'accés als comptes en connectar-t\'hi." + "Espais que queden:" + "Icona d\'aplicació" + "Configuració per compartir missatges mitjançant el Bluetooth" + "No es pot seleccionar el compte. No queda cap espai." + "Àudio per Bluetooth connectat" + "Àudio per Bluetooth desconnectat" + "Àudio per Bluetooth" + "No es poden transferir fitxers més grans de 4 GB" + "Connecta el Bluetooth" + diff --git a/android/app/res/values-ca/strings_pbap.xml b/android/app/res/values-ca/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..0e2b837f468dd1659a7347d45875b167c0c48f51 --- /dev/null +++ b/android/app/res/values-ca/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Introduïu la clau de sessió per a %1$s" + "Es necessita la clau de sessió Bluetooth" + "S\'ha esgotat el temps per acceptar la connexió amb %1$s" + "S\'ha esgotat el temps per a l\'entrada de la clau de sessió amb %1$s" + "Sol·licitud d\'autenticació Obex" + "Clau de sessió" + "Introduïu la clau de sessió per a %1$s" + "Equip per a l\'automòbil" + "Nom desconegut" + "El meu nom" + "000000" + "Compartir contactes amb Bluetooth" + diff --git a/android/app/res/values-ca/strings_pbap_client.xml b/android/app/res/values-ca/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ca/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ca/strings_sap.xml b/android/app/res/values-ca/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..8d56759d67c5b46db17464359a5aafae4e6f42b9 --- /dev/null +++ b/android/app/res/values-ca/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Accés SIM del Bluetooth" + "Accés SIM del Bluetooth" + "Vols sol·licitar al client que es desconnecti?" + "S\'està esperant que el client es desconnecti" + "Desconnecta" + "Força la desconnexió" + diff --git a/android/app/res/values-ca/test_strings.xml b/android/app/res/values-ca/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e1ea4fcd4b82ecfc1d9720d17e271cd961e92074 --- /dev/null +++ b/android/app/res/values-ca/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insereix un registre" + "Confirma el registre" + "Registre de notificacions" + "Suprimeix tots els registres" + "D\'acord" + "Suprimeix el registre" + "Inicia el servidor TCP" + "Notifica-ho al servidor TCP" + diff --git a/android/app/res/values-cs/config.xml b/android/app/res/values-cs/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-cs/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-cs/strings.xml b/android/app/res/values-cs/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..5416cab272bf64363804aa0c7ae59b61076cfd91 --- /dev/null +++ b/android/app/res/values-cs/strings.xml @@ -0,0 +1,141 @@ + + + + + "Získat přístup ke správci stahování." + "Umožňuje aplikaci přistupovat ke Správci sdílení Bluetooth a využívat jej k přenosu souborů." + "Povolit přístup zařízení Bluetooth." + "Umožňuje aplikaci dočasně povolit zařízení Bluetooth, aby tak mohlo odesílat soubory do tohoto zařízení bez potvrzení uživatele." + "Bluetooth" + "Neznámé zařízení" + "Neznámé" + "Režim Letadlo" + "V režimu Letadlo není možné Bluetooth použít." + + "Chcete-li používat služby Bluetooth, musíte Bluetooth nejprve zapnout." + "Zapnout Bluetooth?" + "Zrušit" + "Zapnout" + "Přenos souborů" + "Přijmout příchozí soubor?" + "Odmítnout" + "Přijmout" + "OK" + "Při příjmu příchozího souboru od uživatele %1$s vypršel časový limit." + "Příchozí soubor" + "%1$s – odeslání souboru %2$s je připraveno" + "Sdílení Bluetooth: Příjem souboru %1$s" + "Sdílení Bluetooth: Soubor %1$s byl přijat" + "Sdílení Bluetooth: Soubor %1$s nebyl přijat" + "Sdílení Bluetooth: Odesílání souboru %1$s" + "Sdílení Bluetooth: Soubor %1$s byl odeslán" + "Dokončeno 100 %" + "Sdílení Bluetooth: Soubor %1$s nebyl odeslán" + "Přenos souborů" + "Od: %1$s" + "Soubor: %1$s" + "Velikost souboru: %1$s" + + "Přijímání souboru..." + "Zastavit" + "Skrýt" + "Od" + "Název souboru" + "Velikost" + "Soubor nebyl přijat" + "Soubor: %1$s" + "Důvod: %1$s" + "OK" + "Soubor byl přijat" + "Otevřít" + "Komu: %1$s" + "Typ souboru: %1$s (%2$s)" + "Odesílání souboru..." + "Soubor byl odeslán" + "OK" + "Soubor nebyl odeslán zařízení %1$s." + "Soubor: %1$s" + "Zavřít" + "OK" + "Neznámý soubor" + "Žádná aplikace nedokáže s tímto typem souboru pracovat. \n" + "Žádný soubor" + "Soubor neexistuje. \n" + "Čekejte prosím..." + "Zapínání Bluetooth…" + "Proběhne přijímání souboru. Průběh můžete sledovat na panelu Oznámení." + "Soubor nelze přijmout." + "Příjem souboru od uživatele %1$s byl zastaven" + "Odesílání souboru uživateli %1$s" + "Odesílání %1$s souborů uživateli %2$s" + "Odesílání souboru uživateli %1$s bylo zastaveno" + "Na úložišti USB není dost místa k uložení souboru." + "Na SD kartě není dost místa k uložení souboru." + "Požadované místo v paměti: %1$s" + "Je zpracováváno příliš mnoho požadavků. Opakujte akci později." + "Přenos souborů ještě nebyl zahájen." + "Probíhá přenos souborů." + "Přenos souborů byl úspěšně dokončen." + "Obsah není podporován." + "Přenos byl cílovým zařízením zakázán." + "Přenos byl zrušen uživatelem." + "Problém s úložištěm." + "Žádné úložiště USB." + "Žádná SD karta není dostupná. Chcete-li přenášené soubory uložit, vložte SD kartu." + "Připojení se nezdařilo." + "Požadavek není možné správně zpracovat." + "Neznámá chyba." + "Bluetooth – přijaté soubory" + "Sdílení Bluetooth" + "%1$s Přijetí dokončeno." + "%1$s Odeslání dokončeno." + "Příchozí přenosy" + "Odchozí přenosy" + "Historie přenosů je prázdná." + "Ze seznamu budou vymazány všechny položky." + "Sdílení Bluetooth: Odeslané soubory" + "Sdílení Bluetooth: Přijaté soubory" + + %1$d neúspěšné. + %1$d neúspěšného. + %1$d neúspěšných. + %1$d neúspěšné. + + + %1$d úspěšné, %2$s + %1$d úspěšného, %2$s + %1$d úspěšných, %2$s + %1$d úspěšné, %2$s + + "Vymazat obsah seznamu" + "Otevřít" + "Vymazat ze seznamu" + "Vymazat" + "Co to hraje" + "Uložit" + "Zrušit" + "Vyberte účty, které chcete sdílet prostřednictvím rozhraní Bluetooth. Při připojování budete přístup k účtům muset i nadále schválit." + "Zbývající sloty:" + "Ikona aplikace" + "Nastavení sdílení zpráv přes Bluetooth" + "Účet nelze vybrat. Nezbývají žádné sloty." + "Bluetooth Audio – připojeno" + "Bluetooth Audio – odpojeno" + "Bluetooth Audio" + "Soubory větší než 4 GB nelze přenést" + "Připojit k Bluetooth" + diff --git a/android/app/res/values-cs/strings_pbap.xml b/android/app/res/values-cs/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..94f85f493de7617ae581b8f2cb0b81a86fabbb18 --- /dev/null +++ b/android/app/res/values-cs/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Zadejte klíč relace pro zařízení %1$s" + "Je požadován klíč relace Bluetooth" + "Časový limit pro přijetí spojení se zařízením „%1$s“ vypršel" + "Časový limit zadání klíče relace pro %1$s vypršel" + "Požadavek ověření Obex" + "Klíč relace" + "Zadejte klíč relace pro zařízení %1$s" + "Sada handsfree do auta" + "Neznámý název" + "Mé jméno" + "000000" + "Sdílení kontaktu přes Bluetooth" + diff --git a/android/app/res/values-cs/strings_pbap_client.xml b/android/app/res/values-cs/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-cs/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-cs/strings_sap.xml b/android/app/res/values-cs/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2797aea4d0003f1f46c928dc4992461a658c17c4 --- /dev/null +++ b/android/app/res/values-cs/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Přístup k SIM kartě přes Bluetooth" + "Přístup k SIM kartě přes Bluetooth" + "Chcete klient požádat o odpojení?" + "Čekání na odpojení klientu" + "Odpojit" + "Vynutit odpojení" + diff --git a/android/app/res/values-cs/test_strings.xml b/android/app/res/values-cs/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2350a8d3d396add8f0f98ee146b39fed6229365 --- /dev/null +++ b/android/app/res/values-cs/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Vložit záznam" + "Potvrdit záznam" + "Záznam ACK" + "Smazat všechny záznamy" + "OK" + "Smazat záznam" + "Spustit server TCP" + "Upozornit server TCP" + diff --git a/android/app/res/values-da/config.xml b/android/app/res/values-da/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-da/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-da/strings.xml b/android/app/res/values-da/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..54c9dd19f2509f78030b4de6e939bcafc9bb0cee --- /dev/null +++ b/android/app/res/values-da/strings.xml @@ -0,0 +1,137 @@ + + + + + "Få adgang til downloadadministrator." + "Tillader, at appen får adgang til BluetoothShare-administratoren og kan bruge den til overførsel af filer." + "Godkend adgang for Bluetooth-enhed" + "Tillader, at appen midlertidigt godkender en Bluetooth-enhed, så der kan sendes filer fra den pågældende enhed til din enhed uden bekræftelse fra brugerens side." + "Bluetooth" + "Ukendt enhed" + "Ukendt" + "Flytilstand" + "Du kan ikke anvende Bluetooth i flytilstand." + + "Du skal aktivere Bluetooth, før du kan bruge Bluetooth-tjenester." + "Vil du slå Bluetooth til nu?" + "Annuller" + "Slå til" + "Filoverførsel" + "Vil du acceptere den indgående fil?" + "Afvis" + "Accepter" + "OK" + "Der opstod timeout ved modtagelse af indgående fil fra \"%1$s\"" + "Indgående fil" + "%1$s er klar til at sende %2$s" + "Bluetooth-deling: Modtager %1$s" + "Bluetooth-deling: Modtog %1$s" + "Bluetooth-deling: Filen %1$s blev ikke modtaget" + "Bluetooth-deling: Sender %1$s" + "Bluetooth-deling: Sendt %1$s" + "100 % fuldført" + "Bluetooth-deling: Filen %1$s blev ikke sendt" + "Filoverførsel" + "Fra: \"%1$s\"" + "Fil: %1$s" + "Filstørrelse: %1$s" + + "Modtager fil…" + "Stop" + "Skjul" + "Fra" + "Filnavn" + "Størrelse" + "Filen blev ikke modtaget" + "Fil: %1$s" + "Årsag: %1$s" + "OK" + "Filen blev modtaget" + "Åbn" + "Til: \"%1$s\"" + "Filtype: %1$s (%2$s)" + "Sender fil…" + "Filen er sendt" + "OK" + "Filen blev ikke sendt til \"%1$s\"." + "Fil: %1$s" + "Luk" + "OK" + "Ukendt fil" + "Der er ingen app til at håndtere denne filtype. \n" + "Ingen fil" + "Filen findes ikke. \n" + "Vent..." + "Aktiverer Bluetooth..." + "Filen modtages. Se status i underretningspanelet." + "Filen kan ikke modtages." + "Modtagelse af fil fra \"%1$s\" stoppet" + "Sender filen til \"%1$s\"" + "Sender %1$s filer til \"%2$s\"" + "Afsendelse af fil til \"%1$s\" stoppede" + "Der er ikke nok plads på USB-lageret til at gemme filen." + "Der er ikke nok plads på SD-kortet til at gemme filen." + "Nødvendig plads: %1$s" + "Der behandles for mange anmodninger. Prøv igen senere." + "Filoverførslen er endnu ikke påbegyndt." + "Filoverførslen er i gang." + "Filoverførslen blev gennemført." + "Indholdet understøttes ikke." + "Modtagerenheden forbyder overførslen." + "Overførslen blev annulleret af brugeren." + "Lagerproblem." + "Intet USB-lager." + "Intet SD-kort. Indsæt et SD-kort for at gemme overførte filer." + "Forbindelsen mislykkedes." + "Anmodningen kan ikke håndteres korrekt." + "Ukendt fejl." + "Modtaget via Bluetooth" + "Bluetooth-deling" + "%1$s Modtaget." + "%1$s Afsendelse fuldført." + "Indgående overførsler" + "Udgående overførsler" + "Overførselshistorikken er tom." + "Alle elementer vil blive fjernet fra listen." + "Bluetooth-deling: Sendte filer" + "Bluetooth-deling: Modtagne filer" + + %1$d mislykkedes. + %1$d mislykkedes. + + + %1$d lykkedes, %2$s + %1$d lykkedes, %2$s + + "Ryd liste" + "Åbn" + "Fjern fra listen" + "Ryd" + "Afspiller nu" + "Gem" + "Annuller" + "Vælg de konti, du vil dele via Bluetooth. Du skal stadig acceptere adgang til kontiene, når du opretter forbindelse." + "Pladser tilbage:" + "Appens ikon" + "Indstillinger for beskeddeling via Bluetooth" + "Kontoen kan ikke vælges. Der er ikke flere pladser tilbage" + "Bluetooth-lyden blev tilsluttet" + "Bluetooth-lyden blev afbrudt" + "Bluetooth-lyd" + "File, der er større end 4 GB, kan ikke overføres" + "Opret forbindelse til Bluetooth" + diff --git a/android/app/res/values-da/strings_pbap.xml b/android/app/res/values-da/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..48372605aa4ddce0b9bd394247d67d36c0751637 --- /dev/null +++ b/android/app/res/values-da/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Angiv sessionsnøgle til %1$s" + "Bluetooth-sessionsnøgle kræves" + "Der var timeout ved forbindelsen med %1$s" + "Der var timeout i indgangssessionnøgle med %1$s" + "Anmodning om Obex-godkendelse" + "Sessionstast" + "Angiv sessionsnøgle til %1$s" + "Bilsæt" + "Ukendt navn" + "Mit navn" + "000000" + "Deling af kontakter via Bluetooth" + diff --git a/android/app/res/values-da/strings_pbap_client.xml b/android/app/res/values-da/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-da/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-da/strings_sap.xml b/android/app/res/values-da/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..0fff43b477bdcf621ee2d83a4918b56901072a22 --- /dev/null +++ b/android/app/res/values-da/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth-adgang til SIM-kort" + "Bluetooth-adgang til SIM-kort" + "Vil du anmode klienten om at afbryde forbindelsen?" + "Venter på, at klienten afbryder forbindelsen" + "Afbryd" + "Gennemtving afbrydelse af forbindelsen" + diff --git a/android/app/res/values-da/test_strings.xml b/android/app/res/values-da/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..8d20d4465a4a5f602065b90bf5a7f4e8787548cb --- /dev/null +++ b/android/app/res/values-da/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Indsæt post" + "Bekræft post" + "Ack-post" + "Slet alle poster" + "OK" + "Slet post" + "Start TCP-server" + "Meddel TCP-server" + diff --git a/android/app/res/values-de/config.xml b/android/app/res/values-de/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-de/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-de/strings.xml b/android/app/res/values-de/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..da776dc927761df142a1ee4d74fe0ccd00133d6e --- /dev/null +++ b/android/app/res/values-de/strings.xml @@ -0,0 +1,137 @@ + + + + + "Auf Download-Manager zugreifen" + "Ermöglicht der App, auf den Bluetooth-Weiterleitungs-Manager zuzugreifen und diesen für die Übertragung von Dateien zu verwenden." + "Bluetooth-Gerät für Zugriff zur Zulassungsliste hinzufügen" + "Ermöglicht der App, ein Bluetooth-Gerät vorübergehend zur Zulassungsliste hinzuzufügen, sodass es ohne Bestätigung des Nutzers Dateien an dieses Gerät senden kann." + "Bluetooth" + "Unbekanntes Gerät" + "Unbekannter Anrufer" + "Flugmodus" + "Du kannst Bluetooth im Flugmodus nicht verwenden." + + "Damit du Bluetooth-Dienste nutzen kannst, musst du Bluetooth zuerst aktivieren." + "Bluetooth jetzt aktivieren?" + "Abbrechen" + "Aktivieren" + "Dateiübertragung" + "Eingehende Datei annehmen?" + "Ablehnen" + "Akzeptieren" + "Ok" + "Die Zeit zum Empfang der eingehenden Datei von \"%1$s\" ist abgelaufen." + "Eingehende Datei" + "%1$s kann jetzt %2$s senden." + "Bluetooth-Freigabe: %1$s wird empfangen" + "Bluetooth-Freigabe: %1$s wurde empfangen" + "Bluetooth-Freigabe: %1$s wurde nicht empfangen" + "Bluetooth-Freigabe: %1$s wird gesendet" + "Bluetooth-Freigabe: %1$s gesendet" + "Zu 100 % abgeschlossen" + "Bluetooth-Freigabe: %1$s wurde nicht gesendet" + "Dateiübertragung" + "Von: \"%1$s\"" + "Datei: %1$s" + "Dateigröße: %1$s" + + "Datei wird heruntergeladen..." + "Abbrechen" + "Ausblenden" + "Von" + "Dateiname" + "Größe" + "Datei nicht empfangen" + "Datei: %1$s" + "Grund: %1$s" + "Ok" + "Datei empfangen" + "Öffnen" + "An: \"%1$s\"" + "Dateityp: %1$s (%2$s)" + "Datei wird gesendet..." + "Die Datei wurde gesendet." + "Ok" + "Die Datei wurde nicht an \"%1$s\" gesendet." + "Datei: %1$s" + "Schließen" + "Ok" + "Unbekannte Datei" + "Dieser Dateityp kann von keiner App verarbeitet werden. \n" + "Keine Datei" + "Die Datei ist nicht vorhanden. \n" + "Bitte warten..." + "Bluetooth wird aktiviert..." + "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..." + "%1$s Dateien werden an \"%2$s\" gesendet." + "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" + "Es werden zurzeit zu viele Anfragen verarbeitet. Bitte versuche es später noch einmal." + "Die Dateiübertragung wurde noch nicht gestartet." + "Dateiübertragung läuft." + "Die Dateiübertragung wurde abgeschlossen." + "Der Inhalt wird nicht unterstützt." + "Übertragung wird durch das Zielgerät verhindert." + "Übertragung wurde vom Nutzer abgebrochen." + "Speicherproblem" + "Kein USB-Speicher." + "Keine SD-Karte. Lege eine SD-Karte ein, um die übertragenen Dateien zu speichern." + "Verbindung fehlgeschlagen" + "Die Anfrage kann nicht richtig verarbeitet werden." + "Unbekannter Fehler" + "Per Bluetooth empfangen" + "Bluetooth-Freigabe" + "%1$s vollständig empfangen." + "%1$s vollständig gesendet." + "Eingehende Übertragungen" + "Ausgehende Übertragungen" + "Übertragungsverlauf ist leer." + "Alle Elemente werden aus der Liste gelöscht." + "Bluetooth-Freigabe: Gesendete Dateien" + "Bluetooth-Freigabe: Empfangene Dateien" + + Fehler bei %1$d aufgetreten + Fehler bei %1$d aufgetreten + + + %1$d erfolgreich, %2$s + %1$d erfolgreich, %2$s + + "Liste löschen" + "Öffnen" + "Aus Liste löschen" + "Löschen" + "Now Playing" + "Speichern" + "Abbrechen" + "Wähle die Konten aus, die du über Bluetooth freigeben möchtest. Du musst jedoch weiterhin jedem Zugriff auf die Konten zustimmen, wenn eine Verbindung hergestellt wird." + "Plätze frei:" + "App-Symbol" + "Einstellungen zur Bluetooth-Nachrichtenfreigabe" + "Konto kann nicht ausgewählt werden. 0 Plätze frei." + "Bluetooth-Audio verbunden" + "Bluetooth-Audio-Verbindung aufgehoben" + "Bluetooth-Audio" + "Dateien mit mehr als 4 GB können nicht übertragen werden" + "Mit Bluetooth verbinden" + diff --git a/android/app/res/values-de/strings_pbap.xml b/android/app/res/values-de/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..9988e62a18b883039ef6f705138f0ed6078b3bc5 --- /dev/null +++ b/android/app/res/values-de/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Sitzungsschlüssel für %1$s eingeben" + "Bluetooth-Sitzungsschlüssel erforderlich" + "Die Zeit zum Verbindungsaufbau mit %1$s ist abgelaufen." + "Die Zeit zur Eingabe des Sitzungsschlüssels bei %1$s ist abgelaufen." + "OBEX-Authentifizierungsanfrage" + "Sitzungsschlüssel" + "Sitzungsschlüssel für %1$s eingeben" + "Carkit" + "Unbekannter Name" + "Mein Name" + "000000" + "Kontakte über Bluetooth teilen" + diff --git a/android/app/res/values-de/strings_pbap_client.xml b/android/app/res/values-de/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-de/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-de/strings_sap.xml b/android/app/res/values-de/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..02db3c72511cc25af7a45815d46ea898c6aac740 --- /dev/null +++ b/android/app/res/values-de/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Zugriff auf SIM über Bluetooth" + "Zugriff auf SIM über Bluetooth" + "Client zum Trennen der Verbindung auffordern?" + "Warten, bis die Verbindung durch den Client getrennt wird" + "Verbindung trennen" + "Trennen der Verbindung erzwingen" + diff --git a/android/app/res/values-de/test_strings.xml b/android/app/res/values-de/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e07229e9b2ddc2689f5cbf1af4d2235b6c1117b --- /dev/null +++ b/android/app/res/values-de/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Aufnahme einfügen" + "Aufnahme bestätigen" + "Aufnahme bestätigen" + "Gesamte Aufnahme löschen" + "Ok" + "Aufnahme löschen" + "TCP-Server starten" + "TCP-Server benachrichtigen" + diff --git a/android/app/res/values-el/config.xml b/android/app/res/values-el/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-el/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-el/strings.xml b/android/app/res/values-el/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f9893b08aced34f7c0072052822ee12778dc8c3d --- /dev/null +++ b/android/app/res/values-el/strings.xml @@ -0,0 +1,137 @@ + + + + + "Πρόσβαση στη διαχείριση λήψεων." + "Επιτρέπει στην εφαρμογή να αποκτά πρόσβαση στο πρόγραμμα διαχείρισης BluetoothShare και να το χρησιμοποιεί για τη μεταφορά αρχείων." + "Πρόσβαση συσκευής Bluetooth επιτρεπόμενης λίστας." + "Επιτρέπει στην εφαρμογή την προσωρινή προσθήκη συσκευής Bluetooth σε μια λίστα επιτρεπόμενων συσκευών, δίνοντας τη δυνατότητα στη συγκεκριμένη συσκευή να αποστέλλει αρχεία σε αυτήν τη συσκευή χωρίς επιβεβαίωση χρήστη." + "Bluetooth" + "Άγνωστη συσκευή" + "Άγνωστος" + "Λειτουργία πτήσης" + "Δεν μπορείτε να χρησιμοποιήσετε το Bluetooth σε Λειτουργία πτήσης." + + "Για να χρησιμοποιήσετε υπηρεσίες Bluetooth, θα πρέπει πρώτα να ενεργοποιήσετε τη λειτουργία Bluetooth." + "Να γίνει τώρα ενεργοποίηση του Bluetooth;" + "Ακύρωση" + "Ενεργοποίηση" + "Μεταφορά αρχείου" + "Αποδοχή εισερχόμενου αρχείου;" + "Απόρριψη" + "Αποδοχή" + "OK" + "Σημειώθηκε διακοπή κατά την αποδοχή ενός εισερχόμενου αρχείου από τον αποστολέα \"%1$s\"" + "Εισερχόμενο αρχείο" + "Ο χρήστης %1$s πρόκειται να στείλει το αρχείο %2$s" + "Μοιραστείτε μέσω Bluetooth: Λήψη του %1$s" + "Μοιραστείτε μέσω Bluetooth: Ελήφθη το %1$s" + "Μοιραστείτε μέσω Bluetooth: Το αρχείο %1$s δεν ελήφθη" + "Μοιραστείτε μέσω Bluetooth: Αποστολή του %1$s" + "Μοιραστείτε μέσω Bluetooth: Εστάλη το %1$s" + "Ολοκληρώθηκε το 100%" + "Μοιραστείτε μέσω Bluetooth: Το αρχείο %1$s δεν εστάλη" + "Μεταφορά αρχείου" + "Από: \"%1$s\"" + "Αρχείο: %1$s" + "Μέγεθος αρχείου: %1$s" + + "Λήψη αρχείου..." + "Διακοπή" + "Απόκρυψη" + "Από" + "Όνομα αρχείου" + "Μέγεθος" + "Δεν ελήφθη το αρχείο" + "Αρχείο: %1$s" + "Λόγος: %1$s" + "ΟΚ" + "Το αρχείο ελήφθη" + "Άνοιγμα" + "Προς: \"%1$s\"" + "Τύπος αρχείου: %1$s (%2$s)" + "Αποστολή αρχείου..." + "Το αρχείο εστάλη" + "ΟΚ" + "Το αρχείο δεν εστάλη στον παραλήπτη \"%1$s\"." + "Αρχείο: %1$s" + "Κλείσιμο" + "ΟΚ" + "Άγνωστο αρχείο" + "Δεν υπάρχει καμία εφαρμογή για τη διαχείριση αυτού του τύπου αρχείου. \n" + "Κανένα αρχείο" + "Το αρχείο δεν υπάρχει. \n" + "Περιμένετε..." + "Ενεργοποίηση Bluetooth…" + "Θα γίνει λήψη του αρχείου. Ελέγξτε την πρόοδο στο πλαίσιο \"Ειδοποιήσεις\"." + "Δεν είναι δυνατή η παραλαβή του αρχείου." + "Διακόπηκε η λήψη του αρχείου από τον αποστολέα \"%1$s\"" + "Γίνεται αποστολή του αρχείου στον παραλήπτη \"%1$s\"" + "Αποστολή %1$s αρχείων στον παραλήπτη \"%2$s\"" + "Διακόπηκε η αποστολή του αρχείου στον παραλήπτη \"%1$s\"" + "Δεν υπάρχει αρκετός χώρος στον αποθηκευτικό χώρο USB για την αποθήκευση του αρχείου." + "Δεν υπάρχει αρκετός χώρος στην κάρτα SD για την αποθήκευση του αρχείου." + "Απαιτούμενος χώρος: %1$s" + "Πραγματοποιείται επεξεργασία πάρα πολλών αιτημάτων. Προσπαθήστε ξανά αργότερα." + "Η μεταφορά του αρχείου δεν έχει ξεκινήσει ακόμα." + "Η μεταφορά του αρχείου βρίσκεται σε εξέλιξη." + "Η μεταφορά του αρχείου ολοκληρώθηκε με επιτυχία." + "Το περιεχόμενο δεν υποστηρίζεται." + "Δεν επιτρέπεται αυτή η μεταφορά από τη συσκευή προορισμού." + "Η μεταφορά ακυρώθηκε από τον χρήστη." + "Πρόβλημα αποθηκευτικού χώρου." + "Δεν υπάρχει αποθηκευτικός χώρος USB." + "Δεν υπάρχει κάρτα SD card. Εισαγάγετε μια κάρτα SD για να αποθηκεύετε τα μεταφερόμενα αρχεία." + "Η σύνδεση δεν ήταν επιτυχής." + "Δεν μπορεί να γίνει σωστός χειρισμός του αιτήματος." + "Άγνωστο σφάλμα." + "Ελήφθη μέσω Bluetooth" + "Μοιραστείτε μέσω Bluetooth" + "Ελήφθησαν πλήρως %1$s." + "Ολοκληρώθηκε η αποστολή %1$s." + "Εισερχόμενες μεταφορές" + "Εξερχόμενες μεταφορές" + "Το ιστορικό μεταφορών είναι κενό." + "Όλα τα στοιχεία από τη λίστα θα διαγραφούν." + "Κοινή χρήση Bluetooth: Απεσταλμένα αρχεία" + "Κοινή χρήση Bluetooth: Ληφθέντα αρχεία" + + %1$d ανεπιτυχή. + %1$d ανεπιτυχές. + + + %1$d επιτυχή, %2$s + %1$d επιτυχές, %2$s + + "Διαγραφή λίστας" + "Άνοιγμα" + "Διαγραφή από τη λίστα" + "Διαγραφή" + "Ακούγεται τώρα" + "Αποθήκευση" + "Ακύρωση" + "Επιλέξτε τους λογαριασμούς που θέλετε να μοιραστείτε μέσω Bluetooth. Θα πρέπει ακόμη να αποδεχτείτε τυχόν αιτήματα πρόσβασης στους λογαριασμούς κατά τη σύνδεση." + "Υποδοχές που απομένουν:" + "Εικονίδιο εφαρμογής" + "Ρυθμίσεις κοινής χρήσης μηνυμάτων μέσω Bluetooth" + "Δεν είναι δυνατή η επιλογή λογαριασμού. Απομένουν 0 υποδοχές" + "Ο ήχος Bluetooth συνδέθηκε" + "Ο ήχος Bluetooth αποσυνδέθηκε" + "Ήχος Bluetooth" + "Δεν είναι δυνατή η μεταφορά αρχείων που ξεπερνούν τα 4 GB" + "Σύνδεση σε Bluetooth" + diff --git a/android/app/res/values-el/strings_pbap.xml b/android/app/res/values-el/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..634256fba79150b645f5fa676dee497319f98bfc --- /dev/null +++ b/android/app/res/values-el/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Πληκτρολογήστε το κλειδί της περιόδου σύνδεσης για το %1$s" + "Απαιτείται κλειδί περιόδου σύνδεσης Bluetooth" + "Υπήρχε χρονικό όριο για την αποδοχή της σύνδεσης με τη συσκευή %1$s" + "Το χρονικό όριο του κλειδιού περιόδου σύνδεσης εισόδου με το %1$s έληξε" + "Αίτημα ελέγχου ταυτότητας Obex" + "Κλειδί περιόδου σύνδεσης" + "Πληκτρολογήστε το κλειδί της περιόδου σύνδεσης για το %1$s" + "Κιτ αυτοκινήτου" + "Άγνωστο όνομα" + "Το όνομα μου" + "000000" + "Κοινοποίηση επαφών μέσω Bluetooth" + diff --git a/android/app/res/values-el/strings_pbap_client.xml b/android/app/res/values-el/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-el/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-el/strings_sap.xml b/android/app/res/values-el/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..5f3ae5e4ae64a4d49a684f93677d017d2c46a2b2 --- /dev/null +++ b/android/app/res/values-el/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Πρόσβαση SIM Bluetooth" + "Πρόσβαση SIM Bluetooth" + "Αίτημα εφαρμογής-πελάτη για αποσύνδεση;" + "Αναμονή για αποσύνδεση εφαρμογής-πελάτη" + "Αποσύνδεση" + "Αναγκαστική αποσύνδεση" + diff --git a/android/app/res/values-el/test_strings.xml b/android/app/res/values-el/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7fde900fa54011ba43f8aa661389865352bd1311 --- /dev/null +++ b/android/app/res/values-el/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Εισαγωγή αρχείου" + "Επιβεβαίωση αρχείου" + "Αρχείο ack" + "Διαγραφή όλων των αρχείων" + "ΟΚ" + "Διαγραφή αρχείου" + "Εκκίνηση διακομιστή TCP" + "Να ειδοποιείται ο διακομιστής TCP" + diff --git a/android/app/res/values-en-rAU/config.xml b/android/app/res/values-en-rAU/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-en-rAU/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-en-rAU/strings.xml b/android/app/res/values-en-rAU/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7db5afa95559ca35d172cd87eaf854db78dea5ea --- /dev/null +++ b/android/app/res/values-en-rAU/strings.xml @@ -0,0 +1,137 @@ + + + + + "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 temporarily acceptlist a Bluetooth device, allowing that device to send files to this device without user confirmation." + "Bluetooth" + "Unknown device" + "Unknown" + "Aeroplane mode" + "You can\'t use Bluetooth in Aeroplane mode." + + "To use Bluetooth services, you must first turn on Bluetooth." + "Turn on Bluetooth now?" + "Cancel" + "Turn on" + "File transfer" + "Accept incoming file?" + "Decline" + "Accept" + "OK" + "There was a timeout while accepting an incoming file from \"%1$s\"" + "Incoming file" + "%1$s is ready to send %2$s" + "Bluetooth share: Receiving %1$s" + "Bluetooth share: Received %1$s" + "Bluetooth share: File %1$s not received" + "Bluetooth share: Sending %1$s" + "Bluetooth share: Sent %1$s" + "100% complete" + "Bluetooth share: File %1$s not sent" + "File transfer" + "From: \"%1$s\"" + "File: %1$s" + "File size: %1$s" + + "Receiving file…" + "Stop" + "Hide" + "From" + "Filename" + "Size" + "File not received" + "File: %1$s" + "Reason: %1$s" + "OK" + "File received" + "Open" + "To: \"%1$s\"" + "File type: %1$s (%2$s)" + "Sending file…" + "File sent" + "OK" + "The file wasn\'t sent to \"%1$s\"." + "File: %1$s" + "Close" + "OK" + "Unknown file" + "There\'s no app to handle this type of file. \n" + "No file" + "The file doesn\'t exist. \n" + "Please wait…" + "Turning on Bluetooth…" + "The file will be received. Check progress in the Notifications panel." + "The file can\'t be received." + "Stopped receiving file from \"%1$s\"" + "Sending file to \"%1$s\"" + "Sending %1$s files to \"%2$s\"" + "Stopped sending file to \"%1$s\"" + "There isn\'t enough space in USB storage to save the file." + "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 is ongoing." + "File transfer completed successfully." + "Content isn\'t supported." + "Transfer forbidden by target device." + "Transfer cancelled by user." + "Storage issue" + "No USB storage." + "No SD card. Insert an SD card to save transferred files." + "Connection unsuccessful." + "Request can\'t be handled correctly." + "Unknown error." + "Bluetooth received" + "Bluetooth Share" + "%1$s Received complete." + "%1$s Sent complete." + "Inbound transfers" + "Outbound transfers" + "Transfer history is empty." + "All items will be cleared from the list." + "Bluetooth share: Sent files" + "Bluetooth share: Received files" + + %1$d unsuccessful. + %1$d unsuccessful. + + + %1$d successful, %2$s + %1$d successful, %2$s + + "Clear list" + "Open" + "Clear from list" + "Clear" + "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." + "Slots left:" + "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" + "Connect to Bluetooth" + diff --git a/android/app/res/values-en-rAU/strings_pbap.xml b/android/app/res/values-en-rAU/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd4e5120fbac4962aea689cc7a36b51716a4964c --- /dev/null +++ b/android/app/res/values-en-rAU/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "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" + "Obex authentication request" + "Session key" + "Type session key for %1$s" + "Car Kit" + "Unknown name" + "My name" + "000000" + "Bluetooth Contact share" + diff --git a/android/app/res/values-en-rAU/strings_pbap_client.xml b/android/app/res/values-en-rAU/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-en-rAU/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-en-rAU/strings_sap.xml b/android/app/res/values-en-rAU/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2ebae8c8562b4f0809c2a8e5d43bd13389d5149f --- /dev/null +++ b/android/app/res/values-en-rAU/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM access" + "Bluetooth SIM access" + "Request client to disconnect?" + "Waiting for client to disconnect" + "Disconnect" + "Force disconnect" + diff --git a/android/app/res/values-en-rAU/test_strings.xml b/android/app/res/values-en-rAU/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..63128eee321665468d5ea735ae51e97085db339d --- /dev/null +++ b/android/app/res/values-en-rAU/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insert record" + "Confirm record" + "Ack record" + "Delete all record" + "OK" + "Delete record" + "Start TCP server" + "Notify TCP server" + diff --git a/android/app/res/values-en-rCA/config.xml b/android/app/res/values-en-rCA/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-en-rCA/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-en-rCA/strings.xml b/android/app/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b07f6437328ae6a0de8191380f886b6a86b97c5c --- /dev/null +++ b/android/app/res/values-en-rCA/strings.xml @@ -0,0 +1,137 @@ + + + + + "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 temporarily acceptlist a Bluetooth device, allowing that device to send files to this device without user confirmation." + "Bluetooth" + "Unknown device" + "Unknown" + "Airplane mode" + "You can\'t use Bluetooth in Airplane mode." + + "To use Bluetooth services, you must first turn on Bluetooth." + "Turn on Bluetooth now?" + "Cancel" + "Turn on" + "File transfer" + "Accept incoming file?" + "Decline" + "Accept" + "OK" + "There was a timeout while accepting an incoming file from \"%1$s\"" + "Incoming file" + "%1$s is ready to send %2$s" + "Bluetooth share: Receiving %1$s" + "Bluetooth share: Received %1$s" + "Bluetooth share: File %1$s not received" + "Bluetooth share: Sending %1$s" + "Bluetooth share: Sent %1$s" + "100% complete" + "Bluetooth share: File %1$s not sent" + "File transfer" + "From: \"%1$s\"" + "File: %1$s" + "File size: %1$s" + + "Receiving file…" + "Stop" + "Hide" + "From" + "Filename" + "Size" + "File not received" + "File: %1$s" + "Reason: %1$s" + "OK" + "File received" + "Open" + "To: \"%1$s\"" + "File type: %1$s (%2$s)" + "Sending file…" + "File sent" + "OK" + "The file wasn\'t sent to \"%1$s\"." + "File: %1$s" + "Close" + "OK" + "Unknown file" + "There\'s no app to handle this type of file. \n" + "No file" + "The file doesn\'t exist. \n" + "Please wait…" + "Turning on Bluetooth…" + "The file will be received. Check progress in the Notifications panel." + "The file can\'t be received." + "Stopped receiving file from \"%1$s\"" + "Sending file to \"%1$s\"" + "Sending %1$s files to \"%2$s\"" + "Stopped sending file to \"%1$s\"" + "There isn\'t enough space in USB storage to save the file." + "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 is ongoing." + "File transfer completed successfully." + "Content isn\'t supported." + "Transfer forbidden by target device." + "Transfer cancelled by user." + "Storage issue" + "No USB storage." + "No SD card. Insert an SD card to save transferred files." + "Connection unsuccessful." + "Request can\'t be handled correctly." + "Unknown error." + "Bluetooth received" + "Bluetooth Share" + "%1$s Received complete." + "%1$s Sent complete." + "Inbound transfers" + "Outbound transfers" + "Transfer history is empty." + "All items will be cleared from the list." + "Bluetooth share: Sent files" + "Bluetooth share: Received files" + + %1$d unsuccessful. + %1$d unsuccessful. + + + %1$d successful, %2$s + %1$d successful, %2$s + + "Clear list" + "Open" + "Clear from list" + "Clear" + "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." + "Slots left:" + "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" + "Connect to Bluetooth" + diff --git a/android/app/res/values-en-rCA/strings_pbap.xml b/android/app/res/values-en-rCA/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd4e5120fbac4962aea689cc7a36b51716a4964c --- /dev/null +++ b/android/app/res/values-en-rCA/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "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" + "Obex authentication request" + "Session key" + "Type session key for %1$s" + "Car Kit" + "Unknown name" + "My name" + "000000" + "Bluetooth Contact share" + diff --git a/android/app/res/values-en-rCA/strings_pbap_client.xml b/android/app/res/values-en-rCA/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-en-rCA/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-en-rCA/strings_sap.xml b/android/app/res/values-en-rCA/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2ebae8c8562b4f0809c2a8e5d43bd13389d5149f --- /dev/null +++ b/android/app/res/values-en-rCA/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM access" + "Bluetooth SIM access" + "Request client to disconnect?" + "Waiting for client to disconnect" + "Disconnect" + "Force disconnect" + diff --git a/android/app/res/values-en-rCA/test_strings.xml b/android/app/res/values-en-rCA/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..63128eee321665468d5ea735ae51e97085db339d --- /dev/null +++ b/android/app/res/values-en-rCA/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insert record" + "Confirm record" + "Ack record" + "Delete all record" + "OK" + "Delete record" + "Start TCP server" + "Notify TCP server" + diff --git a/android/app/res/values-en-rGB/config.xml b/android/app/res/values-en-rGB/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-en-rGB/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-en-rGB/strings.xml b/android/app/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7db5afa95559ca35d172cd87eaf854db78dea5ea --- /dev/null +++ b/android/app/res/values-en-rGB/strings.xml @@ -0,0 +1,137 @@ + + + + + "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 temporarily acceptlist a Bluetooth device, allowing that device to send files to this device without user confirmation." + "Bluetooth" + "Unknown device" + "Unknown" + "Aeroplane mode" + "You can\'t use Bluetooth in Aeroplane mode." + + "To use Bluetooth services, you must first turn on Bluetooth." + "Turn on Bluetooth now?" + "Cancel" + "Turn on" + "File transfer" + "Accept incoming file?" + "Decline" + "Accept" + "OK" + "There was a timeout while accepting an incoming file from \"%1$s\"" + "Incoming file" + "%1$s is ready to send %2$s" + "Bluetooth share: Receiving %1$s" + "Bluetooth share: Received %1$s" + "Bluetooth share: File %1$s not received" + "Bluetooth share: Sending %1$s" + "Bluetooth share: Sent %1$s" + "100% complete" + "Bluetooth share: File %1$s not sent" + "File transfer" + "From: \"%1$s\"" + "File: %1$s" + "File size: %1$s" + + "Receiving file…" + "Stop" + "Hide" + "From" + "Filename" + "Size" + "File not received" + "File: %1$s" + "Reason: %1$s" + "OK" + "File received" + "Open" + "To: \"%1$s\"" + "File type: %1$s (%2$s)" + "Sending file…" + "File sent" + "OK" + "The file wasn\'t sent to \"%1$s\"." + "File: %1$s" + "Close" + "OK" + "Unknown file" + "There\'s no app to handle this type of file. \n" + "No file" + "The file doesn\'t exist. \n" + "Please wait…" + "Turning on Bluetooth…" + "The file will be received. Check progress in the Notifications panel." + "The file can\'t be received." + "Stopped receiving file from \"%1$s\"" + "Sending file to \"%1$s\"" + "Sending %1$s files to \"%2$s\"" + "Stopped sending file to \"%1$s\"" + "There isn\'t enough space in USB storage to save the file." + "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 is ongoing." + "File transfer completed successfully." + "Content isn\'t supported." + "Transfer forbidden by target device." + "Transfer cancelled by user." + "Storage issue" + "No USB storage." + "No SD card. Insert an SD card to save transferred files." + "Connection unsuccessful." + "Request can\'t be handled correctly." + "Unknown error." + "Bluetooth received" + "Bluetooth Share" + "%1$s Received complete." + "%1$s Sent complete." + "Inbound transfers" + "Outbound transfers" + "Transfer history is empty." + "All items will be cleared from the list." + "Bluetooth share: Sent files" + "Bluetooth share: Received files" + + %1$d unsuccessful. + %1$d unsuccessful. + + + %1$d successful, %2$s + %1$d successful, %2$s + + "Clear list" + "Open" + "Clear from list" + "Clear" + "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." + "Slots left:" + "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" + "Connect to Bluetooth" + diff --git a/android/app/res/values-en-rGB/strings_pbap.xml b/android/app/res/values-en-rGB/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd4e5120fbac4962aea689cc7a36b51716a4964c --- /dev/null +++ b/android/app/res/values-en-rGB/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "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" + "Obex authentication request" + "Session key" + "Type session key for %1$s" + "Car Kit" + "Unknown name" + "My name" + "000000" + "Bluetooth Contact share" + diff --git a/android/app/res/values-en-rGB/strings_pbap_client.xml b/android/app/res/values-en-rGB/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-en-rGB/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-en-rGB/strings_sap.xml b/android/app/res/values-en-rGB/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2ebae8c8562b4f0809c2a8e5d43bd13389d5149f --- /dev/null +++ b/android/app/res/values-en-rGB/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM access" + "Bluetooth SIM access" + "Request client to disconnect?" + "Waiting for client to disconnect" + "Disconnect" + "Force disconnect" + diff --git a/android/app/res/values-en-rGB/test_strings.xml b/android/app/res/values-en-rGB/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..63128eee321665468d5ea735ae51e97085db339d --- /dev/null +++ b/android/app/res/values-en-rGB/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insert record" + "Confirm record" + "Ack record" + "Delete all record" + "OK" + "Delete record" + "Start TCP server" + "Notify TCP server" + diff --git a/android/app/res/values-en-rIN/config.xml b/android/app/res/values-en-rIN/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-en-rIN/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-en-rIN/strings.xml b/android/app/res/values-en-rIN/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7db5afa95559ca35d172cd87eaf854db78dea5ea --- /dev/null +++ b/android/app/res/values-en-rIN/strings.xml @@ -0,0 +1,137 @@ + + + + + "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 temporarily acceptlist a Bluetooth device, allowing that device to send files to this device without user confirmation." + "Bluetooth" + "Unknown device" + "Unknown" + "Aeroplane mode" + "You can\'t use Bluetooth in Aeroplane mode." + + "To use Bluetooth services, you must first turn on Bluetooth." + "Turn on Bluetooth now?" + "Cancel" + "Turn on" + "File transfer" + "Accept incoming file?" + "Decline" + "Accept" + "OK" + "There was a timeout while accepting an incoming file from \"%1$s\"" + "Incoming file" + "%1$s is ready to send %2$s" + "Bluetooth share: Receiving %1$s" + "Bluetooth share: Received %1$s" + "Bluetooth share: File %1$s not received" + "Bluetooth share: Sending %1$s" + "Bluetooth share: Sent %1$s" + "100% complete" + "Bluetooth share: File %1$s not sent" + "File transfer" + "From: \"%1$s\"" + "File: %1$s" + "File size: %1$s" + + "Receiving file…" + "Stop" + "Hide" + "From" + "Filename" + "Size" + "File not received" + "File: %1$s" + "Reason: %1$s" + "OK" + "File received" + "Open" + "To: \"%1$s\"" + "File type: %1$s (%2$s)" + "Sending file…" + "File sent" + "OK" + "The file wasn\'t sent to \"%1$s\"." + "File: %1$s" + "Close" + "OK" + "Unknown file" + "There\'s no app to handle this type of file. \n" + "No file" + "The file doesn\'t exist. \n" + "Please wait…" + "Turning on Bluetooth…" + "The file will be received. Check progress in the Notifications panel." + "The file can\'t be received." + "Stopped receiving file from \"%1$s\"" + "Sending file to \"%1$s\"" + "Sending %1$s files to \"%2$s\"" + "Stopped sending file to \"%1$s\"" + "There isn\'t enough space in USB storage to save the file." + "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 is ongoing." + "File transfer completed successfully." + "Content isn\'t supported." + "Transfer forbidden by target device." + "Transfer cancelled by user." + "Storage issue" + "No USB storage." + "No SD card. Insert an SD card to save transferred files." + "Connection unsuccessful." + "Request can\'t be handled correctly." + "Unknown error." + "Bluetooth received" + "Bluetooth Share" + "%1$s Received complete." + "%1$s Sent complete." + "Inbound transfers" + "Outbound transfers" + "Transfer history is empty." + "All items will be cleared from the list." + "Bluetooth share: Sent files" + "Bluetooth share: Received files" + + %1$d unsuccessful. + %1$d unsuccessful. + + + %1$d successful, %2$s + %1$d successful, %2$s + + "Clear list" + "Open" + "Clear from list" + "Clear" + "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." + "Slots left:" + "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" + "Connect to Bluetooth" + diff --git a/android/app/res/values-en-rIN/strings_pbap.xml b/android/app/res/values-en-rIN/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd4e5120fbac4962aea689cc7a36b51716a4964c --- /dev/null +++ b/android/app/res/values-en-rIN/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "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" + "Obex authentication request" + "Session key" + "Type session key for %1$s" + "Car Kit" + "Unknown name" + "My name" + "000000" + "Bluetooth Contact share" + diff --git a/android/app/res/values-en-rIN/strings_pbap_client.xml b/android/app/res/values-en-rIN/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-en-rIN/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-en-rIN/strings_sap.xml b/android/app/res/values-en-rIN/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2ebae8c8562b4f0809c2a8e5d43bd13389d5149f --- /dev/null +++ b/android/app/res/values-en-rIN/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM access" + "Bluetooth SIM access" + "Request client to disconnect?" + "Waiting for client to disconnect" + "Disconnect" + "Force disconnect" + diff --git a/android/app/res/values-en-rIN/test_strings.xml b/android/app/res/values-en-rIN/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..63128eee321665468d5ea735ae51e97085db339d --- /dev/null +++ b/android/app/res/values-en-rIN/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insert record" + "Confirm record" + "Ack record" + "Delete all record" + "OK" + "Delete record" + "Start TCP server" + "Notify TCP server" + diff --git a/android/app/res/values-en-rXC/config.xml b/android/app/res/values-en-rXC/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..5653f1dc93aecc088fe887a2e8eb24e70f87fbb8 --- /dev/null +++ b/android/app/res/values-en-rXC/config.xml @@ -0,0 +1,19 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎com.android.settings‎‏‎‎‏‎" + diff --git a/android/app/res/values-en-rXC/strings.xml b/android/app/res/values-en-rXC/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..096e9148efb82a3566c3332480d9090abd9b4b92 --- /dev/null +++ b/android/app/res/values-en-rXC/strings.xml @@ -0,0 +1,137 @@ + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎Access download manager.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎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‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎Unknown‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎Airplane mode‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‏‎‎You can\'t use Bluetooth in Airplane mode.‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‏‎To use Bluetooth services, you must first turn on Bluetooth.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎Turn on Bluetooth now?‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎Cancel‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‎‎‏‎Turn on‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎File transfer‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‎‎Accept incoming file?‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎Decline‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎Accept‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎OK‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎There was a timeout while accepting an incoming file from \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎Incoming file‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ is ready to send ‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎Bluetooth share: Receiving ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎Bluetooth share: Received ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎Bluetooth share: File ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ not received‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎Bluetooth share: Sending ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎Bluetooth share: Sent ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎100% complete‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎Bluetooth share: File ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ not sent‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎File transfer‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‎From: \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‎‎‎File: ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎File size: ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‏‎‏‎‎Receiving file…‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎Stop‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎Hide‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎From‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‎‏‎Filename‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‎Size‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‎File not received‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎File: ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‎Reason: ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎OK‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎File received‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎Open‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎To: \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎File type: ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ (‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎)‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎Sending file…‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎File sent‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎OK‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎The file wasn\'t sent to \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\".‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‎‎File: ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎Close‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎OK‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎Unknown file‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎There\'s no app to handle this type of file. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎No file‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎The file doesn\'t exist. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎Please wait…‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎Turning on Bluetooth…‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎The file will be received. Check progress in the Notifications panel.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎The file can\'t be received.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎Stopped receiving file from \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎Sending file to \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎Sending ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ files to \"‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎Stopped sending file to \"‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎\"‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎There isn\'t enough space in USB storage to save the file.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎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 is ongoing.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎File transfer completed successfully.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎Content isn\'t supported.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‎Transfer forbidden by target device.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎Transfer canceled by user.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎Storage issue.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎No USB storage.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎No SD card. Insert an SD card to save transferred files.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‎Connection unsuccessful.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‏‎Request can\'t be handled correctly.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎Unknown error.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎Bluetooth received‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎Bluetooth Share‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ Received complete.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ Sent complete.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎Inbound transfers‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎Outbound transfers‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎Transfer history is empty.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎All items will be cleared from the list.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎Bluetooth share: Sent files‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎Bluetooth share: Received files‎‏‎‎‏‎" + + ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎%1$d‎‏‎‎‏‏‏‎ unsuccessful.‎‏‎‎‏‎ + ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎%1$d‎‏‎‎‏‏‏‎ unsuccessful.‎‏‎‎‏‎ + + + ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎%1$d‎‏‎‎‏‏‏‎ successful, %2$s‎‏‎‎‏‎ + ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎%1$d‎‏‎‎‏‏‏‎ successful, %2$s‎‏‎‎‏‎ + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎Clear list‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎Open‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎Clear from list‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎Clear‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‏‏‎‎Now Playing‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎Save‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎Cancel‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎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‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎Cannot select account. 0 slots left‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‏‎‎Bluetooth audio connected‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎Bluetooth audio disconnected‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎Bluetooth Audio‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎Files bigger than 4GB cannot be transferred‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎Connect to Bluetooth‎‏‎‎‏‎" + diff --git a/android/app/res/values-en-rXC/strings_pbap.xml b/android/app/res/values-en-rXC/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..e8205139e5b2b8f951937c54749bb7dc13d5917c --- /dev/null +++ b/android/app/res/values-en-rXC/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎Type session key for %1$s‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎Bluetooth session key required‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎There was time out to accept connection with %1$s‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎There was time out to input session key with %1$s‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎Obex authentication request‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‎Session Key‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎Type session key for %1$s‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎Carkit‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎Unknown name‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎My name‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎000000‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎Bluetooth Contact share‎‏‎‎‏‎" + diff --git a/android/app/res/values-en-rXC/strings_pbap_client.xml b/android/app/res/values-en-rXC/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..6a0780a191365b48865e7c9dd58fe0da4fc4a6e4 --- /dev/null +++ b/android/app/res/values-en-rXC/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‎com.android.bluetooth.pbapsink‎‏‎‎‏‎" + diff --git a/android/app/res/values-en-rXC/strings_sap.xml b/android/app/res/values-en-rXC/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..c838e6f361bbb3942f096b7218254d0e1390554a --- /dev/null +++ b/android/app/res/values-en-rXC/strings_sap.xml @@ -0,0 +1,10 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎Bluetooth SIM access‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎Bluetooth SIM Access‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‎Request client to disconnect?‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎Waiting for client to disconnect‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎Disconnect‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎Force disconnect‎‏‎‎‏‎" + diff --git a/android/app/res/values-en-rXC/test_strings.xml b/android/app/res/values-en-rXC/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..d1df1fc6a4b5d36f7aff0b5a5eb5199f6e2854c2 --- /dev/null +++ b/android/app/res/values-en-rXC/test_strings.xml @@ -0,0 +1,13 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎Bluetooth‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‎Insert record‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎Confirm record‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‎Ack record‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎Delete all record‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎OK‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‎‎Delete record‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎Start TCP server‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎Notify TCP server‎‏‎‎‏‎" + diff --git a/android/app/res/values-es-rUS/config.xml b/android/app/res/values-es-rUS/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-es-rUS/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-es-rUS/strings.xml b/android/app/res/values-es-rUS/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..998094237ac37bac69691809369b6990d6f3da55 --- /dev/null +++ b/android/app/res/values-es-rUS/strings.xml @@ -0,0 +1,137 @@ + + + + + "Accede al administrador de descarga." + "Permite a la aplicación acceder al administrador de BluetoothShare y usarlo para transferir archivos." + "Acceso del dispositivo Bluetooth a la lista blanca." + "Permite que la app incluya temporalmente un dispositivo Bluetooth en la lista blanca, lo que permite que ese dispositivo envíe archivos sin la confirmación del usuario." + "Bluetooth" + "Dispositivo desconocido" + "Desconocido" + "Modo de avión" + "No puedes usar Bluetooth en Modo avión." + + "Para acceder a nuestros servicios de Bluetooth, primero debes activar el Bluetooth." + "¿Deseas activar el Bluetooth ahora?" + "Cancelar" + "Activar" + "Transferencia de archivo" + "¿Aceptar archivo entrante?" + "Rechazar" + "Aceptar" + "Aceptar" + "Tiempo de espera agotado al aceptar un archivo entrante de \"%1$s\"" + "Archivo entrante" + "%1$s está listo para enviar %2$s" + "Bluetooth: recibiendo %1$s" + "Bluetooth: %1$s recibido" + "Bluetooth: no se recibió el archivo %1$s" + "Bluetooth: enviando %1$s" + "Bluetooth: %1$s enviado" + "Completado el 100%" + "Bluetooth: no se envió el archivo %1$s" + "Transferencia de archivo" + "De: \"%1$s\"" + "Archivo: %1$s" + "Tamaño de archivo:%1$s" + + "Recibiendo archivo..." + "Detener" + "Ocultar" + "De" + "Nombre del archivo" + "Tamaño" + "Archivo no recibido" + "Archivo: %1$s" + "Motivo: %1$s" + "Aceptar" + "Archivo recibido" + "Abrir" + "Para: \"%1$s\"" + "Tipo de archivo: %1$s (%2$s)" + "Enviando archivo..." + "Archivo enviado" + "Aceptar" + "El archivo no se envió a \"%1$s\"." + "Archivo: %1$s" + "Cerrar" + "Aceptar" + "Archivo desconocido" + "No hay ninguna aplicación que pueda procesar este tipo de archivo. \n" + "No hay archivos" + "El archivo no existe. \n" + "Por favor, espera..." + "Activando Bluetooth..." + "El archivo será recibido. Verifica el progreso en el panel de notificaciones." + "No se puede recibir el archivo." + "Se detuvo la recepción del archivo de \"%1$s\"" + "Enviando archivo a \"%1$s\"" + "Enviando %1$s archivos a \"%2$s\"" + "Se detuvo el envío del archivo a \"%1$s\"" + "No hay espacio suficiente en el almacenamiento USB para guardar el archivo." + "No hay espacio suficiente en la tarjeta SD para guardar el archivo." + "Espacio necesario: %1$s" + "Se están procesando demasiadas solicitudes. Vuelve a intentarlo más tarde." + "Aún no comenzó la transferencia de archivos." + "Transferencia de archivo en curso" + "La transferencia de archivos se completó correctamente." + "El contenido no es compatible." + "El dispositivo de destino prohíbe la transferencia." + "Transferencia cancelada por el usuario" + "Problema de almacenamiento" + "No se encuentra ningún almacenamiento USB." + "No se ha encontrado una tarjeta SD. Inserta una para guardar los archivos transferidos." + "Conexión incorrecta" + "No se puede procesar la solicitud correctamente." + "Error desconocido" + "Recibido por Bluetooth" + "Compartir por Bluetooth" + "Se recibieron %1$s completos." + "%1$s enviados por completo." + "Transferencias de entrada" + "Transferencias de salida" + "El historial de transferencias está vacío." + "Se borrarán todos los elementos de la lista." + "Bluetooth: archivos enviados" + "Bluetooth: archivos recibidos" + + %1$d incompletos + %1$d incompleto + + + %1$d completos, %2$s + %1$d completo, %2$s + + "Eliminar lista" + "Abrir" + "Eliminar de la lista" + "Eliminar" + "Está sonando" + "Guardar" + "Cancelar" + "Selecciona las cuentas que deseas compartir mediante Bluetooth. Al conectarte, tendrás que aceptar cualquier acceso a las cuentas." + "Espacios restantes:" + "Ícono de la aplicación" + "Configuración de mensajes compartidos por Bluetooth" + "No puedes seleccionar la cuenta. No quedan espacios." + "Audio Bluetooth conectado" + "Audio Bluetooth desconectado" + "Audio Bluetooth" + "No se pueden transferir los archivos de más de 4 GB" + "Conectarse a Bluetooth" + diff --git a/android/app/res/values-es-rUS/strings_pbap.xml b/android/app/res/values-es-rUS/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..7bca930a0e8e51d7936b7de8d4a5da453caf5207 --- /dev/null +++ b/android/app/res/values-es-rUS/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Escribe la clave de la sesión para %1$s." + "Se requiere la clave de la sesión de Bluetooth." + "Hubo tiempo muerto al aceptar la conexión con %1$s." + "Hubo tiempo muerto al ingresar la clave de la sesión con %1$s" + "Solicitud de autenticación de Obex" + "Clave de la sesión" + "Escribe la clave de la sesión para %1$s." + "Kit de automóvil" + "Nombre desconocido" + "Mi nombre" + "000000" + "Compartir contactos por Bluetooth" + diff --git a/android/app/res/values-es-rUS/strings_pbap_client.xml b/android/app/res/values-es-rUS/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-es-rUS/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-es-rUS/strings_sap.xml b/android/app/res/values-es-rUS/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..32efc2f87e80fc485a8b3a0b796e82d9d862f073 --- /dev/null +++ b/android/app/res/values-es-rUS/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Acceso a SIM mediante Bluetooth" + "Acceso a SIM mediante Bluetooth" + "¿Quieres solicitar al cliente que se desconecte?" + "Esperando a que el cliente se desconecte" + "Desconectar" + "Forzar desconexión" + diff --git a/android/app/res/values-es-rUS/test_strings.xml b/android/app/res/values-es-rUS/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3417422fe05f80e797399ef95544bb2870132b45 --- /dev/null +++ b/android/app/res/values-es-rUS/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insertar grabación" + "Confirmar grabación" + "Confirmación de la grabación" + "Eliminar todas las grabaciones" + "Aceptar" + "Eliminar grabación" + "Iniciar el servidor TCP" + "Notificar al servidor TCP" + diff --git a/android/app/res/values-es/config.xml b/android/app/res/values-es/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-es/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-es/strings.xml b/android/app/res/values-es/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..9298ef853f03ceead647f2858835e735b0676caa --- /dev/null +++ b/android/app/res/values-es/strings.xml @@ -0,0 +1,137 @@ + + + + + "Acceso al administrador de descargas" + "Permite que la aplicación acceda al administrador BluetoothShare y lo use para transferir archivos." + "Permitir acceso de dispositivo Bluetooth." + "Permite que la aplicación autorice temporalmente un dispositivo Bluetooth a enviar archivos a este dispositivo sin la confirmación del usuario." + "Bluetooth" + "Dispositivo desconocido" + "Desconocido" + "Modo avión" + "No puedes utilizar el Bluetooth en el modo avión." + + "Para utilizar los servicios de Bluetooth, primero debes activar la función Bluetooth." + "¿Quieres activar la función Bluetooth ahora?" + "Cancelar" + "Activar" + "Transferencia de archivos" + "¿Aceptar archivo entrante?" + "Rechazar" + "Aceptar" + "Aceptar" + "Se ha agotado el tiempo para aceptar el archivo entrante de \"%1$s\"." + "Archivo entrante" + "%1$s ya puede enviar %2$s" + "Bluetooth: recibiendo %1$s" + "Compartir con Bluetooth: %1$s recibido" + "Bluetooth: %1$s no recibido" + "Bluetooth: enviando %1$s" + "Bluetooth: %1$s enviado" + "100% completado" + "Bluetooth: archivo %1$s no enviado" + "Transferencia de archivos" + "De: \"%1$s\"" + "Archivo: %1$s" + "Tamaño del archivo: %1$s" + + "Recibiendo archivo…" + "Detener" + "Ocultar" + "De" + "Nombre del archivo" + "Tamaño" + "Archivo no recibido" + "Archivo: %1$s" + "Motivo: %1$s" + "Aceptar" + "Archivo recibido" + "Abrir" + "Para: \"%1$s\"" + "Tipo de archivo: %1$s (%2$s)" + "Enviando archivo…" + "Archivo enviado" + "Aceptar" + "No se ha enviado el archivo a \"%1$s\"." + "Archivo: %1$s" + "Cerrar" + "Aceptar" + "Archivo desconocido" + "No hay ninguna aplicación que pueda procesar este tipo de archivo. \n" + "No hay archivos." + "El archivo no existe. \n" + "Por favor, espera..." + "Activando Bluetooth..." + "Se recibirá el archivo. Comprueba el progreso en la barra de notificaciones." + "No se puede recibir el archivo." + "Se ha detenido la recepción del archivo de \"%1$s\"" + "Enviando archivo a \"%1$s\"" + "Enviando %1$s archivos a \"%2$s\"" + "Se ha detenido el envío del archivo a \"%1$s\"" + "No hay suficiente espacio en el almacenamiento USB para guardar el archivo." + "No hay suficiente espacio en la tarjeta SD para guardar el archivo." + "Espacio necesario: %1$s" + "Se están procesando demasiadas solicitudes. Vuelve a intentarlo más tarde." + "Aún no se ha iniciado la transferencia de archivos." + "Transferencia de archivos en curso" + "La transferencia de archivos se ha completado correctamente." + "Contenido no admitido" + "El dispositivo de destino no permite la transferencia." + "Transferencia cancelada por el usuario" + "Error relacionada con el almacenamiento" + "Sin almacenamiento USB" + "No se detecta ninguna tarjeta SD. Inserta una para guardar los archivos transferidos." + "Conexión incorrecta" + "No se puede procesar la solicitud correctamente." + "Error desconocido" + "Recibido por Bluetooth" + "Bluetooth Share" + "Recepción de %1$s completada" + "Envío de %1$s completado" + "Transferencias entrantes" + "Transferencias salientes" + "El historial de transferencias está vacío." + "Se borrarán todos los elementos de la lista." + "Bluetooth: archivos enviados" + "Bluetooth: archivos recibidos" + + %1$d incorrectamente. + %1$d incorrectamente. + + + %1$d correctamente, %2$s + %1$d correctamente, %2$s + + "Borrar lista" + "Abrir" + "Borrar de la lista" + "Borrar" + "Reproduciendo" + "Guardar" + "Cancelar" + "Selecciona las cuentas que quieras compartir por Bluetooth. Tendrás que aceptar cualquier acceso a las cuentas al establecer conexión." + "Ranuras libres:" + "Icono de aplicación" + "Ajustes de mensajes compartidos por Bluetooth" + "No se puede seleccionar la cuenta. No quedan ranuras" + "Audio por Bluetooth conectado" + "Audio por Bluetooth desconectado" + "Audio por Bluetooth" + "No se pueden transferir archivos de más de 4 GB" + "Conectarse a un dispositivo Bluetooth" + diff --git a/android/app/res/values-es/strings_pbap.xml b/android/app/res/values-es/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..db1abf5b9c35b753b01e23e8377e7b6da8c77bb5 --- /dev/null +++ b/android/app/res/values-es/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Introducir clave de sesión para %1$s" + "Se necesita la clave de sesión de Bluetooth." + "Se ha agotado el tiempo para aceptar la conexión con %1$s." + "Se ha agotado el tiempo para introducir la clave de sesión con %1$s." + "Solicitud de autenticación de Obex" + "Clave de sesión" + "Introducir clave de sesión para %1$s" + "Carkit" + "Nombre desconocido" + "Mi nombre" + "000000" + "Compartir contactos por Bluetooth" + diff --git a/android/app/res/values-es/strings_pbap_client.xml b/android/app/res/values-es/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-es/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-es/strings_sap.xml b/android/app/res/values-es/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..567eaf8ae1da8fef363fc088b3a5feb866b42ede --- /dev/null +++ b/android/app/res/values-es/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Perfil Bluetooth de acceso a la tarjeta SIM" + "Perfil Bluetooth de acceso a la tarjeta SIM" + "¿Solicitar al cliente que se desconecte?" + "Esperando al cliente para desconectar" + "Desconectar" + "Forzar desconexión" + diff --git a/android/app/res/values-es/test_strings.xml b/android/app/res/values-es/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..a805fcf9fbc0417c7ead03bd3ee20e34182f5af5 --- /dev/null +++ b/android/app/res/values-es/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insertar grabación" + "Confirmar grabación" + "Confirmar grabación" + "Eliminar todas las grabaciones" + "Aceptar" + "Eliminar grabación" + "Iniciar servidor TCP" + "Informar al servidor TCP" + diff --git a/android/app/res/values-et/config.xml b/android/app/res/values-et/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-et/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-et/strings.xml b/android/app/res/values-et/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..2efc14b6c9825450bafb6efec7623e08239f99a5 --- /dev/null +++ b/android/app/res/values-et/strings.xml @@ -0,0 +1,137 @@ + + + + + "Pääs allalaadimishalduri juurde." + "Võimaldab rakendusel pääseda BluetoothShare\'i haldurisse ja kasutada seda failide edastamiseks." + "Bluetooth-seadmele juurdepääsu andmine." + "Lubab rakendusel lisada Bluetooth-seadme ajutiselt lubatud seadmete loendisse, mis võimaldab seadmel saata faile sellesse seadmesse ilma kasutaja kinnituseta." + "Bluetooth" + "Tundmatu seade" + "Tundmatu" + "Lennukirežiim" + "Te ei saa Bluetoothi lennurežiimis kasutada." + + "Bluetoothi teenuste kasutamiseks peate esmalt Bluetoothi sisse lülitama." + "Lülitan Bluetoothi kohe sisse?" + "Tühista" + "Lülita sisse" + "Failiedastus" + "Kas aktsepteerida sissetulev fail?" + "Keeldu" + "Nõustun" + "OK" + "Esines ajalõpp sissetuleva faili aktsepteerimisel saatjalt „%1$s”" + "Sissetulev fail" + "Saatja %1$s on faili %2$s saatmiseks valmis" + "Bluetoothi jagamine: faili %1$s vastuvõtmine" + "Bluetoothi jagamine: %1$s vastu võetud" + "Bluetoothi jagamine: faili %1$s pole saadud" + "Bluetoothi jagamine: faili %1$s saatmine" + "Bluetoothi jagamine: fail %1$s saadetud" + "100% lõpetatud" + "Bluetoothi jagamine: faili %1$s ei saadetud" + "Failiedastus" + "Saatja: „%1$s”" + "Fail: %1$s" + "Faili suurus: %1$s" + + "Faili vastuvõtmine ..." + "Peata" + "Peida" + "Saatja" + "Faili nimi" + "Suurus" + "Faili ei saadud" + "Fail: %1$s" + "Põhjus: %1$s" + "OK" + "Fail vastu võetud" + "Ava" + "Saaja: „%1$s”" + "Faili tüüp: %1$s (%2$s)" + "Faili saatmine ..." + "Fail saadetud" + "OK" + "Faili ei saadetud kasutajale „%1$s”." + "Fail: %1$s" + "Sulge" + "OK" + "Tundmatu fail" + "Seda tüüpi faili käsitlemiseks pole sobivat rakendust. \n" + "Fail puudub" + "Faili ei ole olemas. \n" + "Palun oodake ..." + "Bluetoothi sisselülitamine ..." + "Fail võetakse vastu. Vaadake edenemist teatiste paneelil." + "Faili ei saa vastu võtta." + "Faili vastuvõtmine saatjalt „%1$s” peatatud" + "Faili saatmine saajale „%1$s”" + "%1$s faili saatmine saajale „%2$s”" + "Faili saatmine saajale „%1$s” peatatud" + "USB-salvestusseadmes pole faili salvestamiseks piisavalt ruumi." + "SD-kaardil pole faili salvestamiseks piisavalt ruumi." + "Vajalik ruum: %1$s" + "Liiga palju taotlusi on töötlemisel. Proovige hiljem uuesti." + "Failiedastust pole veel käivitatud." + "Failiedastus on pooleli." + "Failiedastuse lõpuleviimine õnnestus." + "Sisu ei toetata." + "Sihtseade on edastuse keelanud." + "Kasutaja tühistas edastuse." + "Probleem talletamisega." + "USB-salvestusruum puudub." + "SD-kaart puudub. Edastatud failide salvestamiseks sisestage SD-kaart." + "Ühendus ebaõnnestus." + "Taotlust ei saa õigesti käsitleda." + "Tundmatu viga." + "Bluetoothiga vastu võetud" + "Jagamine Bluetoothiga" + "%1$s vastuvõtmine lõpetatud." + "%1$s saatmine lõpetatud." + "Sissetulevad edastused" + "Väljuvad edastused" + "Edastusajalugu on tühi." + "Kõik üksused eemaldatakse loendist." + "Bluetoothi jagamine: saadetud failid" + "Bluetoothi jagamine: vastuvõetud failid" + + %1$d ebaõnnestus. + %1$d ebaõnnestus. + + + %1$d õnnestus, %2$s + %1$d õnnestus, %2$s + + "Tühjendage loend" + "Ava" + "Eemaldage loendist" + "Kustuta" + "Hetkel mängimas" + "Salvesta" + "Tühista" + "Valige kontod, mida soovite Bluetoothi kaudu jagada. Ühendamisel peate ikka lubama mis tahes juurdepääsu kontodele." + "Järelejäänud vabu kohti:" + "Rakenduse ikoon" + "Bluetoothi sõnumi jagamise seaded" + "Kontot ei saa valida. Pole ühtegi vaba kohta" + "Bluetoothi heli on ühendatud" + "Bluetoothi heli ühendus on katkestatud" + "Bluetoothi heli" + "Faile, mis on üle 4 GB, ei saa üle kanda" + "Ühenda Bluetoothiga" + diff --git a/android/app/res/values-et/strings_pbap.xml b/android/app/res/values-et/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..22117b8ceeef9782ca87f60735e4f787b4f4a932 --- /dev/null +++ b/android/app/res/values-et/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Sisestage seansivõti failile %1$s" + "Vajalik Bluetoothi seansivõti" + "Esines ajalõpp ühenduse aktsepteerimiseks seadmega %1$s" + "Esines ajalõpp sisendseansivõtmele failiga %1$s" + "Obexi autentimise taotlus" + "Seansivõti" + "Sisestage seansivõti failile %1$s" + "Autokomplekt" + "Tundmatu nimi" + "Minu nimi" + "000000" + "Bluetoothi kontakti jagamine" + diff --git a/android/app/res/values-et/strings_pbap_client.xml b/android/app/res/values-et/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-et/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-et/strings_sap.xml b/android/app/res/values-et/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c2a71522d4e4999aa01f831069edeefaecf6e7f --- /dev/null +++ b/android/app/res/values-et/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetoothi SIM-kaardi juurdepääs" + "Bluetoothi SIM-kaardi juurdepääs" + "Kas paluda kliendil ühendus katkestada?" + "Kliendi ühenduse katkestamise ootel" + "Katkesta ühendus" + "Sundkatkesta ühendus" + diff --git a/android/app/res/values-et/test_strings.xml b/android/app/res/values-et/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b365c164ba16f9b1eb2819c931819b71102a6732 --- /dev/null +++ b/android/app/res/values-et/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Sisestage kirje" + "Kinnita kirje" + "Acki kirje" + "Kustuta kõik kirjed" + "OK" + "Kustuta kirje" + "Käivita TCP-server" + "Teavita TCP-serverit" + diff --git a/android/app/res/values-eu/config.xml b/android/app/res/values-eu/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-eu/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-eu/strings.xml b/android/app/res/values-eu/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..d878463a15741ee90dd005f998b735c36b16d2a0 --- /dev/null +++ b/android/app/res/values-eu/strings.xml @@ -0,0 +1,137 @@ + + + + + "Atzitu deskargen kudeatzailea." + "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" + "Identifikatu ezin den gailua" + "Ezezaguna" + "Hegaldi modua" + "Ezin duzu erabili Bluetooth-a Hegaldi moduan." + + "Bluetooth-zerbitzuak erabiltzeko, Bluetooth-a aktibatu behar duzu." + "Bluetooth-a aktibatu nahi duzu?" + "Utzi" + "Aktibatu" + "Fitxategi-transferentzia" + "Sarrerako fitxategia onartu nahi duzu?" + "Baztertu" + "Onartu" + "Ados" + "\"%1$s\" igorlearen sarrerako fitxategia onartzeko denbora-muga gainditu da" + "Sarrerako fitxategia" + "%1$s prest dago %2$s bidaltzeko" + "Bluetooth bidez partekatzea: %1$s fitxategia jasotzen" + "Bluetooth bidez partekatzea: %1$s fitxategia jaso da" + "Bluetooth bidez partekatzea: ez da jaso %1$s fitxategia" + "Bluetooth bidez partekatzea: %1$s fitxategia bidaltzen" + "Bluetooth bidez partekatzea: %1$s fitxategia bidali da" + "% 100ean osatuta" + "Bluetooth bidez partekatzea: ez da bidali %1$s fitxategia" + "Fitxategi-transferentzia" + "Igorlea: \"%1$s\"" + "Fitxategia: %1$s" + "Fitxategiaren tamaina: %1$s" + + "Fitxategia jasotzen…" + "Gelditu" + "Ezkutatu" + "Igorlea" + "Fitxategi-izena" + "Tamaina" + "Ez da fitxategia jaso" + "Fitxategia: %1$s" + "Arrazoia: %1$s" + "Ados" + "Fitxategia jaso da" + "Ireki" + "Hartzaileak: \"%1$s\"" + "Fitxategi mota: %1$s (%2$s)" + "Fitxategia bidaltzen…" + "Fitxategia bidali da" + "Ados" + "Fitxategia ez zaio \"%1$s\" hartzaileari bidali." + "Fitxategia: %1$s" + "Itxi" + "Ados" + "Fitxategi ezezaguna" + "Ez dago fitxategi mota hau kudeatzeko aplikaziorik. \n" + "Ez dago fitxategirik" + "Ez dago horrelako fitxategirik. \n" + "Itxaron…" + "Bluetooth-a aktibatzen…" + "Fitxategia jasoko da. Egoera kontrolatzeko, joan Jakinarazpenen panelera." + "Ezin da fitxategia jaso." + "\"%1$s\" igorlearen fitxategia jasotzeari utzi zaio" + "\"%1$s\" hartzaileari fitxategia bidaltzen" + "\"%2$s\" hartzaileari %1$s fitxategi bidaltzen" + "\"%1$s\" hartzaileari fitxategia bidaltzeari utzi zaio." + "Ez dago fitxategia gordetzeko behar adina toki USB bidezko memorian." + "Ez dago fitxategia gordetzeko behar adina toki SD txartelean." + "Beharrezko memoria: %1$s" + "Eskaera gehiegi prozesatzen ari dira. Saiatu berriro geroago." + "Ez da fitxategi-transferentzia oraindik hasi." + "Fitxategi-transferentzia abian da." + "Fitxategi-transferentzia behar bezala osatu da." + "Ez da edukia onartzen." + "Xede-gailuak transferentzia debekatu du." + "Erabiltzaileak bertan behera utzi du transferentzia." + "Memoria-arazoa." + "Ez dago USB bidezko memoriarik." + "Ez dago SD txartelik. Transferitutako fitxategiak gordetzeko, sartu SD txartel bat." + "Ezin izan da konektatu." + "Ezin da eskaera behar bezala kudeatu." + "Errore ezezaguna." + "Bluetooth bidez jasotakoak" + "Bluetooth bidez partekatzea" + "%1$s jaso dira osorik." + "%1$s osorik bidali da." + "Sarrerako transferentziak" + "Irteerako transferentziak" + "Transferentzia-historia hutsik dago." + "Elementu guztiak zerrendatik garbituko dira." + "Bluetooth bidez partekatzea: fitxategiak bidali dira" + "Bluetooth bidez partekatzea: fitxategiak jaso dira" + + %1$d hutsegite. + %1$d hutsegite. + + + %1$d ongi, %2$s + %1$d ongi, %2$s + + "Garbitu zerrenda" + "Ireki" + "Garbitu zerrendatik" + "Garbitu" + "Orain erreproduzitzen" + "Gorde" + "Utzi" + "Hautatu Bluetooth bidez partekatu nahi dituzun kontuak. Konektatzean, berariaz eman beharko duzu kontuetarako sarbidea." + "Geratzen diren zirrikituak:" + "Aplikazioaren ikonoa" + "Bluetooth bidez mezuak partekatzeko ezarpenak" + "Ezin da hautatu kontua. Ez da geratzen zirrikiturik." + "Konektatu da Bluetooth bidezko audioa" + "Deskonektatu da Bluetooth bidezko audioa" + "Bluetooth bidezko audioa" + "Ezin dira transferitu 4 GB baino gehiagoko fitxategiak" + "Konektatu Bluetooth-era" + diff --git a/android/app/res/values-eu/strings_pbap.xml b/android/app/res/values-eu/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..1dfe623768c5b66c9ac7c9cb88f4ef4a2df61708 --- /dev/null +++ b/android/app/res/values-eu/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Idatzi %1$s gailuaren saio-gakoa" + "Bluetooth saio-gakoa idatzi behar da" + "%1$s gailuarekin konektatzea onartzeko denbora-muga gainditu da" + "%1$s gailuarekin saio-gakoa idazteko denbora-muga gainditu da" + "Obex bidezko autentifikazio-eskaera" + "Saio-gakoa" + "Idatzi %1$s gailuaren saio-gakoa" + "Autorako telefono-kita" + "Izen ezezaguna" + "Nire izena" + "000000" + "Kontaktuak Bluetooth bidez partekatzeko aukera" + diff --git a/android/app/res/values-eu/strings_pbap_client.xml b/android/app/res/values-eu/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-eu/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-eu/strings_sap.xml b/android/app/res/values-eu/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c1205f122691a8e73db14c5659d15b536af9d49 --- /dev/null +++ b/android/app/res/values-eu/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM sarbidea" + "Bluetooth SIM sarbidea" + "Deskonektatzeko eskatu nahi diozu bezeroari?" + "Bezeroa noiz deskonektatuko zain" + "Deskonektatu" + "Behartu deskonektatzera" + diff --git a/android/app/res/values-eu/test_strings.xml b/android/app/res/values-eu/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e7236e799d51c34cdd36ef24e8923dcc44fbf83e --- /dev/null +++ b/android/app/res/values-eu/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth-a" + "Sartu erregistroa" + "Berretsi erregistroa" + "ACK erregistroa" + "Ezabatu erregistro guztiak" + "Ados" + "Ezabatu erregistroa" + "Hasi TCP zerbitzaria" + "Jakinarazi TCP zerbitzariari" + diff --git a/android/app/res/values-fa/config.xml b/android/app/res/values-fa/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-fa/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-fa/strings.xml b/android/app/res/values-fa/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..de44926eb81e375220b90bab999a4ffe7343b93f --- /dev/null +++ b/android/app/res/values-fa/strings.xml @@ -0,0 +1,137 @@ + + + + + "‏دسترسی به Download Manager." + "‏به برنامه برای دسترسی به مدیر BluetoothShare و استفاده از آن برای انتقال فایل‌ها اجازه می‌دهد." + "قرار دادن دسترسی دستگاه بلوتوث در فهرست مجاز." + "به برنامه اجازه می‌دهد موقتاً یک دستگاه بلوتوث را در فهرست مجاز قرار دهد؛ با این کار دستگاه موردنظر مجاز خواهد بود بدون تأیید کاربر برای این دستگاه فایل ارسال کند." + "بلوتوث" + "دستگاه ناشناس" + "ناشناس" + "حالت هواپیما" + "در حالت هواپیما نمی‌توانید از بلوتوث استفاده کنید." + + "برای استفاده از خدمات بلوتوث، باید ابتدا بلوتوث را روشن کنید." + "اکنون بلوتوث روشن شود؟" + "لغو" + "روشن کردن" + "انتقال فایل" + "فایل ورودی پذیرفته شود؟" + "نپذیرفتن" + "پذیرفتن" + "تأیید" + "هنگام پذیرش یک فایل ورودی از \"%1$s\" درنگ پیش آمد" + "فایل ورودی" + "%1$s آماده ارسال %2$s است" + "اشتراک بلوتوث: در حال دریافت %1$s" + "اشتراک بلوتوث: %1$s دریافت شد" + "اشتراک بلوتوث: فایل %1$sدریافت نشد" + "اشتراک بلوتوث: در حال ارسال %1$s" + "اشتراک بلوتوث:%1$s ارسال شد" + "100% کامل شد" + "اشتراک بلوتوث: فایل %1$s ارسال نشد" + "انتقال فایل" + "از: \"%1$s\"" + "فایل: %1$s" + "اندازه فایل: %1$s" + + "درحال دریافت فایل…" + "توقف" + "پنهان کردن" + "از" + "نام فایل" + "اندازه" + "فایل دریافت نشد" + "فایل: %1$s" + "دلیل: %1$s" + "تأیید" + "فایل دریافت شد" + "باز کردن" + "به: \"%1$s\"" + "نوع فایل: %1$s (%2$s)" + "درحال ارسال فایل…" + "فایل ارسال شد" + "تأیید" + "فایل به \"%1$s\" ارسال نشد." + "فایل: %1$s" + "بستن" + "تأیید" + "فایل ناشناس" + "هیچ برنامه‌ای برای کار با این نوع فایل وجود ندارد.\n" + "فایلی وجود ندارد" + "فایل موجود نیست. \n" + "لطفاً منتظر بمانید…" + "روشن کردن بلوتوث…" + "فایل دریافت می‌شود: پیشرفت دریافت را در پانل اعلان‌ها بررسی کنید." + "فایل را نمی‌توان دریافت کرد." + "دریافت فایل از \"%1$s\" متوقف شد" + "ارسال فایل به \"%1$s\"" + "ارسال %1$s فایل به \"%2$s\"" + "ارسال فایل به \"%1$s\" متوقف شد" + "‏فضای کافی در حافظه USB برای ذخیره کردن فایل موجود نیست." + "‏فضای کافی در کارت SD برای ذخیره کردن فایل موجود نیست." + "فضای مورد نیاز: %1$s" + "درخواست‌های بسیاری در حال انجام هستند. بعداً دوباره امتحان کنید." + "انتقال فایل هنوز شروع نشده است." + "انتقال فایل در حال انجام است." + "انتقال فایل با موفقیت انجام شد." + "محتوا پشتیبانی نمی‌شود." + "انتقال توسط دستگاه مقصد ممنوع شده است." + "انتقال توسط کاربر لغو شد." + "مشکل فضای ذخیره‌سازی." + "‏حافظهٔ USB وجود ندارد." + "‏هیچ کارت SD موجود نیست. برای ذخیره فایل‌های منتقل‌شده، کارت SD در گوشی قرار دهید." + "اتصال ناموفق بود." + "درخواست به درستی انجام نمی‌شود." + "خطای ناشناس." + "بلوتوث دریافت شد" + "اشتراکگذاری بلوتوث" + "%1$s دریافت کامل شد." + "%1$s ارسال کامل شد." + "انتقال های ورودی" + "انتقال های خروجی" + "سابقه انتقال خالی است." + "همهٔ موارد از فهرست پاک می‌شوند." + "اشتراک بلوتوث: فایل‌های ارسال شده" + "اشتراک بلوتوث: فایل‌های دریافت شده" + + %1$d مورد ناموفق. + %1$d مورد ناموفق. + + + %1$d مورد موفق، %2$s + %1$d مورد موفق، %2$s + + "پاک کردن فهرست" + "باز کردن" + "پاک کردن از فهرست" + "پاک کردن" + "درحال پخش" + "ذخیره‌" + "لغو" + "حساب‌هایی را انتخاب کنید که می‌خواهید از طریق بلوتوث به اشتراک بگذارید. هنگام اتصال، همچنان باید با هر گونه دسترسی به حساب‌ها موافقت کنید." + "شیارهای باقی‌مانده:" + "نماد برنامه" + "تنظیمات اشتراک‌گذاری پیام بلوتوث" + "انتخاب حساب امکان‌پذیر نیست. ۰ شیار باقی مانده است" + "بلوتوث صوتی متصل شد" + "ارتباط بلوتوث صوتی قطع شد" + "بلوتوث‌ صوتی" + "فایل‌های بزرگ‌تر از ۴ گیگابایت نمی‌توانند منتقل شوند" + "اتصال به بلوتوث" + diff --git a/android/app/res/values-fa/strings_pbap.xml b/android/app/res/values-fa/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..b73b52d50384cc4dab9a74d008cb590e50fb85a8 --- /dev/null +++ b/android/app/res/values-fa/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "‏تایپ کلید جلسه برای %1$s" + "به کلید جلسه بلوتوث نیاز است" + "‏پذیرفتن اتصال با %1$s خیلی زمان برد" + "‏وارد کردن کلید جلسه با %1$s خیلی زمان برد" + "‏درخواست اصالت‌سنجی Obex" + "کلید جلسه" + "‏تایپ کلید جلسه برای %1$s" + "کیت خودرو" + "نام ناشناس" + "نام من" + "000000" + "اشتراک‌گذاری مخاطبین ازطریق بلوتوث" + diff --git a/android/app/res/values-fa/strings_pbap_client.xml b/android/app/res/values-fa/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-fa/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-fa/strings_sap.xml b/android/app/res/values-fa/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec7e49d4171230dcefd0f8d799f252efb84f99ea --- /dev/null +++ b/android/app/res/values-fa/strings_sap.xml @@ -0,0 +1,10 @@ + + + "دسترسی به سیم‌کارت بلوتوث" + "دسترسی به سیم‌کارت بلوتوث" + "قطع ارتباط از کلاینت درخواست شود؟" + "در انتظار کلاینت برای قطع ارتباط" + "قطع ارتباط" + "قطع ارتباط اجباری" + diff --git a/android/app/res/values-fa/test_strings.xml b/android/app/res/values-fa/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..97617751226f9857ed0bebcba676b69e9724e98a --- /dev/null +++ b/android/app/res/values-fa/test_strings.xml @@ -0,0 +1,13 @@ + + + "بلوتوث" + "درج سابقه" + "تأیید سابقه" + "‏سابقه Ack" + "حذف همه سابقه" + "تأیید" + "حذف سابقه" + "‏راه‌اندازی سرور TCP" + "‏اعلان سرور TCP" + diff --git a/android/app/res/values-fi/config.xml b/android/app/res/values-fi/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-fi/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-fi/strings.xml b/android/app/res/values-fi/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..8151d60ffd4ed5d46cc656047a32d9ebee2caa9b --- /dev/null +++ b/android/app/res/values-fi/strings.xml @@ -0,0 +1,137 @@ + + + + + "Lataustenhallinnan käyttö." + "Antaa sovelluksen käyttää BluetoothShare-hallintaa tiedostojen siirtoon." + "Käyttö Bluetooth-laitteella, jolla on tilapäinen lupa." + "Mahdollistaa tilapäisen luvan antamisen Bluetooth-laitteelle, jolloin tiedostojen lähettäminen laitteesta tähän laitteeseen ei vaadi käyttäjän hyväksyntää." + "Bluetooth" + "Tuntematon laite" + "Tuntematon" + "Lentokonetila" + "Et voi käyttää Bluetoothia lentokonetilassa." + + "Jos haluat käyttää Bluetooth-palveluita, ota Bluetooth käyttöön." + "Otetaanko Bluetooth käyttöön nyt?" + "Peru" + "Ota käyttöön" + "Tiedostonsiirto" + "Hyväksytäänkö saapuva tiedosto?" + "Hylkää" + "Hyväksy" + "OK" + "Yhteys aikakatkaistiin vastaanotettaessa tiedostoa lähettäjältä %1$s" + "Saapuva tiedosto" + "%1$s on valmis lähettämään kohteen %2$s." + "Bluetooth-jako: vastaanotetaan tiedostoa %1$s" + "Bluetooth-jako: %1$s vastaanotettu" + "Bluetooth-jako: tiedostoa %1$s ei vastaanotettu" + "Bluetooth-jako: lähetetään tiedostoa %1$s" + "Bluetooth-jako: lähetä %1$s" + "100 % valmis" + "Bluetooth-jako: tiedostoa %1$s ei lähetetty" + "Tiedostonsiirto" + "Lähettäjä: %1$s" + "Tiedosto: %1$s" + "Tiedostokoko: %1$s" + + "Vastaanotetaan tiedostoa…" + "Lopeta" + "Piilota" + "Lähettäjä" + "Tiedostonimi" + "Koko" + "Tiedostoa ei vastaanotettu" + "Tiedosto: %1$s" + "Syy: %1$s" + "OK" + "Tiedosto vastaanotettu" + "Avaa" + "Vastaanottaja: %1$s" + "Tiedostotyyppi: %1$s (%2$s)" + "Lähetetään tiedostoa…" + "Tiedosto lähetetty" + "OK" + "Tiedostoa ei lähetetty vastaanottajalle %1$s." + "Tiedosto: %1$s" + "Sulje" + "OK" + "Tuntematon tiedosto" + "Tämän tyyppisten tiedostojen käsittelyyn sopivaa sovellusta ei ole asennettu. \n" + "Ei tiedostoa" + "Tiedostoa ei ole olemassa. \n" + "Odota…" + "Otetaan Bluetooth käyttöön..." + "Tiedosto otetaan vastaan. Näet siirron tilan Ilmoitukset-paneelissa." + "Tiedostoa ei voi vastaanottaa." + "Pysäytettiin tiedoston vastaanotto lähettäjältä %1$s" + "Lähetetään tiedosto vastaanottajalle %1$s" + "Lähetetään %1$s tiedostoa vastaanottajalle %2$s" + "Tiedoston lähettäminen vastaanottajalle %1$s pysäytetty" + "USB-tallennustila ei riitä tiedoston tallentamiseen." + "SD-kortin tila ei riitä tiedoston tallentamiseen." + "Tilaa tarvitaan: %1$s" + "Liian monta käsiteltävää pyyntöä. Yritä myöhemmin uudelleen." + "Tiedostonsiirto ei ole vielä alkanut." + "Tiedostonsiirto on käynnissä." + "Tiedostonsiirto onnistui." + "Sisältöä ei tueta." + "Kohdelaite kieltää siirron." + "Käyttäjä peruutti tiedonsiirron." + "Tallennusongelma." + "Ei USB-tallennustilaa" + "Ei SD-korttia. Aseta SD-kortti, niin voit tallentaa siirrettyjä tiedostoja." + "Yhteys epäonnistui." + "Pyyntö ei ole käsiteltävissä." + "Tuntematon virhe." + "Bluetooth, vastaanotettu" + "Bluetooth-jako" + "%1$s vastaanotto valmis." + "%1$s lähetys valmis." + "Saapuvat siirrot" + "Lähtevät siirrot" + "Lähetyshistoria on tyhjä." + "Koko luettelo tyhjennetään." + "Bluetooth-jako: Lähetetyt tiedostot" + "Bluetooth-jako: Vastaanotetut tiedostot" + + %1$d epäonnistui. + %1$d epäonnistui. + + + %1$d onnistui, %2$s + %1$d onnistui, %2$s + + "Tyhjennä luettelo" + "Avaa" + "Poista luettelosta" + "Poista" + "Musiikintunnistus" + "Tallenna" + "Peru" + "Valitse tilit, jotka haluat jakaa Bluetoothin kautta. Tilien käyttöoikeus pitää silti hyväksyä yhdistettäessä." + "Paikkoja jäljellä:" + "Sovelluskuvake" + "Bluetoothin viestinjakoasetukset" + "Tilin valinta epäonnistui. 0 paikkaa jäljellä" + "Bluetooth-ääni liitetty" + "Bluetooth-ääni katkaistu" + "Bluetooth-ääni" + "Yli 4 Gt:n kokoisia tiedostoja ei voi siirtää." + "Muodosta Bluetooth-yhteys" + diff --git a/android/app/res/values-fi/strings_pbap.xml b/android/app/res/values-fi/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..4275fba1d5106234766f374ce215d8a45c7cdf8a --- /dev/null +++ b/android/app/res/values-fi/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Kirjoita kohteen %1$s käyttökerran avain" + "Bluetooth-käyttökerran avain vaaditaan" + "Kohteen %1$s yhteyspyyntö aikakatkaistiin" + "Kohteen %1$s käyttökerran avaimen syöttö aikakatkaistiin" + "Obex-todennuspyyntö" + "Käyttökerran avain" + "Kirjoita kohteen %1$s käyttökerran avain" + "Autosarja" + "Tuntematon nimi" + "Oma nimeni" + "000000" + "Bluetooth-yhteyden jako" + diff --git a/android/app/res/values-fi/strings_pbap_client.xml b/android/app/res/values-fi/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-fi/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-fi/strings_sap.xml b/android/app/res/values-fi/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..b29e3e1460f393d66168c7a0e782e31ca45339b2 --- /dev/null +++ b/android/app/res/values-fi/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM -käyttö" + "Bluetooth SIM -käyttö" + "Pyydetäänkö asiakassovellusta katkaisemaan yhteys?" + "Odotetaan, että asiakassovellus katkaisee yhteyden." + "Katkaise yhteys" + "Pakota yhteyden katkaiseminen" + diff --git a/android/app/res/values-fi/test_strings.xml b/android/app/res/values-fi/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..44cd177032cbd8ef4e4775cf37f81ab6f1cdf598 --- /dev/null +++ b/android/app/res/values-fi/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Lisää tiedot" + "Vahvista tiedot" + "Hyv. tiedot" + "Poista kaikki tiedot" + "OK" + "Poista tiedot" + "Käynnistä TCP-palvelin" + "Ilmoita TCP-palvelin" + diff --git a/android/app/res/values-fr-rCA/config.xml b/android/app/res/values-fr-rCA/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-fr-rCA/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..66ad1f1c333f1443a63b6c06d40170bc2ac0f9c4 --- /dev/null +++ b/android/app/res/values-fr-rCA/strings.xml @@ -0,0 +1,137 @@ + + + + + "Accéder au gestionnaire de téléchargement." + "Permet à l\'application d\'accéder au gestionnaire BluetoothShare et de l\'utiliser pour le transfert de fichiers." + "Ajouter l\'accès d\'un appareil Bluetooth à la liste verte." + "Permet à l\'application d\'ajouter temporairement un appareil Bluetooth à la liste verte afin que celui-ci puisse envoyer des fichiers à cet appareil sans que la confirmation de l\'utilisateur soit nécessaire." + "Bluetooth" + "Périphérique inconnu" + "Inconnu" + "Mode Avion" + "Bluetooth ne peut être utilisé en mode Avion." + + "Vous devez activer la fonction Bluetooth pour pouvoir utiliser les services Bluetooth." + "Activer Bluetooth maintenant?" + "Annuler" + "Activer" + "Transfert de fichier" + "Accepter le fichier entrant?" + "Refuser" + "Accepter" + "OK" + "Expiration du délai de réception du fichier de \"%1$s\"" + "Fichier entrant" + "%1$s est prêt à envoyer le fichier « %2$s »" + "Partage Bluetooth : réception de %1$s" + "Partage Bluetooth : %1$s reçu(s)" + "Partage Bluetooth : fichier %1$s non reçu" + "Partage Bluetooth : envoi de %1$s" + "Partage Bluetooth : %1$s envoyé" + "100 % effectués" + "Partage Bluetooth : fichier %1$s non envoyé" + "Transfert de fichier" + "De : \"%1$s\"" + "Fichier : %1$s" + "Taille du fichier : %1$s" + + "Réception de fichier en cours" + "Arrêter" + "Masquer" + "De" + "Nom de fichier" + "Taille" + "Fichier non reçu" + "Fichier : %1$s" + "Motif : %1$s" + "OK" + "Fichier reçu" + "Ouvrir" + "À : \"%1$s\"" + "Type de fichier : %1$s (%2$s)" + "Envoi de fichier en cours" + "Fichier envoyé" + "OK" + "Le fichier n\'a pas été envoyé à %1$s." + "Fichier : %1$s" + "Fermer" + "OK" + "Fichier inconnu" + "Aucune application ne permet de gérer ce type de fichier. \n" + "Aucun fichier" + "Le fichier n\'existe pas. \n" + "Veuillez patienter…" + "Activation de Bluetooth…" + "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 de %1$s fichiers à \"%2$s\"" + "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" + "Trop de requêtes sont en cours de traitement. Veuillez réessayer plus tard." + "Le transfert de fichier n\'a pas encore commencé." + "Le transfert de fichier est en cours." + "Le transfert de fichiers s\'est déroulé correctement." + "Le contenu n\'est pas compatible." + "L\'appareil cible n\'autorise pas le transfert." + "Le transfert a été annulé par l\'utilisateur." + "Problème de mémoire." + "Aucune mémoire de stockage USB" + "Aucune carte SD trouvée. Insérez une carte SD pour enregistrer les fichiers transférés." + "Échec de la connexion." + "Impossible de traiter la demande correctement." + "Erreur inconnue." + "Reçu par Bluetooth" + "Partage Bluetooth" + "Réception de %1$s terminée" + "Envoi de %1$s terminé" + "Transferts entrants" + "Transferts sortants" + "L\'historique des transferts est vide." + "Tous les éléments de la liste seront effacés." + "Partage Bluetooth : fichiers envoyés" + "Partage Bluetooth : fichiers reçus" + + Échec de %1$d élément. + Échec de %1$d éléments. + + + Réussite de %1$d élément, %2$s + Réussite de %1$d éléments, %2$s + + "Effacer la liste" + "ouvrir" + "Effacer de la liste" + "Effacer" + "En cours de lecture" + "Enregistrer" + "Annuler" + "Sélectionnez les comptes que vous souhaitez partager par Bluetooth. Vous devez toujours accepter l\'accès à ces comptes lors de la connexion." + "Espaces libres :" + "Icône de l\'application" + "Paramètres de partage des messages par Bluetooth" + "Impossible de sélectionner le compte : aucun espace libre." + "Audio Bluetooth connecté" + "Audio Bluetooth déconnecté" + "Audio Bluetooth" + "Les fichiers dépassant 4 Go ne peuvent pas être transférés" + "Connexion au Bluetooth" + diff --git a/android/app/res/values-fr-rCA/strings_pbap.xml b/android/app/res/values-fr-rCA/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..6fecae114d2d6ff54c60055d695f7d85e7358a55 --- /dev/null +++ b/android/app/res/values-fr-rCA/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Entrer la clé de session de %1$s" + "Clé de session Bluetooth requise" + "Délai d\'attente dépassé pour l\'acceptation de connexion à \"%1$s\"" + "Délai d\'attente dépassé pour la saisie de la clé de session avec %1$s" + "Demande d\'authentification Obex" + "Clé de session" + "Entrer la clé de session de %1$s" + "Trousse mains libres" + "Nom inconnu" + "Mon nom" + "000000" + "Partage de contacts par Bluetooth" + diff --git a/android/app/res/values-fr-rCA/strings_pbap_client.xml b/android/app/res/values-fr-rCA/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-fr-rCA/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-fr-rCA/strings_sap.xml b/android/app/res/values-fr-rCA/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2898b45412162a8d390d754c08f60884a46e67a6 --- /dev/null +++ b/android/app/res/values-fr-rCA/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Accès SIM Bluetooth" + "Accès SIM Bluetooth" + "Demander au client de se déconnecter?" + "En attente de déconnexion du client…" + "Déconnecter" + "Forcer la déconnexion" + diff --git a/android/app/res/values-fr-rCA/test_strings.xml b/android/app/res/values-fr-rCA/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..a6feadf397d0dcaeb57d9e7fb4528f269af2a8af --- /dev/null +++ b/android/app/res/values-fr-rCA/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insérer l\'enregistrement" + "Confirmer l\'enregistrement" + "Enregistrement accusé de réception" + "Supprimer tout l\'enregistrement" + "OK" + "Supprimer l\'enregistrement" + "Démarrer le serveur TCP" + "Avertir le serveur TCP" + diff --git a/android/app/res/values-fr/config.xml b/android/app/res/values-fr/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-fr/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-fr/strings.xml b/android/app/res/values-fr/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..03e41d34124abbc271e9adeb1b46a7ae63587c69 --- /dev/null +++ b/android/app/res/values-fr/strings.xml @@ -0,0 +1,137 @@ + + + + + "Accéder au gestionnaire de téléchargement" + "Permet à l\'application d\'accéder au gestionnaire BluetoothShare et de l\'utiliser pour le transfert de fichiers." + "Ajouter des appareils Bluetooth à la liste d\'autorisation" + "Permet à l\'application d\'ajouter temporairement un appareil Bluetooth à la liste d\'autorisation. Ainsi, celui-ci peut envoyer des fichiers à cet appareil sans que la confirmation de l\'utilisateur ne soit nécessaire." + "Bluetooth" + "Périphérique inconnu" + "Inconnu" + "Mode Avion" + "Bluetooth ne peut être utilisé en mode Avion." + + "Vous devez activer la fonction Bluetooth pour pouvoir utiliser les services Bluetooth." + "Activer le Bluetooth maintenant ?" + "Annuler" + "Activer" + "Transfert de fichier" + "Accepter le fichier entrant ?" + "Refuser" + "Accepter" + "OK" + "Expiration du délai de réception du fichier de \"%1$s\"" + "Fichier entrant" + "%1$s est prêt à envoyer le fichier \"%2$s\"." + "Partage Bluetooth : réception de %1$s" + "Partage Bluetooth : %1$s reçu" + "Partage Bluetooth : fichier %1$s non reçu" + "Partage Bluetooth : envoi de %1$s" + "Partage Bluetooth : %1$s envoyé" + "100 % effectués" + "Partage Bluetooth : fichier %1$s non envoyé" + "Transfert de fichier" + "De : \"%1$s\"" + "Fichier : %1$s" + "Taille du fichier : %1$s" + + "Réception de fichier en cours…" + "Arrêter" + "Masquer" + "Expéditeur" + "Nom de fichier" + "Taille" + "Fichier non reçu" + "Fichier : %1$s" + "Motif : %1$s" + "OK" + "Fichier reçu" + "Ouvrir" + "À : \"%1$s\"" + "Type de fichier : %1$s (%2$s)" + "Envoi de fichier en cours..." + "Fichier envoyé" + "OK" + "Le fichier n\'a pas été envoyé à %1$s." + "Fichier : %1$s" + "Fermer" + "OK" + "Fichier inconnu" + "Aucune application ne permet de gérer ce type de fichier. \n" + "Aucun fichier" + "Le fichier n\'existe pas. \n" + "Veuillez patienter..." + "Activation Bluetooth…" + "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 de %1$s fichiers à \"%2$s\"" + "Envoi du fichier à \"%1$s\" interrompu" + "Espace insuffisant sur la mémoire de stockage USB pour enregistrer le fichier." + "Espace insuffisant sur la carte SD pour enregistrer le fichier." + "Espace nécessaire : %1$s" + "Trop de requêtes sont en cours de traitement. Veuillez réessayer ultérieurement." + "Le transfert de fichier n\'a pas encore commencé." + "Le transfert de fichier est en cours." + "Le transfert de fichiers s\'est déroulé correctement." + "Le contenu n\'est pas compatible." + "L\'appareil cible n\'autorise pas le transfert." + "le transfert a été annulé par l\'utilisateur." + "Problème de mémoire." + "Aucune mémoire de stockage USB." + "Carte SD absente. Insérez une carte SD pour enregistrer les fichiers transférés." + "Échec de la connexion." + "Impossible de traiter la demande correctement." + "Erreur inconnue." + "Reçus via Bluetooth" + "Partage Bluetooth" + "Réception de %1$s terminée" + "Envoi de %1$s terminé" + "Transferts entrants" + "Transferts sortants" + "L\'historique des transferts est vide." + "Tous les éléments de la liste seront effacés." + "Partage Bluetooth : fichiers envoyés" + "Partage Bluetooth : fichiers reçus" + + Échec de %1$d élément + Échec de %1$d éléments + + + Réussite de %1$d élément, %2$s + Réussite de %1$d éléments, %2$s + + "Effacer la liste" + "Ouvrir" + "Effacer de la liste" + "Effacer" + "En écoute" + "Enregistrer" + "Annuler" + "Sélectionnez les comptes que vous voulez partager via le Bluetooth. Vous devez toujours accepter l\'accès à ces comptes lors de la connexion." + "Espaces libres :" + "Icône de l\'application" + "Paramètres de partage de la messagerie via le Bluetooth" + "Impossible de sélectionner le compte : aucun espace libre." + "Audio Bluetooth connecté" + "Audio Bluetooth déconnecté" + "Audio Bluetooth" + "Impossible de transférer les fichiers supérieurs à 4 Go" + "Se connecter au Bluetooth" + diff --git a/android/app/res/values-fr/strings_pbap.xml b/android/app/res/values-fr/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..40bf7b4566d7a1be4a2a067250c3cab3911b3528 --- /dev/null +++ b/android/app/res/values-fr/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Entrer la clé de session de %1$s" + "Clé de session Bluetooth requise" + "Délai d\'attente dépassé pour l\'acceptation de connexion à \"%1$s\"" + "Délai d\'attente dépassé pour la saisie de la clé de session avec \"%1$s\"" + "Demande d\'authentification Obex" + "Clé de session" + "Entrer la clé de session de %1$s" + "Carkit" + "Nom inconnu" + "Mon nom" + "000000" + "Partage de contact Bluetooth" + diff --git a/android/app/res/values-fr/strings_pbap_client.xml b/android/app/res/values-fr/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-fr/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-fr/strings_sap.xml b/android/app/res/values-fr/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..047dae33eafbdf07583ca9399c88b75092e292be --- /dev/null +++ b/android/app/res/values-fr/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Accès SIM Bluetooth" + "Accès SIM Bluetooth" + "Demander au client de se déconnecter ?" + "En attente de déconnexion du client…" + "Déconnecter" + "Forcer la déconnexion" + diff --git a/android/app/res/values-fr/test_strings.xml b/android/app/res/values-fr/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..a6feadf397d0dcaeb57d9e7fb4528f269af2a8af --- /dev/null +++ b/android/app/res/values-fr/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Insérer l\'enregistrement" + "Confirmer l\'enregistrement" + "Enregistrement accusé de réception" + "Supprimer tout l\'enregistrement" + "OK" + "Supprimer l\'enregistrement" + "Démarrer le serveur TCP" + "Avertir le serveur TCP" + diff --git a/android/app/res/values-gl/config.xml b/android/app/res/values-gl/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-gl/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-gl/strings.xml b/android/app/res/values-gl/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..013ce2cfd6cb76f01f63be9c5348608c0239f4aa --- /dev/null +++ b/android/app/res/values-gl/strings.xml @@ -0,0 +1,137 @@ + + + + + "Acceso ao administrador de descargas." + "Permite á aplicación acceder ao administrador BluetoothShare e usalo para transferir ficheiros." + "Permitir acceso ao dispositivo mediante Bluetooth." + "Permite á aplicación autorizar un dispositivo Bluetooth para que poida enviarlle ficheiros a este dispositivo sen confirmación do usuario." + "Bluetooth" + "Dispositivo descoñecido" + "Descoñecido" + "Modo avión" + "Non se pode usar o Bluetooth no modo avión." + + "Para utilizar servizos de Bluetooth, primeiro tes que activar o Bluetooth." + "Queres activar o Bluetooth agora?" + "Cancelar" + "Activar" + "Transferencia de ficheiros" + "Queres aceptar o ficheiro entrante?" + "Rexeitar" + "Aceptar" + "Aceptar" + "Superouse o tempo de espera mentres se aceptaba un ficheiro entrante de \"%1$s\"" + "Ficheiro entrante" + "%1$s está listo para enviar %2$s" + "Uso compartido por Bluetooth: recibindo %1$s" + "Uso compartido por Bluetooth: %1$s recibido" + "Uso compartido por Bluetooth: ficheiro %1$s non recibido" + "Uso compartido por Bluetooth: enviando %1$s" + "Uso compartido por Bluetooth: %1$s enviado" + "100% completado" + "Uso compartido por Bluetooth: ficheiro %1$s non enviado" + "Transferencia de ficheiros" + "De: \"%1$s\"" + "Ficheiro: %1$s" + "Tamaño do ficheiro: %1$s" + + "Recibindo ficheiro..." + "Deter" + "Ocultar" + "De" + "Nome do ficheiro" + "Tamaño" + "Ficheiro non recibido" + "Ficheiro: %1$s" + "Motivo: %1$s" + "Aceptar" + "Ficheiro recibido" + "Abrir" + "Para: \"%1$s\"" + "Tipo de ficheiro: %1$s (%2$s)" + "Enviando ficheiro..." + "Ficheiro enviado" + "Aceptar" + "O ficheiro non se enviou a \"%1$s\"." + "Ficheiro: %1$s" + "Pechar" + "Aceptar" + "Ficheiro descoñecido" + "Non hai ningunha aplicación que admita este tipo de ficheiro. \n" + "Ningún ficheiro" + "O ficheiro non existe. \n" + "Agarda..." + "Activando Bluetooth…" + "Recibirase o ficheiro. Comproba o progreso no panel de notificacións." + "O ficheiro non se pode recibir." + "Detívose a recepción do ficheiro de \"%1$s\"" + "Enviando ficheiro a \"%1$s\"" + "Enviando %1$s ficheiros a \"%2$s\"" + "Detívose o envío do ficheiro a \"%1$s\"" + "Non hai espazo suficiente no almacenamento USB para gardar o ficheiro." + "Non hai espazo suficiente na tarxeta SD para gardar o ficheiro." + "Espazo necesario: %1$s" + "Estanse procesando demasiadas solicitudes. Téntao de novo máis tarde." + "Aínda non se iniciou a transferencia de ficheiros." + "A transferencia de ficheiros está en curso." + "A transferencia de ficheiros completouse correctamente." + "O contido non é compatible." + "Transferencia prohibida polo dispositivo de destino." + "Transferencia cancelada polo usuario." + "Problema de almacenamento" + "Non hai almacenamento USB." + "Non hai tarxeta SD. Insire unha para gardar os ficheiros transferidos." + "Conexión incorrecta" + "A solicitude non se pode atender correctamente." + "Erro descoñecido" + "Recibido por Bluetooth" + "Uso compartido por Bluetooth" + "%1$s: recepción completa." + "Envío de %1$s completado." + "Transferencias de información entrante" + "Transferencias de información saínte" + "O historial de transferencias está baleiro." + "Borraranse todos os elementos da lista." + "Uso compartido por Bluetooth: ficheiros enviados" + "Uso compartido por Bluetooth: ficheiros recibidos" + + %1$d incorrectos. + %1$d incorrecto. + + + %1$d correctos, %2$s + %1$d correcto, %2$s + + "Borrar lista" + "Abrir" + "Borrar da lista" + "Borrar" + "Está soando" + "Gardar" + "Cancelar" + "Selecciona as contas que queres compartir a través de Bluetooth. Aínda así, tes que aceptar o acceso ás contas cando te conectes." + "Rañuras restantes:" + "Icona da aplicación" + "Configuración de mensaxes compartidas por Bluetooth" + "Non se pode seleccionar a conta. Quedan 0 rañuras" + "Conectouse o audio por Bluetooth" + "Desconectouse o audio por Bluetooth" + "Audio por Bluetooth" + "Non se poden transferir ficheiros de máis de 4 GB" + "Conectar ao Bluetooth" + diff --git a/android/app/res/values-gl/strings_pbap.xml b/android/app/res/values-gl/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..7a3895214af49b75883f898a679283f3f4fa0aab --- /dev/null +++ b/android/app/res/values-gl/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Escribe a clave da sesión para %1$s" + "Requírese unha clave de sesión Bluetooth" + "Superouse o tempo de espera para aceptar a conexión con %1$s" + "Superouse o tempo de espera para introducir a clave de sesión con %1$s" + "Solicitude de autenticación Obex" + "Clave de sesión" + "Escribe a clave da sesión para %1$s" + "Kit para coche" + "Nome descoñecido" + "O meu nome" + "000000" + "Compartir contactos por Bluetooth" + diff --git a/android/app/res/values-gl/strings_pbap_client.xml b/android/app/res/values-gl/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-gl/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-gl/strings_sap.xml b/android/app/res/values-gl/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..d3f7e587b950bb256e3cb532cb808c89957a0c99 --- /dev/null +++ b/android/app/res/values-gl/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Acceso á SIM do Bluetooth" + "Acceso á SIM do Bluetooth" + "Queres solicitar ao cliente que desconecte?" + "Esperando a que o cliente desconecte" + "Desconectar" + "Forzar desconexión" + diff --git a/android/app/res/values-gl/test_strings.xml b/android/app/res/values-gl/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..d919f86f784b6db99b91155aa6ecefde0a969905 --- /dev/null +++ b/android/app/res/values-gl/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Inserir rexistro" + "Confirmar rexistro" + "Rexistro Ack" + "Eliminar todos os rexistros" + "Aceptar" + "Eliminar rexistro" + "Iniciar servidor TCP" + "Notificar ao servidor TCP" + diff --git a/android/app/res/values-gu/config.xml b/android/app/res/values-gu/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-gu/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-gu/strings.xml b/android/app/res/values-gu/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..00086bb06f95c9040d99f9985b6edc696cdb0b02 --- /dev/null +++ b/android/app/res/values-gu/strings.xml @@ -0,0 +1,137 @@ + + + + + "ઍક્સેસ ડાઉનલોડ મેનેજર." + "એપ્લિકેશનને BluetoothShare મેનેજર અ‍ૅક્સેસ કરવાની અને ફાઇલો ટ્રાન્સફર કરવા માટે તેનો ઉપયોગ કરવાની મંજૂરી આપે છે." + "વ્હાઇટલિસ્ટ બ્લૂટૂથ ડિવાઇસ ઍક્સેસ." + "ઍપને હંગામી રૂપે બ્લૂટૂથ ડિવાઇસને વ્હાઇટલિસ્ટ કરવાની મંજૂરી આપે છે, જેનાથી તે ડિવાઇસ આ ડિવાઇસ પર વપરાશકર્તાની ખાતરી વિના ફાઇલ મોકલી શકે છે." + "બ્લૂટૂથ" + "અજાણ્યું ઉપકરણ" + "અજાણ્યો" + "એરપ્લેન મોડ" + "તમે એરપ્લેન મોડમાં બ્લૂટૂથ નો ઉપયોગ કરી શકતા નથી." + + "બ્લૂટૂથ સેવાઓનો ઉપયોગ કરવા માટે, તમારે પ્રથમ બ્લૂટૂથ ચાલુ કરવું આવશ્યક છે." + "હવે બ્લૂટૂથ ચાલુ કરીએ?" + "રદ કરો" + "ચાલુ કરો" + "ફાઇલ સ્થાનાંતરણ" + "આવનારી ફાઇલ સ્વીકારીએ?" + "નકારો" + "સ્વીકારો" + "ઓકે" + "\"%1$s\" તરફની એક આવનારી ફાઇલ સ્વીકારતી વખતે સમયસમાપ્તિ થઈ હતી" + "આવનારી ફાઇલ" + "%1$s, %2$s મોકલવા માટે તૈયાર છે" + "બ્લૂટૂથ શેર: %1$s પ્રાપ્ત કરી રહ્યું છે" + "બ્લૂટૂથ શેર: %1$s પ્રાપ્ત" + "બ્લૂટૂથ શેર: ફાઇલ %1$s પ્રાપ્ત થઈ નથી" + "બ્લૂટૂથ શેર: %1$s મોકલી રહ્યું છે" + "બ્લૂટૂથ શેર: %1$s મોકલી" + "100% પૂર્ણ" + "બ્લૂટૂથ શેર: ફાઇલ %1$s મોકલાઈ નથી" + "ફાઇલ સ્થાનાંતરણ" + "દ્વારા: \"%1$s\"" + "ફાઇલ: %1$s" + "ફાઇલનું કદ: %1$s" + + "ફાઇલ પ્રાપ્ત થઈ રહી છે ..." + "રોકો" + "છુપાવો" + "માંથી" + "ફાઇલનું નામ" + "કદ" + "ફાઇલ પ્રાપ્ત થઈ નથી" + "ફાઇલ: %1$s" + "કારણ: %1$s" + "ઓકે" + "ફાઇલ પ્રાપ્ત" + "ખોલો" + "પ્રતિ: \"%1$s\"" + "ફાઇલ પ્રકાર: %1$s (%2$s)" + "ફાઇલ મોકલી રહ્યું છે…" + "ફાઇલ મોકલી" + "ઓકે" + "ફાઇલ \"%1$s\" ને મોકલાઈ નહોતી." + "ફાઇલ: %1$s" + "બંધ કરો" + "ઓકે" + "અજાણી ફાઇલ" + "આ પ્રકારની ફાઇલ હેન્ડલ કરવા માટે કોઈ ઍપ્લિકેશન નથી. \n" + "કોઈ ફાઇલ નથી" + "આ ફાઇલ અસ્તિત્વમાં નથી. \n" + "કૃપા કરીને રાહ જુઓ..." + "બ્લૂટૂથ ચાલુ કરી રહ્યું છે…" + "ફાઇલ પ્રાપ્ત થશે. સૂચનાઓ પેનલમાં પ્રગતિ તપાસો." + "ફાઇલ પ્રાપ્ત કરી શકાતી નથી." + "\"%1$s\" થી ફાઇલ પ્રાપ્ત કરવાનું બંધ કર્યું" + "\"%1$s\" પર ફાઇલ મોકલી રહ્યાં છે" + "%1$s ફાઇલો \"%2$s\" પર મોકલી રહ્યાં છે" + "\"%1$s\" પર ફાઇલ મોકલવું બંધ કર્યું" + "ફાઇલને USB સ્ટોરેજમાં સાચવવા માટે પૂરતી સ્પેસ નથી." + "ફાઇલને SD કાર્ડમાં સાચવવા માટે પૂરતી સ્પેસ નથી." + "સ્થાન જરૂરી: %1$s" + "ઘણી બધી વિનંતીઓ પર પ્રક્રિયા કરવામાં આવી રહી છે. પછીથી ફરી પ્રયાસ કરો." + "ફાઇલ સ્થાનાંતરણ હજી પ્રારંભ થયું નથી." + "ફાઇલ સ્થાનાંતરણ ચાલુ છે." + "ફાઇલ સ્થાનાંતરણ સફળતાપૂર્વક પૂર્ણ થયું." + "સામગ્રી સમર્થિત નથી." + "સ્થાનાંતરણ લક્ષિત ઉપકરણ દ્વારા પ્રતિબંધિત." + "વપરાશકર્તા દ્વારા સ્થાનાંતરણ રદ." + "સંગ્રહ સમસ્યા." + "કોઈ USB સ્ટોરેજ નથી." + "કોઈ SD કાર્ડ નથી. ટ્રાન્સફર કરેલી ફાઇલોને સાચવવા માટે SD કાર્ડ દાખલ કરો." + "કનેક્શન અસફળ." + "વિનંતી યોગ્ય રીતે હેન્ડલ કરી શકાતી નથી." + "અજાણી ભૂલ." + "બ્લૂટૂથથી મળેલી ફાઇલો" + "બ્લૂટૂથ શેર" + "%1$s પ્રાપ્તિ પૂર્ણ" + "%1$s મોકલવું પૂર્ણ." + "ઇન્બાઉન્ડ સ્થાનાંતરણો" + "આઉટબાઉન્ડ સ્થાનાંતરણ" + "કંઈપણ ટ્રાન્સફર કરવામાં આવ્યું નથી." + "સૂચિમાંથી તમામ આઇટમ્સ સાફ કરવામાં આવશે." + "બ્લૂટૂથ શેર: મોકલેલી ફાઇલો" + "બ્લૂટૂથ શેર: પ્રાપ્ત ફાઇલો" + + %1$d અસફળ. + %1$d અસફળ. + + + %1$d સફળ, %2$s + %1$d સફળ, %2$s + + "સૂચિ સાફ કરો" + "ખોલો" + "સૂચિમાંથી સાફ કરો" + "સાફ કરો" + "હમણાં વાગી રહ્યું છે" + "સાચવો" + "રદ કરો" + "તમે બ્લૂટૂથ મારફતે શેર કરવા માગતા હો તે એકાઉન્ટ્સ પસંદ કરો. તમારે હજી પણ કનેક્ટ કરતી વખતે એકાઉન્ટ્સ પરની કોઈપણ અ‍ૅક્સેસ સ્વીકારવી પડશે." + "સ્લોટ્સ બાકી:" + "ઍપ્લિકેશન આયકન" + "બ્લૂટૂથ સંદેશ શેરિંગ સેટિંગ્સ" + "એકાઉન્ટ પસંદ કરી શકાતું નથી. 0 સ્લોટ્સ બાકી" + "બ્લૂટૂથ ઑડિઓ કનેક્ટ થયું" + "બ્લૂટૂથ ઑડિઓ ડિસ્કનેક્ટ થયું" + "બ્લૂટૂથ ઑડિઓ" + "4GB કરતા મોટી ફાઇલ ટ્રાન્સફર કરી શકાતી નથી" + "બ્લૂટૂથ સાથે કનેક્ટ કરો" + diff --git a/android/app/res/values-gu/strings_pbap.xml b/android/app/res/values-gu/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..6dd2b6c71e28586079e8d1520e1df3ef0a79a7bf --- /dev/null +++ b/android/app/res/values-gu/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s માટેની સત્ર કી લખો" + "બ્લૂટૂથ સત્ર કી આવશ્યક છે" + "%1$s સાથે કનેક્શન સ્વીકારવા માટેનો સમય સમાપ્ત થયો" + "%1$s સાથે સત્ર કી ઇનપુટ કરવાનો સમય સમાપ્ત થયો" + "Obex પ્રમાણીકરણ વિનંતી" + "સત્ર કી" + "%1$s માટેની સત્ર કી લખો" + "કારકિટ" + "અજાણ્યું નામ" + "મારું નામ" + "000000" + "બ્લૂટૂથ સંપર્ક શેર કરો" + diff --git a/android/app/res/values-gu/strings_pbap_client.xml b/android/app/res/values-gu/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-gu/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-gu/strings_sap.xml b/android/app/res/values-gu/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..b42c6895f8b0bf796adeac9931992987f32be780 --- /dev/null +++ b/android/app/res/values-gu/strings_sap.xml @@ -0,0 +1,10 @@ + + + "બ્લૂટૂથ સિમ ઍક્સેસ" + "બ્લૂટૂથ સિમ ઍક્સેસ" + "ક્લાઇન્ટને ડિસ્કનેક્ટ કરવાની વિનંતી કરીએ?" + "ડિસ્કનેક્ટ કરવા માટે ક્લાઇન્ટની રાહ જોઈ રહ્યાં છે" + "ડિસ્કનેક્ટ કરો" + "ડિસ્કનેક્ટ કરવાની ફરજ પાડો" + diff --git a/android/app/res/values-gu/test_strings.xml b/android/app/res/values-gu/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f064d59628bf3b3a7dd71d508a27d5642c9820a1 --- /dev/null +++ b/android/app/res/values-gu/test_strings.xml @@ -0,0 +1,13 @@ + + + "બ્લૂટૂથ" + "રેકોર્ડ શામેલ કરો" + "રેકોર્ડની પુષ્ટિ કરો" + "Ack રેકોર્ડ" + "તમામ રેકોર્ડ કાઢી નાખો" + "ઓકે" + "રેકોર્ડ કાઢી નાખો" + "TCP સર્વર પ્રારંભ કરો" + "TCP સર્વરને સૂચિત કરો" + diff --git a/android/app/res/values-hi/config.xml b/android/app/res/values-hi/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-hi/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-hi/strings.xml b/android/app/res/values-hi/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..9b607338e53cd4cd8b63cd39c8803990e78d71ef --- /dev/null +++ b/android/app/res/values-hi/strings.xml @@ -0,0 +1,137 @@ + + + + + "डाउनलोड मैनेजर में पहुंच पाएं." + "ऐप्लिकेशन को BluetoothShare मैनेजर के इस्तेमाल की मंज़ूरी देता है और फ़ाइलों को ट्रांसफ़र करने के लिए उसका उपयोग करने देता है." + "ब्लूटूथ डिवाइस ऐक्सेस को व्हाइटलिस्ट में डालें." + "ऐप्लिकेशन को, यह कुछ देर के लिए किसी ब्लूटूथ डिवाइस को व्हाइटलिस्ट में डालने की सुविधा देता है. इससे उपयोगकर्ता की पुष्टि के बिना, उस डिवाइस से इस डिवाइस में फ़ाइलें भेजी जा सकती हैं." + "ब्लूटूथ" + "अज्ञात डिवाइस" + "अज्ञात" + "हवाई जहाज़ मोड" + "आप हवाई जहाज मोड में ब्लूटूथ का उपयोग नहीं कर सकते हैं." + + "ब्लूटूथ सेवाओं के उपयोग के लिए, आपको पहले ब्लूटूथ चालू करना चाहिए." + "अभी ब्लूटूथ चालू करें?" + "रद्द करें" + "चालू करें" + "फ़ाइल ट्रांसफ़र करें" + "आवक फ़ाइल स्वीकार करें?" + "अस्वीकारें" + "स्वीकारें" + "ठीक है" + "\"%1$s\" से आने वाली फ़ाइल स्वीकार करते हुए टाइम आउट हो गया." + "आवक फ़ाइल" + "%1$s %2$s भेजने के लिए तैयार है" + "ब्लूटूथ शेयर: %1$s पा रहा है" + "ब्लूटूथ शेयर: %1$s पाई गई" + "ब्लूटूथ शेयर: फ़ाइल %1$s नहीं मिली" + "ब्लूटूथ शेयर: %1$s भेज रहा है" + "ब्लूटूथ शेयर: %1$s भेजा गया" + "100% पूरा" + "ब्लूटूथ शेयर: फ़ाइल %1$s भेजी नहीं गई" + "फ़ाइल ट्रांसफ़र करें" + "प्रेषक: \"%1$s\"" + "फ़ाइल: %1$s" + "फ़ाइल आकार: %1$s" + + "फ़ाइल पा रहा है…" + "रोकें" + "छुपाएं" + "प्रेषक" + "फ़ाइल नाम" + "आकार" + "फ़ाइल नहीं मिली" + "फ़ाइल: %1$s" + "कारण: %1$s" + "ठीक है" + "फ़ाइल मिली" + "खोलें" + "प्रति: \"%1$s\"" + "फ़ाइल प्रकार: %1$s (%2$s)" + "फ़ाइल भेज रहा है…" + "फ़ाइल भेजी गई" + "ठीक है" + "फ़ाइल \"%1$s\" को नहीं भेजी गई थी." + "फ़ाइल: %1$s" + "बंद करें" + "ठीक है" + "अज्ञात फ़ाइल" + "इस प्रकार की फ़ाइल प्रबंधित करने के लिए कोई ऐप्लिकेशन नहीं है. \n" + "कोई फ़ाइल नहीं" + "फ़ाइल मौजूद नहीं है. \n" + "कृपया प्रतीक्षा करें..." + "ब्लूटूथ चालू कर रहा है…" + "फ़ाइल मिलेगी. सूचना पैनल में प्रगति देखें." + "फ़ाइल पाई नहीं जा सकती." + "\"%1$s\" से फ़ाइल पाना रोका गया" + "\"%1$s\" को फ़ाइल भेज रहा है" + "\"%2$s\" को %1$s फ़ाइलें भेज रहा है" + "\"%1$s\" को फ़ाइल भेजना रोका गया" + "इस फ़ाइल को सेव करने के लिए यूएसबी मेमोरी में ज़रूरत के हिसाब से जगह खाली नहीं है." + "इस फ़ाइल को सेव करने के लिए एसडी कार्ड में ज़रूरत के हिसाब से जगह खाली नहीं है." + "जगह चाहिए: %1$s" + "बहुत सारे अनुरोधों पर कार्रवाई चल रही है. बाद में फिर से प्रयास करें." + "फ़ाइल स्थानांतरण अभी तक प्रारंभ नहीं हुआ." + "फ़ाइल स्थानांतरण जारी है." + "फ़ाइल स्थानांतरण सफलतापूर्वक पूरा हुआ." + "सामग्री समर्थित नहीं है." + "लक्ष्य डिवाइस द्वारा स्थानांतरण प्रतिबंधित किया गया." + "उपयोगकर्ता ने ट्रांसफर रद्द किया." + "मेमोरी समस्या." + "कोई USB मेमोरी नहीं." + "कोई SD कार्ड नहीं. ट्रांसफ़र की गई फ़ाइलें सेव करने के लिए SD कार्ड लगाएं." + "कनेक्‍शन विफल." + "अनुरोध को सही तरह से प्रबंधित नहीं किया जा सकता." + "अज्ञात गड़बड़ी‍." + "ब्लूटूथ से मिली फ़ाइलें" + "ब्लूटूथ के ज़रिए शेयर" + "%1$s मिलना पूरा हुआ." + "%1$s भेजना पूरा हुआ." + "इनबाउंड स्थानांतरण" + "आउटबाउंड स्थानांतरण" + "कुछ भी ट्रांसफ़र नहीं किया गया." + "सूची से सभी आइटम साफ़ कर दिए जाएंगे." + "ब्लूटूथ शेयर: भेजी गई फ़ाइलें" + "ब्लूटूथ शेयर: पाई गई फ़ाइलें" + + %1$d असफल. + %1$d असफल. + + + %1$d सफल, %2$s + %1$d सफल, %2$s + + "सूची साफ़ करें" + "खोलें" + "सूची से साफ़ करें" + "साफ़ करें" + "अभी चल रहा है" + "सेव करें" + "रद्द करें" + "वे खाते चुनें जिन्हें आप ब्लूटूथ के ज़रिये शेयर करना चाहते हैं. आपको अब भी कनेक्ट करते समय खातों के किसी भी ऐक्सेस को स्वीकार करना होगा." + "शेष स्लॉट:" + "ऐप्लिकेशन आइकॉन" + "ब्लूटूथ संदेश साझाकरण सेटिंग" + "खाता नहीं चुना जा सकता. 0 स्लॉट शेष" + "ब्लूटूथ ऑडियो कनेक्ट किया गया" + "ब्लूटूथ ऑडियो डिसकनेक्ट किया गया" + "ब्लूटूथ ऑडियो" + "4 जीबी से बड़ी फ़ाइलें ट्रांसफ़र नहीं की जा सकतीं" + "ब्लूटूथ से कनेक्ट करें" + diff --git a/android/app/res/values-hi/strings_pbap.xml b/android/app/res/values-hi/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..12e5e84507cd45df0ae1a61db0b947a38fae0ae2 --- /dev/null +++ b/android/app/res/values-hi/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s के लिए सत्र कुंजी लिखें" + "ब्लूटूथ सत्र कुंजी आवश्यक" + "%1$s के साथ जुड़ते समय टाइम आउट हो गया" + "%1$s के साथ सत्र कुंजी इनपुट करते समय टाइम आउट हो गया" + "Obex की पुष्टि का अनुरोध" + "सत्र कुंजी" + "%1$s के लिए सत्र कुंजी लिखें" + "कार किट" + "अज्ञात नाम" + "मेरा नाम" + "000000" + "ब्लूटूथ संपर्क शेयर करें" + diff --git a/android/app/res/values-hi/strings_pbap_client.xml b/android/app/res/values-hi/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-hi/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-hi/strings_sap.xml b/android/app/res/values-hi/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..9e9d76e55dbe3e4982c3ed1708530d82b76e2729 --- /dev/null +++ b/android/app/res/values-hi/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ब्लूटूथ सिम ऐक्सेस" + "ब्लूटूथ सिम ऐक्सेस" + "डिसकनेक्ट करने के लिए क्लाइंट से अनुरोध करें?" + "डिसकनेक्ट करने के लिए क्लाइंट की प्रतीक्षा की जा रही है" + "डिसकनेक्ट करें" + "बलपूर्वक डिसकनेक्ट करें" + diff --git a/android/app/res/values-hi/test_strings.xml b/android/app/res/values-hi/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..52e0e1a17eb8d7836798eb2f84f06cbdfe4337fe --- /dev/null +++ b/android/app/res/values-hi/test_strings.xml @@ -0,0 +1,13 @@ + + + "ब्लूटूथ" + "रिकॉर्ड सम्मिलित करें" + "रिकॉर्ड की पुष्टि करें" + "रिकॉर्ड अभिस्वीकृत करें" + "सभी रिकॉर्ड मिटाएं" + "ठीक है" + "रिकॉर्ड मिटाएं" + "TCP सर्वर शरू करें" + "TCP सर्वर को सूचित करें" + diff --git a/android/app/res/values-hr/config.xml b/android/app/res/values-hr/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-hr/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-hr/strings.xml b/android/app/res/values-hr/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..25e04da5147eef032b95cf57e6fb513ba1b2ff3e --- /dev/null +++ b/android/app/res/values-hr/strings.xml @@ -0,0 +1,139 @@ + + + + + "Pristupite upravitelju za preuzimanje." + "Aplikaciji omogućuje pristup upravitelju za BluetoothShare i njegovu upotrebu za prijenos datoteka." + "Stavi pristup Bluetooth uređaja na popis prihvaćenih." + "Omogućuje aplikaciji privremeno stavljanje Bluetooth uređaja na popis prihvaćenih, čime mu omogućuje slanje datoteka na ovaj uređaj bez korisnikove potvrde." + "Bluetooth" + "Nepoznati uređaj" + "Nepoznato" + "Način rada u zrakoplovu" + "Bluetooth se ne može upotrebljavati u načinu rada u zrakoplovu." + + "Prima: upotreba Bluetooth usluga, prvo morate uključiti Bluetooth." + "Uključiti Bluetooth sada?" + "Odustani" + "Uključi" + "Prijenos datoteke" + "Prihvatiti dolaznu datoteku?" + "Odbaci" + "Prihvaćam" + "U redu" + "Za vrijeme primanja datoteke koju šalje \"%1$s\" došlo je do privremenog prekida" + "Dolazna datoteka" + "%1$s može poslati datoteku %2$s" + "Dijeljenje Bluetoothom: Primanje datoteke %1$s" + "Dijeljenje Bluetoothom: Primljena datoteka %1$s" + "Dijeljenje Bluetoothom: Datoteka %1$s nije primljena" + "Dijeljenje Bluetoothom: Slanje datoteke %1$s" + "Dijeljenje Bluetoothom: Datoteka %1$s poslana" + "100% dovršeno" + "Dijeljenje Bluetoothom: Datoteka %1$s nije poslana" + "Prijenos datoteke" + "Šalje: \"%1$s\"" + "Datoteka: %1$s" + "Veličina datoteke: %1$s" + + "Primanje datoteke..." + "Zaustavi" + "Sakrij" + "Šalje" + "Naziv datoteke" + "Veličina" + "Datoteka nije primljena" + "Datoteka: %1$s" + "Razlog: %1$s" + "U redu" + "Datoteka primljena" + "Otvori" + "Prima: \"%1$s\"" + "Vrsta datoteke: %1$s (%2$s)" + "Slanje datoteke..." + "Datoteka poslana" + "U redu" + "Datoteka nije poslana primatelju \"%1$s\"." + "Datoteka: %1$s" + "Zatvori" + "U redu" + "Nepoznata datoteka" + "Nema aplikacija za obradu ove vrste datoteke. \n" + "Nema datoteke" + "Datoteka ne postoji. \n" + "Pričekajte…" + "Uključivanje Bluetootha…" + "Datoteka će biti primljena. Napredak provjerite na ploči Obavijesti." + "Datoteka ne može biti primljena." + "Zaustavljeno primanje datoteke od pošiljatelja \"%1$s\"" + "Slanje datoteke primatelju \"%1$s\"" + "Sljedeći broj datoteka: %1$s šalje se primatelju \"%2$s\"" + "Zaustavljeno slanje datoteke primatelju \"%1$s\"" + "U USB pohrani nema dovoljno prostora za spremanje datoteke." + "Na SD kartici nema dovoljno prostora za spremanje datoteke." + "Potrebno prostora: %1$s" + "U obradi je previše zahtjeva. Pokušajte ponovo kasnije." + "Prijenos datoteke još nije započeo." + "Prijenos datoteke u tijeku." + "Prijenos datoteke uspješno je dovršen." + "Sadržaj nije podržan." + "Ciljni uređaj zabranio je prijenos." + "Korisnik je otkazao prijenos." + "Problem s pohranjivanjem." + "Nema USB pohrane." + "Nema SD kartice. Umetnite SD karticu kako biste spremili prenesene datoteke." + "Neuspješno povezivanje." + "Zahtjev nije moguće ispravno obraditi." + "Nepoznata pogreška." + "Primljeno Bluetoothom" + "Dijeljenje Bluetoothom" + "%1$s primljeno u cijelosti." + "%1$s Poslano u potpunosti." + "Dolazni prijenosi" + "Odlazni prijenosi" + "Povijest prijenosa je prazna." + "S popisa će biti izbrisane sve stavke." + "Bluetooth dijeljenje: poslane datoteke" + "Bluetooth dijeljenje: primljene datoteke" + + %1$d bez uspjeha. + %1$d bez uspjeha. + %1$d bez uspjeha. + + + %1$d s uspjehom, %2$s + %1$d s uspjehom, %2$s + %1$d s uspjehom, %2$s + + "Izbriši popis" + "Otvori" + "Izbriši s popisa" + "Brisanje" + "Upravo svira" + "Spremi" + "Odustani" + "Odaberite račune koje želite dijeliti putem Bluetootha. I dalje morate prihvatiti svako pristupanje računima prilikom povezivanja." + "Preostala mjesta:" + "Ikona aplikacije" + "Postavke dijeljenja poruka putem Bluetootha" + "Račun nije moguće odabrati. Nema više nijednog mjesta" + "Bluetooth Audio povezan" + "Veza Bluetooth Audija prekinuta" + "Bluetooth Audio" + "Datoteke veće od 4 GB ne mogu se prenijeti" + "Povezivanje s Bluetoothom" + diff --git a/android/app/res/values-hr/strings_pbap.xml b/android/app/res/values-hr/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..aab5b7e221b87a22d9e48f3a5d56dea9582f5712 --- /dev/null +++ b/android/app/res/values-hr/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Upiši šifru sesije za %1$s" + "Potrebna je šifra za Bluetooth sesiju" + "Došlo je do prekoračenja vremena za prihvat povezivanja s korisnikom %1$s" + "Došlo je do prekoračenja vremena za unos šifre sesije s korisnikom %1$s" + "Zahtjev za provjeru autentičnosti Obex protokola" + "Šifra sesije" + "Upiši šifru sesije za %1$s" + "Komplet za auto" + "Nepoznati naziv" + "Moje ime" + "000000" + "Dijeljenje kontakata Bluetoothom" + diff --git a/android/app/res/values-hr/strings_pbap_client.xml b/android/app/res/values-hr/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-hr/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-hr/strings_sap.xml b/android/app/res/values-hr/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..a9ab8584d3c03876b2a48cd0912c83033945772e --- /dev/null +++ b/android/app/res/values-hr/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Pristup SIM-u putem Bluetootha" + "Pristup SIM-u putem Bluetootha" + "Želite li postaviti zahtjev klijentu da prekine vezu?" + "Čekanje da klijent prekine vezu." + "Prekini vezu" + "Prisilno prekini vezu" + diff --git a/android/app/res/values-hr/test_strings.xml b/android/app/res/values-hr/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3635b017a6e8c8ac257794b89d97099d52aac2d4 --- /dev/null +++ b/android/app/res/values-hr/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Umetni zapis" + "Potvrdi zapis" + "Ack zapis" + "Izbriši sve zapise" + "U redu" + "Izbriši zapis" + "Pokreni TCP poslužitelj" + "Obavijesti TCP poslužitelj" + diff --git a/android/app/res/values-hu/config.xml b/android/app/res/values-hu/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-hu/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-hu/strings.xml b/android/app/res/values-hu/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..10a58a4de3bda20791cb6d45beebadd5b7be990e --- /dev/null +++ b/android/app/res/values-hu/strings.xml @@ -0,0 +1,137 @@ + + + + + "Hozzáférés a letöltéskezelőhöz." + "Lehetővé teszi az alkalmazás számára a BluetoothShare kezelő elérését és használatát fájlátvitelre." + "Bluetooth-eszköz hozzáférésének engedélyezése" + "Lehetővé teszi az alkalmazás számára Bluetooth-eszközök ideiglenes engedélyezését, amelyek így a felhasználó jóváhagyása nélkül küldhetnek fájlokat erre az eszközre." + "Bluetooth" + "Ismeretlen eszköz" + "Ismeretlen" + "Repülőgép üzemmód" + "A Bluetooth nem használható Repülőgép üzemmódban." + + "A Bluetooth-szolgáltatásokhoz használatához először be kell kapcsolnia a Bluetooth-funkciót." + "Bekapcsolja most a Bluetooth-funkciót?" + "Mégse" + "Bekapcsolás" + "Fájlátvitel" + "Fogadja a bejövő fájlt?" + "Elutasítás" + "Fogadás" + "OK" + "Időtúllépés történt \"%1$s\" feladótól érkező fájl fogadása során" + "Beérkező fájl" + "%1$s készen áll a következő küldésére: %2$s" + "Bluetooth megosztás: %1$s fogadása" + "Bluetooth megosztás: %1$s fogadva" + "Bluetooth megosztás: %1$s fájl fogadása nem sikerült" + "Bluetooth megosztás: %1$s küldése" + "Bluetooth megosztás: %1$s elküldve" + "100% kész" + "Bluetooth megosztás: %1$s fájl küldése nem sikerült" + "Fájlátvitel" + "Feladó: \"%1$s\"" + "Fájl: %1$s" + "Fájlméret: %1$s" + + "Fájl fogadása..." + "Leállítás" + "Elrejtés" + "Küldő" + "Fájlnév" + "Méret" + "Nem sikerült fogadni a fájlt" + "Fájl: %1$s" + "Indok: %1$s" + "OK" + "A fájl megérkezett" + "Megnyitás" + "Címzett: \"%1$s\"" + "Fájltípus: %1$s (%2$s)" + "Fájl küldése..." + "A fájl elküldve" + "OK" + "A fájlt nem sikerült elküldeni a következőnek: \"%1$s\"." + "Fájl: %1$s" + "Bezárás" + "OK" + "Ismeretlen fájl" + "Nincs alkalmazás a fájltípus kezeléséhez. \n" + "Nincs fájl" + "A fájl nem létezik. \n" + "Kérjük, várjon..." + "Bluetooth bekapcsolása..." + "A fájl fogadható. Az átvitel haladását az Értesítések párbeszédpanelen kísérheti figyelemmel." + "A fájlt nem lehet fogadni." + "A fájl fogadása \"%1$s\" feladótól leállítva" + "Fájl küldése a következőnek: \"%1$s\"" + "%1$s fájl küldése a következőnek: \"%2$s\"" + "A fájl küldése \"%1$s\" címzettnek leállítva" + "Nincs elég hely az USB-háttértáron a fájl mentéséhez." + "Nincs elég hely az SD-kártyán a fájl mentéséhez." + "Szükséges tárterület: %1$s" + "Túl sok kérés áll feldolgozás alatt. Próbálja újra később." + "A fájlátvitel még nem kezdődött el." + "A fájlátvitel folyamatban van." + "A fájlátvitel sikeresen befejeződött." + "A tartalom nem támogatott." + "A céleszköz letiltotta az átvitelt." + "A felhasználó megszakította." + "Tárolási probléma." + "Nem található USB-háttértár." + "Nincs SD-kártya. A küldött fájlok mentéséhez helyezzen be SD-kártyát." + "A kapcsolódás sikertelen." + "A kérést nem lehet megfelelően kezelni." + "Ismeretlen hiba." + "Fogadás Bluetooth-on keresztül" + "Bluetooth-megosztás" + "%1$s A fogadás kész." + "%1$s A küldés kész.." + "Bejövő átvitelek" + "Kimenő átvitelek" + "Az átvitelek előzménye üres." + "Minden elemet töröl a listáról." + "Bluetooth-megosztás: elküldött fájlok" + "Bluetooth-megosztás: fogadott fájlok" + + %1$d sikertelen. + %1$d sikertelen. + + + %1$d sikeres, %2$s + %1$d sikeres, %2$s + + "Lista törlése" + "Megnyitás" + "Törlés a listáról" + "Törlés" + "Now Playing" + "Mentés" + "Mégse" + "Válassza ki a Bluetooth használatával megosztani kívánt fiókokat. Kapcsolódásnál el kell fogadnia a fiókokhoz való hozzáférést is." + "Szabadon maradt helyek száma:" + "Alkalmazás ikonja" + "Bluetooth-kapcsolaton keresztüli üzenetmegosztás beállításai" + "A fiók kiválasztása sikertelen. 0 hely maradt" + "Bluetooth audió csatlakoztatva" + "Bluetooth audió leválasztva" + "Bluetooth audió" + "A 4 GB-nál nagyobb fájlokat nem lehet átvinni" + "Csatlakozás Bluetooth-eszközhöz" + diff --git a/android/app/res/values-hu/strings_pbap.xml b/android/app/res/values-hu/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..e96811614ff19f72a47a15d885ec2975f2a13557 --- /dev/null +++ b/android/app/res/values-hu/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Írja be a munkamenetkulcsot a(z) %1$s eszközhöz" + "Meg kell adni egy Bluetooth-munkamenetkulcsot" + "Időtúllépés történt a(z) %1$s eszközhöz történő kapcsolódás során" + "Időtúllépés történt a(z) %1$s munkamenetkulcsának bevitele során" + "Obex azonosítási kérelem" + "Munkamenetkulcs" + "Írja be a munkamenetkulcsot a(z) %1$s eszközhöz" + "Autós készlet" + "Ismeretlen név" + "Telefon neve" + "000000" + "Bluetooth-névjegymegosztás" + diff --git a/android/app/res/values-hu/strings_pbap_client.xml b/android/app/res/values-hu/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-hu/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-hu/strings_sap.xml b/android/app/res/values-hu/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..bd9728f8bfe69a61eb0ae5ea8bb3c9c67027a1fe --- /dev/null +++ b/android/app/res/values-hu/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM-elérés" + "Bluetooth SIM-elérés" + "Ügyfél felkérése szétválasztásra?" + "Várakozás az ügyfél szétválasztására" + "Szétválasztás" + "Szétválasztás kényszerítése" + diff --git a/android/app/res/values-hu/test_strings.xml b/android/app/res/values-hu/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..2802ef4fc57391f449ceae464b59c86be1b4283a --- /dev/null +++ b/android/app/res/values-hu/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Rekord beszúrása" + "Rekord megerősítése" + "ACK rekord" + "Minden rekord törlése" + "OK" + "Rekord törlése" + "TCP-szerver indítása" + "TCP-szerver értesítése" + diff --git a/android/app/res/values-hy/config.xml b/android/app/res/values-hy/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-hy/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-hy/strings.xml b/android/app/res/values-hy/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c91e4da11c1b5d2d9e61f59e9bf0be9ec5abcd14 --- /dev/null +++ b/android/app/res/values-hy/strings.xml @@ -0,0 +1,137 @@ + + + + + "Օգտվել ներբեռնման կառավարչից:" + "Թույլ է տալիս, որ ծրագիրը մատչի BluetoothShare կառավարչին և այն օգտագործի ֆայլեր փոխանցելու համար:" + "Թույլատրված Bluetooth սարքի հասանելիություն։" + "Թույլատրում է հավելվածին ժամանակավորապես ավելացնել Bluetooth սարքը սպիտակ ցուցակում, ինչը հնարավորություն է տալիս, որ Bluetooth սարքը ֆայլեր ուղարկի այս սարքին՝ առանց օգտատիրոջ հաստատման։" + "Bluetooth" + "Անհայտ սարք" + "Անհայտ" + "Ավիառեժիմ" + "Դուք չեք կարող օգտվել Bluetooth-ից Ավիառեժիմում:" + + "Bluetooth ծառայություններից օգտվելու համար նախ պետք է միացնեք Bluetooth-ը:" + "Միացնե՞լ Bluetooth-ը հիմա:" + "Չեղարկել" + "Միացնել" + "Ֆայլերի փոխանցում" + "Ընդունե՞լ մուտքային ֆայլը:" + "Մերժել" + "Ընդունել" + "Եղավ" + %1$s»-ից մուտքային ֆայլի ընդունման ժամանակը սպառվեց" + "Մուտքային ֆայլ" + "%1$s-ը պատրաստ է ուղարկելու %2$s ֆայլը" + "Bluetooth համօգտագործում՝ %1$s-ը ստացվում է" + "Bluetooth համօգտագործում՝ ստացվեց %1$s-ը" + "Bluetooth համօգտագործում՝ %1$s ֆայլը չի ստացվել" + "Bluetooth համօգտագործում՝ ուղարկվում է %1$s-ը" + "Bluetooth համօգտագործում՝ %1$s-ն ուղարկված է" + "100% ավարտուն" + "Bluetooth համօգտագործում՝ %1$s ֆայլը չի ուղարկվել" + "Ֆայլերի փոխանցում" + "Ումից՝ «%1$s»" + "Ֆայլ՝ %1$s" + "Ֆայլի չափը՝ %1$s" + + "Ֆայլի ստացում..." + "Դադարեցնել" + "Թաքցնել" + "Ումից" + "Ֆայլի անունը" + "Չափը" + "Ֆայլը չհաջողվեց ստանալ" + "Ֆայլ՝ %1$s" + "Պատճառը՝ %1$s" + "Եղավ" + "Ֆայլը ստացվել է" + "Բացել" + "Ում՝ «%1$s»" + "Ֆայլի տեսակը՝ %1$s (%2$s)" + "Ֆայլն ուղարկվում է..." + "Ֆայլն ուղարկված է" + "Հաստատել" + "Ֆայլը չի ուղարկվել «%1$s»-ին:" + "Ֆայլ՝ %1$s" + "Փակել" + "Եղավ" + "Անհայտ ֆայլ" + "Ֆայլի այս տեսակի համար գործող ծրագիր չկա: \n" + "Ֆայլեր չկան" + "Ֆայլը գոյություն չունի: \n" + "Խնդրում ենք սպասել..." + "Bluetooth-ը միանում է..." + "Դուք կստանաք ֆայլը: Առաջընթացի մասին տեղեկացեք Ծանուցումների վահանակից:" + "Հնարավոր չէ ստանալ ֆայլը:" + "Դադարեցվեց «%1$s»-ից ֆայլի ստացումը" + "Ֆայլն ուղարկվում է «%1$s»-ին" + "%1$s ֆայլեր ուղարկվում են «%2$s»-ին" + "Դադարեցվեց ֆայլի ուղարկումը «%1$s»-ին" + "USB կրիչում բավարար տարածք չկա ֆայլը պահելու համար" + "SD քարտում բավարար տարածք չկա ֆայլը պահելու համար" + "Անհրաժեշտ տեղը՝ %1$s" + "Չափից շատ հարցումներ են մշակվում: Կրկին փորձեք ավելի ուշ:" + "Ֆայլի փոխանցումը դեռ չի մեկնարկել:" + "Ֆայլի փոխանցումն ընթացքի մեջ է:" + "Ֆայլերի փոխանցումը հաջողությամբ ավարտվել է:" + "Բովանդակությունը չի աջակցվում:" + "Փոխանցումն արգելված է նպատակային սարքի կողմից:" + "Փոխանցումը չեղարկվել է օգտատիրոջ կողմից:" + "Կրիչի խնդիր:" + "USB կրիչ չկա:" + "SD քարտ չկա: Տեղադրեք SD քարտ` փոխանցված ֆայլերը պահելու համար:" + "Միացումը անհաջող էր:" + "Հարցումը հնարավոր չէ ճշգրտորեն մշակել:" + "Անհայտ սխալ:" + "Bluetooth-ով ստացված" + "Bluetooth համօգտագործում" + "%1$s ստացումն ավարտված է:" + "%1$s ուղարկումն ավարտված է:" + "Մուտքային փոխանցումներ" + "Ելքային փոխանցումներ" + "Փոխանցումների պատմությունը դատարկ է:" + "Ցուցակի բոլոր տվյալները կջնջվեն:" + "Bluetooth համօգտագործում՝ ֆայլերն ուղարկված են" + "Bluetooth համօգտագործում՝ ֆայլերը ստացված են" + + %1$d չհաջողված: + %1$d չհաջողված: + + + %1$d հաջողված, %2$s + %1$d հաջողված, %2$s + + "Ջնջել ցուցակը" + "Բաց" + "Ջնջել ցուցակից" + "Ջնջել" + "Այժմ հնչում է" + "Պահել" + "Չեղարկել" + "Ընտրեք հաշիվները, որոնք ցանկանում եք հասանելի դարձնել Bluetooth-ի միջոցով: Ամեն դեպքում, կապակցվելիս պետք է ընդունեք հաշիվներն օգտագործելու թույլտվությունը:" + "Մնացած տողերի քանակը՝" + "Հավելվածի պատկերակը" + "Հաղորդագրությունների համօգտագործման Bluetooth-ի կարգավորումներ" + "Հնարավոր չէ ընտրել հաշիվը: Տող չի մնացել" + "Bluetooth աուդիոն կապակցված է" + "Bluetooth աուդիոն անջատված է" + "Bluetooth աուդիո" + "4 ԳԲ-ից մեծ ֆայլերը հնարավոր չէ փոխանցել" + "Միանալ Bluetooth-ին" + diff --git a/android/app/res/values-hy/strings_pbap.xml b/android/app/res/values-hy/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..cdd2f9ce6db0381ed37d5c1e4b993fc65ee6d4ef --- /dev/null +++ b/android/app/res/values-hy/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Մուտքագրեք աշխատաշրջանի բանալին %1$s-ի համար" + "Պահանջվում է Bluetooth-ի աշխատաշրջանի բանալի" + "«%1$s»-ի հետ կապի ընդունման ժամանակը սպառվեց" + "%1$s-ով աշխատաշրջանի բանալու մուտքագրման ժամանակը սպառվեց" + "Կասեցնել նույնականացման հարցումը" + "Աշխատաշրջանի բանալի" + "Մուտքագրեք աշխատաշրջանի բանալի %1$s-ի համար" + "Carkit" + "Անհայտ անուն" + "Իմ անունը" + "000000" + "Bluetooth կոնտակտների համօգտագործում" + diff --git a/android/app/res/values-hy/strings_pbap_client.xml b/android/app/res/values-hy/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-hy/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-hy/strings_sap.xml b/android/app/res/values-hy/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..57618f3c8e3cdd44546f4341c7cc39a2fd291370 --- /dev/null +++ b/android/app/res/values-hy/strings_sap.xml @@ -0,0 +1,10 @@ + + + "SIM քարտի օգտագործում Bluetooth-ի միջոցով" + "SIM քարտի օգտագործում Bluetooth-ի միջոցով" + "Խնդրե՞լ սպասառուին անջատել:" + "Սպասառուի անջատման սպասում" + "Անջատել" + "Հարկադրաբար անջատել" + diff --git a/android/app/res/values-hy/test_strings.xml b/android/app/res/values-hy/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..33693e47ee857d1330dfb1ff6fbeccd48116e435 --- /dev/null +++ b/android/app/res/values-hy/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Կատարեք գրառում" + "Հաստատել գրառումը" + "ACK գրառում" + "Ջնջել բոլոր գրառումները" + "Եղավ" + "Ջնջել գրառումը" + "Մեկնարկել TCP սերվերը" + "Ծանուցել TCP սերվերին" + diff --git a/android/app/res/values-in/config.xml b/android/app/res/values-in/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-in/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-in/strings.xml b/android/app/res/values-in/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3652946fa56c8a214792211cc56a3b6e227d4ec3 --- /dev/null +++ b/android/app/res/values-in/strings.xml @@ -0,0 +1,137 @@ + + + + + "Akses pengelola download." + "Izinkan apl mengakses pengelola BluetoothShare dan menggunakannya untuk mentransfer file." + "Memasukkan akses perangkat Bluetooth ke daftar yang diizinkan." + "Mengizinkan aplikasi memasukkan perangkat Bluetooth ke daftar yang diizinkan untuk sementara waktu, yang memungkinkan perangkat tersebut mengirimkan file ke perangkat ini tanpa konfirmasi pengguna." + "Bluetooth" + "Perangkat tidak diketahui" + "Tidak diketahui" + "Mode pesawat" + "Anda tidak dapat menggunakan Bluetooth dalam mode Pesawat." + + "Kepada menggunakan Layanan Bluetooth, Anda harus menghidupkan Bluetooth terlebih dulu." + "Hidupkan Bluetooth sekarang?" + "Batal" + "Hidupkan" + "Transfer file" + "Terima file masuk?" + "Tolak" + "Terima" + "Oke" + "Terjadi waktu tunggu saat menerima file masuk dari \"%1$s\"" + "File masuk" + "%1$s siap mengirim %2$s" + "Berbagi Bluetooth: Menerima %1$s" + "Berbagi Bluetooth: Telah menerima %1$s" + "Berbagi Bluetooth: File %1$s tidak diterima" + "Berbagi Bluetooth: Mengirim %1$s" + "Berbagi Bluetooth: Telah mengirim %1$s" + "100% selesai" + "Berbagi Bluetooth: File %1$s tidak terkirim" + "Transfer file" + "Dari: \"%1$s\"" + "File: %1$s" + "Ukuran file: %1$s" + + "Menerima file…" + "Berhenti" + "Sembunyikan" + "Dari" + "Nama file" + "Ukuran" + "File tidak diterima" + "File: %1$s" + "Alasan: %1$s" + "Oke" + "File diterima" + "Buka" + "Kepada: \"%1$s\"" + "Jenis file: %1$s (%2$s)" + "Mengirim file…" + "File terkirim" + "Oke" + "File tidak terkirim ke \"%1$s\"." + "File: %1$s" + "Tutup" + "Oke" + "File tidak diketahui" + "Tidak ada apl untuk menangani file jenis ini. \n" + "Tidak ada file" + "File tidak ada. \n" + "Harap tunggu…" + "Menghidupkan Bluetooth..." + "File akan diterima. Periksa kemajuan dalam panel Notifikasi." + "File tidak dapat diterima." + "Berhenti menerima file dari \"%1$s\"" + "Mengirim file ke \"%1$s\"" + "Mengirim %1$s file ke \"%2$s\"" + "Berhenti mengirim file ke \"%1$s\"" + "Tidak ada ruang yang cukup di penyimpanan USB untuk menyimpan file." + "Tidak ada ruang yang cukup di kartu SD untuk menyimpan file." + "Ruang yang diperlukan: %1$s" + "Terlalu banyak permintaan yang diproses. Coba lagi nanti." + "Transfer file belum dimulai." + "Transfer file sedang berlangsung." + "Transfer file berhasil diselesaikan." + "Konten tidak didukung." + "Transfer dilarang oleh perangkat target." + "Transfer dibatalkan oleh pengguna." + "Masalah penyimpanan." + "Tidak ada penyimpanan USB." + "Tidak ada kartu SD. Masukkan kartu SD untuk menyimpan file yang ditransfer." + "Sambungan tidak berhasil." + "Permintaan tidak dapat ditangani dengan semestinya." + "Kesalahan tidak dikenal." + "Bluetooth diterima" + "Berbagi Bluetooth" + "%1$s Telah selesai diterima." + "%1$s Telah selesai dikirim." + "Transfer masuk" + "Transfer keluar" + "Histori transfer kosong." + "Semua item akan dihapus dari daftar." + "Berbagi bluetooth: Telah mengirimkan file" + "Berbagi Bluetooth: File yang diterima" + + %1$d gagal. + %1$d gagal. + + + %1$d berhasil, %2$s + %1$d berhasil, %2$s + + "Hapus daftar" + "Buka" + "Hapus dari daftar" + "Hapus" + "Now Playing" + "Simpan" + "Batal" + "Pilih akun yang ingin Anda bagikan melalui Bluetooth. Anda masih harus menerima akses apa pun ke akun saat menghubungkan." + "Sisa Slot:" + "Ikon Aplikasi" + "Setelan Berbagi Pesan Bluetooth" + "Tidak dapat memilih akun. Sisa 0 slot" + "Bluetooth audio terhubung" + "Bluetooth audio terputus" + "Bluetooth Audio" + "File yang berukuran lebih dari 4GB tidak dapat ditransfer" + "Hubungkan ke Bluetooth" + diff --git a/android/app/res/values-in/strings_pbap.xml b/android/app/res/values-in/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..47bdc224ff5411c627939df9c278975c528f1a4f --- /dev/null +++ b/android/app/res/values-in/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Ketikkan kunci sesi untuk %1$s" + "Kunci sesi bluetooth diperlukan" + "Waktu habis untuk menerima sambungan dengan %1$s" + "Waktu habis untuk memasukkan kunci sesi masuk dengan %1$s" + "Permintaan autentikasi Obex" + "Tombol Sesi" + "Ketikkan kunci sesi untuk %1$s" + "Carkit" + "Nama tidak diketahui" + "Nama saya" + "000000" + "Berbagi Kontak Bluetooth" + diff --git a/android/app/res/values-in/strings_pbap_client.xml b/android/app/res/values-in/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-in/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-in/strings_sap.xml b/android/app/res/values-in/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..94211dfa79efc55bf3d750f9943e4138849246ad --- /dev/null +++ b/android/app/res/values-in/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Akses SIM Bluetooth" + "Akses SIM Bluetooth" + "Minta klien untuk memutus sambungan?" + "Menunggu klien untuk memutus sambungan" + "Putuskan sambungan" + "Putuskan paksa sambungan" + diff --git a/android/app/res/values-in/test_strings.xml b/android/app/res/values-in/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..38236c2a5f7d4f4c8aecb30b945fb984eb6f7aa0 --- /dev/null +++ b/android/app/res/values-in/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Masukkan catatan" + "Konfirmasi catatan" + "Akui catatan" + "Hapus semua catatan" + "Oke" + "Hapus catatan" + "Mulai server TCP" + "Beri tahu server TCP" + diff --git a/android/app/res/values-is/config.xml b/android/app/res/values-is/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-is/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-is/strings.xml b/android/app/res/values-is/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e61e25f0bfcfb7e9e3b72655b806b0aaeb112cf7 --- /dev/null +++ b/android/app/res/values-is/strings.xml @@ -0,0 +1,137 @@ + + + + + "Aðgangur að niðurhalsstjórnun." + "Leyfir forriti að fá aðgang að BluetoothShare-stjórnun og nota hana til að flytja skrár." + "Setja aðgang Bluetooth-tækis á hvítan lista." + "Leyfir forritinu að setja Bluetooth-tæki tímabundið á hvítan lista og gera því þannig kleift að senda skrár í þetta tæki án staðfestingar frá notanda." + "Bluetooth" + "Óþekkt tæki" + "Óþekkt" + "Flugstilling" + "Þú getur ekki notað Bluetooth í flugstillingu." + + "Til að nota Bluetooth-þjónustu þarftu fyrst að kveikja á Bluetooth." + "Viltu kveikja á Bluetooth núna?" + "Hætta við" + "Kveikja" + "Skráaflutningur" + "Samþykkja skrá sem berst?" + "Hafna" + "Samþykkja" + "Í lagi" + "Rann út á tíma við að samþykkja skrá sem „%1$s“ sendi" + "Skrá á innleið" + "%1$s er tilbúin(n) að senda %2$s" + "Bluetooth-deiling: Tekur á móti %1$s" + "Bluetooth-deiling: Skráin %1$s var móttekin" + "Bluetooth-deiling: Skráin %1$s var ekki móttekin" + "Bluetooth-deiling: Sendir %1$s" + "Bluetooth-deiling: Skráin %1$s var send" + "100% lokið" + "Bluetooth-deiling: Skráin %1$s var ekki send" + "Skráaflutningur" + "Frá: „%1$s“" + "Skrá: %1$s" + "Skráarstærð: %1$s" + + "Tekur á móti skrá…" + "Stöðva" + "Fela" + "Frá" + "Skráarheiti" + "Stærð" + "Skrá ekki móttekin" + "Skrá: %1$s" + "Ástæða: %1$s" + "Í lagi" + "Skrá móttekin" + "Opna" + "Til: „%1$s“" + "Skráargerð: %1$s (%2$s)" + "Sendir skrá…" + "Skrá send" + "Í lagi" + "„%1$s“ fékk skrána ekki senda." + "Skrá: %1$s" + "Loka" + "Í lagi" + "Óþekkt skrá" + "Ekkert forrit til að meðhöndla þessa skráargerð fyrir hendi. \n" + "Engin skrá" + "Skráin er ekki til. \n" + "Augnablik…" + "Kveikir á Bluetooth…" + "Tekið verður á móti skránni. Fylgstu með framvindunni í tilkynningaglugganum." + "Ekki er hægt að taka á móti skránni." + "Móttaka skráar sem „%1$s“ sendi stöðvuð" + "Sendir skrá til „%1$s“" + "Sendir %1$s skrár til „%2$s“" + "Sending skráar til „%1$s“ stöðvuð" + "Ekki er nóg pláss í USB-geymslu til að vista skrána." + "Ekki er nóg pláss á SD-kortinu til að vista skrána." + "Pláss sem þarf: %1$s" + "Of margir beiðnir eru í vinnslu. Reyndu aftur síðar." + "Skráaflutningur er enn ekki hafinn." + "Skráaflutningur er í gangi." + "Skráaflutningi er lokið." + "Efnið er ekki stutt." + "Viðtökutækið bannaði flutninginn." + "Notandi hætti við flutninginn." + "Vandamál með geymslu." + "Engin USB-geymsla." + "Ekkert SD-kort. Settu SD-kort í til að vista fluttar skrár." + "Tenging mistókst." + "Ekki er hægt að afgreiða beiðnina á réttan hátt." + "Óþekkt villa." + "Móttekið um Bluetooth" + "Bluetooth-deiling" + "Móttöku %1$s lokið." + "Sendingu %1$s lokið." + "Flutningur á innleið" + "Flutningur á útleið" + "Flutningsferillinn er auður." + "Öll atriði verða hreinsuð af listanum." + "Bluetooth-deiling: Sendar skrár" + "Bluetooth-deiling: Mótteknar skrár" + + %1$d mistókst. + %1$d mistókust. + + + %1$d tókst, %2$s + %1$d tókust, %2$s + + "Hreinsa lista" + "Opna" + "Hreinsa af lista" + "Hreinsa" + "Í spilun" + "Vista" + "Hætta við" + "Veldu reikningana sem þú vilt deila í gegnum Bluetooth. Þú þarft samt að samþykkja allan aðgang að reikningunum við tengingu." + "Hólf eftir:" + "Forritstákn" + "Deilingarstillingar Bluetooth-skilaboða" + "Ekki er hægt að velja reikning. Engin hólf eftir" + "Bluetooth-hljóð tengt" + "Bluetooth-hljóð aftengt" + "Bluetooth-hljóð" + "Ekki er hægt að flytja skrár sem eru stærri en 4 GB" + "Tengjast við Bluetooth" + diff --git a/android/app/res/values-is/strings_pbap.xml b/android/app/res/values-is/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..45b9f05a2303ee733c3ebe3c00f98ddef7beab90 --- /dev/null +++ b/android/app/res/values-is/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Sláðu inn lotulykil fyrir %1$s" + "Bluetooth-lotulykils krafist" + "Rann út á tíma við að samþykkja tengingu við %1$s" + "Rann út á tíma við að færa inn lotulykil fyrir %1$s" + "Obex-sannvottunarbeiðni" + "Lotulykill" + "Sláðu inn lotulykil fyrir %1$s" + "Bílabúnaður" + "Óþekkt heiti" + "Nafnið mitt" + "000000" + "Tengiliðum deilt með Bluetooth" + diff --git a/android/app/res/values-is/strings_pbap_client.xml b/android/app/res/values-is/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-is/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-is/strings_sap.xml b/android/app/res/values-is/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..8dbc2d4cd8c0d7c36a6f064309289b94daeb34a3 --- /dev/null +++ b/android/app/res/values-is/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth-aðgangur að SIM-korti" + "Bluetooth-aðgangur að SIM-korti" + "Biðja biðlara um að aftengjast?" + "Bíður eftir að biðlari aftengist" + "Aftengja" + "Þvinga aftengingu" + diff --git a/android/app/res/values-is/test_strings.xml b/android/app/res/values-is/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..68fbc9c423897efb9a9c4788c7670c39801457b0 --- /dev/null +++ b/android/app/res/values-is/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Setja inn færslu" + "Staðfesta færslu" + "Ack-færsla" + "Eyða öllum færslum" + "Í lagi" + "Eyða færslu" + "Ræsa TCP-þjón" + "Tilkynna TCP-þjóni" + diff --git a/android/app/res/values-it/config.xml b/android/app/res/values-it/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-it/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-it/strings.xml b/android/app/res/values-it/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..4114266158a63042727173b7398b6a9cfaf97487 --- /dev/null +++ b/android/app/res/values-it/strings.xml @@ -0,0 +1,137 @@ + + + + + "Accedere alla gestione dei download." + "Consente all\'applicazione di accedere al gestore BluetoothShare e di utilizzarlo per trasferire file." + "Aggiunta dell\'accesso del dispositivo Bluetooth alla lista consentita." + "Consente all\'app di aggiungere temporaneamente un dispositivo Bluetooth alla lista consentita, permettendo a tale dispositivo di inviare file a questo dispositivo senza la conferma dell\'utente." + "Bluetooth" + "Dispositivo sconosciuto" + "Sconosciuto" + "Modalità aereo" + "Non puoi utilizzare Bluetooth in modalità aereo." + + "Per utilizzare i servizi Bluetooth, prima è necessario attivare Bluetooth." + "Attivare Bluetooth adesso?" + "Annulla" + "Attiva" + "Trasferimento file" + "Accettare il file in arrivo?" + "Rifiuta" + "Accetta" + "OK" + "Si è verificato un timeout durante l\'accettazione di un file in arrivo da \"%1$s\"" + "File in arrivo" + "%1$s è pronto per inviare %2$s" + "Bluetooth: ricezione %1$s" + "Bluetooth: %1$s ricevuto" + "Bluetooth: file %1$s non ricevuto" + "Bluetooth: invio %1$s" + "Bluetooth: %1$s inviato" + "100% completato" + "Bluetooth: file %1$s non inviato" + "Trasferimento file" + "Da: \"%1$s\"" + "File: %1$s" + "Dim. file: %1$s" + + "Ricezione file…" + "Interrompi" + "Nascondi" + "Da" + "Nome file" + "Dimensioni" + "File non ricevuto" + "File: %1$s" + "Motivo: %1$s" + "OK" + "File ricevuto" + "Apri" + "A: \"%1$s\"" + "Tipo di file: %1$s (%2$s)" + "Invio file…" + "File inviato" + "OK" + "Impossibile inviare il file a \"%1$s\"." + "File: %1$s" + "Chiudi" + "OK" + "File sconosciuto" + "Non sono disponibili applicazioni in grado di gestire questo tipo di file. \n" + "Nessun file" + "Il file non esiste. \n" + "Attendi…" + "Attivazione Bluetooth…" + "Il file verrà ricevuto. Controlla l\'avanzamento nel pannello Notifiche." + "Impossibile ricevere il file." + "Interruzione della ricezione del file da \"%1$s\"" + "Invio del file a \"%1$s\"" + "Invio di %1$s file a \"%2$s\"" + "Invio del file a \"%1$s\" interrotto" + "Spazio nell\'archivio USB insufficiente per salvare il file." + "Spazio sulla scheda SD insufficiente per salvare il file." + "Spazio necessario: %1$s" + "Troppe richieste in fase di elaborazione. Riprova più tardi." + "Trasferimento file non ancora iniziato." + "Trasferimento file" + "Trasferimento file completato." + "Contenuti non supportati." + "Il dispositivo di destinazione non consente il trasferimento." + "Trasferimento annullato dall\'utente." + "Problema di memorizzazione." + "Nessun archivio USB." + "Nessuna scheda SD. Inserisci una scheda SD per salvare i file trasferiti." + "Connessione non riuscita." + "Impossibile gestire correttamente la richiesta." + "Errore sconosciuto." + "Ricevuti tramite Bluetooth" + "Condivisione Bluetooth" + "Ricezione completata (%1$s)" + "Invio completato (%1$s)" + "Trasferimenti in entrata" + "Trasferimenti in uscita" + "La cronologia dei trasferimenti è vuota." + "Tutti gli elementi verranno cancellati dall\'elenco." + "Bluetooth: file inviati" + "Bluetooth: file ricevuti" + + Operazioni non riuscite: %1$d. + Operazione non riuscita: %1$d. + + + Operazioni riuscite: %1$d, %2$s + Operazione riuscita: %1$d, %2$s + + "Cancella elenco" + "Apri" + "Cancella da elenco" + "Cancella" + "Now Playing" + "Salva" + "Annulla" + "Seleziona gli account che desideri condividere tramite Bluetooth. Devi comunque accettare tutti gli accessi agli account durante la connessione." + "Slot rimanenti:" + "Icona applicazione" + "Impostazioni di condivisione dei messaggi Bluetooth" + "Impossibile selezionare l\'account. Nessuno slot rimanente" + "Audio Bluetooth connesso" + "Audio Bluetooth disconnesso" + "Audio Bluetooth" + "Impossibile trasferire file con dimensioni superiori a 4 GB" + "Connettiti a Bluetooth" + diff --git a/android/app/res/values-it/strings_pbap.xml b/android/app/res/values-it/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..d8d387aebab156c0a03d81af648698f4591c1f53 --- /dev/null +++ b/android/app/res/values-it/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Digita la chiave di sessione per %1$s" + "Chiave sessione Bluetooth necessaria" + "Timeout dell\'accettazione della connessione con %1$s" + "Timeout dell\'ingresso della chiave di sessione con %1$s" + "Richiesta di autenticazione Obex" + "Chiave sessione" + "Digita la chiave di sessione per %1$s" + "Kit auto" + "Nome sconosciuto" + "Il mio nome" + "000000" + "Condivisione dei contatti tramite Bluetooth" + diff --git a/android/app/res/values-it/strings_pbap_client.xml b/android/app/res/values-it/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-it/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-it/strings_sap.xml b/android/app/res/values-it/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..62566152404ea2a4cc73e8b2e4d18bbfcfccab35 --- /dev/null +++ b/android/app/res/values-it/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Accesso SIM Bluetooth" + "Accesso SIM Bluetooth" + "Richiedere al client di disconnettersi?" + "In attesa della disconnessione del client" + "Disconnetti" + "Forza disconnessione" + diff --git a/android/app/res/values-it/test_strings.xml b/android/app/res/values-it/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e4a17f37d2e8127c20d49009cd637892ed4d0893 --- /dev/null +++ b/android/app/res/values-it/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Inserisci record" + "Conferma record" + "Record ACK" + "Elimina tutti i record" + "OK" + "Elimina record" + "Avvia server TCP" + "Invia notifica al server TCP" + diff --git a/android/app/res/values-iw/config.xml b/android/app/res/values-iw/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-iw/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-iw/strings.xml b/android/app/res/values-iw/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..26bf8cc7c5e541f55cbe0b599a24a60ffef82cf2 --- /dev/null +++ b/android/app/res/values-iw/strings.xml @@ -0,0 +1,141 @@ + + + + + "גישה למנהל ההורדות." + "‏מאפשר לאפליקציה לגשת למנהל BluetoothShare ולהשתמש בו להעברת קבצים." + "‏גישה לרשימת ההיתרים של מכשיר Bluetooth." + "‏התכונה הזו מאפשרת לאפליקציה להוסיף מכשיר Bluetooth לרשימת ההיתרים באופן זמני, ובכך לאפשר לאותו מכשיר לשלוח קבצים למכשיר זה ללא אישור משתמש." + "Bluetooth" + "מכשיר לא ידוע" + "לא ידוע" + "מצב טיסה" + "‏לא ניתן להשתמש ב-Bluetooth במצב טיסה." + + "‏כדי להשתמש בשירותי Bluetooth, עליך להפעיל תחילה את Bluetooth." + "‏להפעיל Bluetooth כעת?" + "ביטול" + "הפעלה" + "העברת קבצים" + "האם לקבל את הקובץ הנכנס?" + "דחייה" + "קבלה" + "אישור" + "תם הזמן הקצוב לקבלת קובץ נכנס מאת \"%1$s\"" + "קובץ שהתקבל" + "%1$s מוכן לשלוח את %2$s" + "‏שיתוף Bluetooth: ‏%1$s מתקבל" + "‏שיתוף Bluetooth‏: התקבל %1$s" + "‏שיתוף Bluetooth: הקובץ %1$s לא התקבל" + "‏שיתוף Bluetooth: שליחת %1$s מתבצעת" + "‏שיתוף Bluetooth‏: נשלח %1$s" + "‏הושלם ב-‎100%‎" + "‏שיתוף Bluetooth: הקובץ %1$s לא נשלח" + "העברת קבצים" + "מאת: \"%1$s\"" + "קובץ: %1$s" + "גודל קובץ: %1$s" + + "הקובץ מתקבל…" + "עצירה" + "הסתרה" + "מ-" + "שם קובץ" + "גודל" + "הקובץ לא התקבל" + "קובץ: %1$s" + "סיבה: %1$s" + "אישור" + "הקובץ התקבל" + "פתיחה" + "אל: \"%1$s\"" + "סוג קובץ: %1$s (%2$s)" + "שליחת קובץ מתבצעת…" + "הקובץ נשלח" + "אישור" + "הקובץ לא נשלח אל %1$s." + "קובץ: %1$s" + "סגירה" + "אישור" + "קובץ לא ידוע" + "אין אפליקציה המתאימה לטיפול בקבצים מסוג זה. \n" + "אין קובץ" + "הקובץ לא קיים. \n" + "יש להמתין..." + "‏הפעלת Bluetooth מתבצעת…" + "הקובץ יתקבל. יש לבדוק את ההתקדמות בלוח ההתראות." + "לא ניתן לקבל את הקובץ." + "הופסקה קבלת קובץ מאת \"%1$s\"" + "שליחת קובץ אל \"%1$s\" מתבצעת" + "שליחת %1$s קבצים אל \"%2$s\" מתבצעת" + "שליחת קובץ אל \"%1$s\" הופסקה" + "‏אין מספיק שטח באחסון ה-USB כדי לשמור את הקובץ." + "‏אין מספיק שטח אחסון בכרטיס ה-SD כדי לשמור את הקובץ." + "שטח דרוש: %1$s" + "בקשות רבות מדי הועברו לעיבוד. יש לנסות שוב מאוחר יותר." + "העברת הקובץ עדיין לא החלה." + "מתבצעת העברת קובץ." + "העברת הקובץ הושלמה בהצלחה." + "התוכן אינו נתמך." + "מכשיר היעד לא התיר את ההעברה." + "ההעברה בוטלה על ידי המשתמש." + "בעיית אחסון." + "‏אין אחסון USB." + "‏אין כרטיס SD. יש להכניס כרטיס SD כדי לשמור קבצים שהועברו." + "החיבור נכשל." + "לא ניתן לטפל בבקשה כהלכה." + "שגיאה לא ידועה." + "‏התקבל באמצעות Bluetooth" + "‏שיתוף Bluetooth" + "%1$s שהתקבלו הושלמו." + "%1$s שנשלחו הושלמו." + "העברות נכנסות" + "העברות יוצאות" + "היסטוריית ההעברות ריקה." + "כל הפריטים ינוקו מהרשימה." + "‏שיתוף Bluetooth: נשלחו קבצים" + "‏שיתוף Bluetooth: התקבלו קבצים" + + %1$d לא הצליחו. + %1$d לא הצליחו. + %1$d לא הצליחו. + %1$d לא הצליח. + + + %1$d הצליחו, %2$s + %1$d הצליחו, %2$s + %1$d הצליחו, %2$s + %1$d הצליח, %2$s + + "ניקוי רשימה" + "פתיחה" + "ניקוי מהרשימה" + "ניקוי" + "מה שומעים עכשיו?" + "שמירה" + "ביטול" + "‏יש לבחור חשבונות שברצונך לשתף באמצעות Bluetooth. עדיין יהיה עליך לאשר גישה אל החשבונות בעת החיבור." + "מקומות נותרים:" + "סמל אפליקציה" + "‏הגדרות לשיתוף הודעות ב-Bluetooth" + "לא ניתן לבחור חשבון. נותרו 0 מקומות" + "‏אודיו Bluetooth מחובר" + "‏אודיו Bluetooth מנותק" + "‏אודיו Bluetooth" + "‏לא ניתן להעביר קבצים שגדולים מ-4GB" + "‏התחברות באמצעות Bluetooth" + diff --git a/android/app/res/values-iw/strings_pbap.xml b/android/app/res/values-iw/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..1e513503bc90523361fc7735a71a52da2d925dbc --- /dev/null +++ b/android/app/res/values-iw/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "‏יש להקליד קוד סשן עבור %1$s" + "‏דרוש קוד הפעלת Bluetooth" + "‏תם הזמן שהוקצב להסכמה לחיבור עם %1$s" + "‏תם הזמן הקצוב להזנת קוד הפעלה עם %1$s" + "‏בקשת אימות של Obex" + "קוד סשן" + "‏יש להקליד קוד סשן עבור %1$s" + "דיבורית לרכב" + "שם לא ידוע" + "השם שלי" + "000000" + "‏שיתוף אנשי קשר באמצעות Bluetooth" + diff --git a/android/app/res/values-iw/strings_pbap_client.xml b/android/app/res/values-iw/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-iw/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-iw/strings_sap.xml b/android/app/res/values-iw/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..3287d7af5d935ad879f9aa3fcf0761f739b647e1 --- /dev/null +++ b/android/app/res/values-iw/strings_sap.xml @@ -0,0 +1,10 @@ + + + "‏גישת SIM ל-Bluetooth" + "‏גישת SIM ל-Bluetooth" + "האם לבקש מלקוח להתנתק?" + "ממתין לניתוק לקוח" + "ניתוק" + "אילוץ ניתוק" + diff --git a/android/app/res/values-iw/test_strings.xml b/android/app/res/values-iw/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..4a5dd4f6bad8d93a53effa665dee444e2e3410c7 --- /dev/null +++ b/android/app/res/values-iw/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "הוספת רשומה" + "אישור רשומה" + "אישור רשומה" + "מחיקת כל הרשומות" + "אישור" + "מחיקת רשומה" + "‏הפעלת שרת TCP" + "‏שליחת התראה לשרת TCP" + diff --git a/android/app/res/values-ja/config.xml b/android/app/res/values-ja/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ja/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ja/strings.xml b/android/app/res/values-ja/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..5932c40c815651d7bf10e30df2c2681976d23242 --- /dev/null +++ b/android/app/res/values-ja/strings.xml @@ -0,0 +1,137 @@ + + + + + "ダウンロード マネージャーにアクセスします。" + "BluetoothShareマネージャーへのアクセスとそれを利用したファイル転送をアプリに許可します。" + "Bluetoothデバイスによるアクセスを許可します。" + "Bluetooth デバイスによるアクセスを一時的に許可して、ユーザーの確認を受けずにそのデバイスからこのデバイスにファイルを送信することをアプリに許可します。" + "Bluetooth" + "不明なモバイルデバイス" + "不明" + "機内モード" + "機内モードではBluetoothを使用できません。" + + "Bluetoothサービスを利用するには、まずBluetoothをONにしてください。" + "Bluetoothを今すぐONにしますか?" + "キャンセル" + "ONにする" + "ファイル転送" + "着信ファイルを受信しますか?" + "拒否" + "承諾" + "OK" + "「%1$s」からのファイル受信中に接続がタイムアウトしました" + "着信ファイル" + "%1$sさんが%2$sを送信できるようになりました" + "Bluetooth共有: %1$sを受信中" + "Bluetooth共有: %1$sを受信済み" + "Bluetooth共有: ファイル%1$sの受信に失敗" + "Bluetooth共有: %1$sを送信中" + "Bluetooth共有: %1$sを送信済み" + "100%完了" + "Bluetooth共有: ファイル%1$sの送信に失敗" + "ファイル転送" + "送信元: 「%1$s」" + "ファイル: %1$s" + "ファイルサイズ: %1$s" + + "ファイルを受信中..." + "停止" + "非表示" + "送信元" + "ファイル名" + "サイズ" + "ファイルの受信に失敗" + "ファイル: %1$s" + "理由: %1$s" + "OK" + "ファイルを受信しました" + "開く" + "送信先: 「%1$s」" + "ファイル形式: %1$s%2$s)" + "ファイルを送信中..." + "ファイルを送信しました" + "OK" + "ファイルは「%1$s」に送信されませんでした。" + "ファイル: %1$s" + "閉じる" + "OK" + "不明なファイル" + "この形式のファイルを処理するアプリがありません。\n" + "ファイルがありません" + "そのファイルは存在しません。\n" + "お待ちください..." + "BluetoothをONにしています..." + "ファイルを受信します。進行状況は[通知]パネルでご確認ください。" + "ファイルを受信できません。" + "「%1$s」からのファイルの受信を停止しました" + "「%1$s」にファイルを送信中" + "%1$s個のファイルを「%2$s」に送信中" + "「%1$s」へのファイルの送信を停止しました" + "USB ストレージにファイルを保存するのに十分な容量がありません。" + "SD カードにファイルを保存するのに十分な容量がありません。" + "必要な空き領域: %1$s" + "処理中のリクエストが多すぎるため、しばらくしてからもう一度お試しください。" + "ファイル転送はまだ開始されていません。" + "ファイルを転送中です。" + "ファイル転送が完了しました。" + "コンテンツはサポートされていません。" + "転送先のデバイスで転送が禁止されています。" + "ユーザーが転送をキャンセルしました。" + "ストレージのエラーです。" + "USB ストレージがありません。" + "SD カードが見つかりません。転送ファイルを保存する SD カードを挿入してください。" + "接続できませんでした。" + "リクエストを正しく処理できません。" + "不明なエラーです。" + "Bluetooth で受信したファイル" + "Bluetooth 共有" + "%1$sの受信が完了しました。" + "%1$sの送信が完了しました。" + "外部からの転送" + "外部への転送" + "転送履歴が空です。" + "すべてのアイテムがリストから消去されます。" + "Bluetooth共有: 送信したファイル" + "Bluetooth共有: 受信したファイル" + + 失敗: %1$d + 失敗: %1$d + + + 成功: %1$d 件(%2$s) + 成功: %1$d 件(%2$s) + + "リストを消去" + "開く" + "リストから消去" + "消去" + "この曲なに?" + "保存" + "キャンセル" + "Bluetooth を介して共有するアカウントを選択してください。接続中はアカウントへのアクセスをすべて承認する必要があります。" + "残りスロット数:" + "アプリアイコン" + "Bluetoothメッセージ共有の設定" + "アカウントを選択できません。残りスロット数が0です" + "Bluetooth オーディオは接続されています" + "Bluetooth オーディオは接続を解除されています" + "Bluetooth オーディオ" + "4 GB を超えるファイルは転送できません" + "Bluetooth に接続する" + diff --git a/android/app/res/values-ja/strings_pbap.xml b/android/app/res/values-ja/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..104c9da5d5b2a074d12ba0e1f5b1074402b92ff3 --- /dev/null +++ b/android/app/res/values-ja/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$sのセッションキーを入力" + "Bluetoothセッションキーが必要です" + "%1$sとの接続の承諾がタイムアウトになりました" + "%1$sでのセッションキーの入力がタイムアウトになりました" + "OBEX認証リクエスト" + "セッションキー" + "%1$sのセッションキーを入力してください" + "カーキット" + "不明な名前" + "名前" + "000000" + "Bluetooth での連絡先の共有" + diff --git a/android/app/res/values-ja/strings_pbap_client.xml b/android/app/res/values-ja/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ja/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ja/strings_sap.xml b/android/app/res/values-ja/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..a411f6fc8028ff70c9d97e4815032743c7947e65 --- /dev/null +++ b/android/app/res/values-ja/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIMアクセス" + "Bluetooth SIMアクセス" + "クライアントに接続解除をリクエストしますか?" + "クライアントの接続解除を待機しています" + "接続を解除" + "強制的に接続解除" + diff --git a/android/app/res/values-ja/test_strings.xml b/android/app/res/values-ja/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..5bd7d77d8de6137a06633768d84f2a711428d123 --- /dev/null +++ b/android/app/res/values-ja/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "記録を挿入" + "レコードを確認" + "Ackレコード" + "レコードをすべて削除" + "OK" + "レコードを削除" + "TCPサーバーを起動" + "TCPサーバーに通知" + diff --git a/android/app/res/values-ka/config.xml b/android/app/res/values-ka/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ka/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ka/strings.xml b/android/app/res/values-ka/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..78d27ecb7222a9315531995b69b1f531eeabe551 --- /dev/null +++ b/android/app/res/values-ka/strings.xml @@ -0,0 +1,137 @@ + + + + + "ჩამოტვირთვების მენეჯერზე წვდომა." + "ანიჭებს აპს BluetoothShare მენეჯერზე წვდომას და მისი გამოყენების უფლებას ფაილების გასაგზავნად." + "შეიყვანეთ Bluetooth მოწყობილობის წვდომა მისაღებ სიაში." + "საშუალებას აძლევს აპს, რომ დროებით მისაღებ სიაში შეიყვანოს Bluetooth მოწყობილობა, რაც ამ მოწყობილობას საშუალებას მისცემს, ფაილები ამ მოწყობილობაზე მომხმარებლის დასტურის გარეშე გამოგზავნოს." + "Bluetooth" + "უცნობი მოწყობილობა" + "უცნობი" + "თვითმფრინავის რეჟიმი" + "თვითმფრინავის რეჟიმში Bluetooth-ს ვერ გამოიყენებთ." + + "Bluetooth სერვისების გამოსაყენებლად ჯერ Bluetooth უნდა ჩართოთ." + "გსურთ Bluetooth-ის ახლა ჩართვა?" + "გაუქმება" + "ჩართვა" + "ფაილის ტრანსფერი" + "გსურთ შემომავალი ფაილის მიღება?" + "უარყოფა" + "მიღება" + "კარგი" + "„%1$s“-გან ფაილის შემომავალი ფაილის მიღების დრო ამოიწურა" + "შემომავალი ფაილი" + "%1$s მზად არის %2$s-ის გასაგზავნად" + "Bluetooth გაზიარება: %1$s ფაილის მიღება" + "Bluetooth გაზიარება: მიღებულია %1$s" + "Bluetooth გაზიარება: ფაილი %1$s არ მიღებულა" + "Bluetooth გაზიარება: %1$s-ის გაგზავნა" + "Bluetooth გაზიარება: გაიგზავნა %1$s" + "დასრულდა 100%" + "Bluetooth გაზიარება: ფაილი %1$s არ გაიგზავნა" + "ფაილის ტრანსფერი" + "გამგზავნი: „%1$s“" + "ფაილი: %1$s" + "ფაილის ზომა: %1$s" + + "მიმდინარეობს ფაილის მიღება…" + "შეწყვეტა" + "დამალვა" + "საიდან" + "ფაილის სახელი" + "ზომა" + "ფაილი არ მიღებულა" + "ფაილი: %1$s" + "მიზეზი: %1$s" + "კარგი" + "ფაილი მიღებულია" + "გახსნა" + "მიმღები: „%1$s“" + "ფაილის ტიპი: %1$s (%2$s)" + "ფაილი იგზავნება…" + "ფაილი გაიგზავნა" + "კარგი" + "ფაილი არ გაიგზავნა „%1$s“-თან." + "ფაილი: %1$s" + "დახურვა" + "კარგი" + "უცნობი ფაილი" + "არ არსებობს აპი, რომელიც ამ ფაილის ტიპის დაამუშავებს. \n" + "ფაილი არ არის" + "ფაილი არ არსებობს. \n" + "გთხოვთ, მოითმინოთ..." + "Bluetooth-ის ჩართვა…" + "ფაილი მიღებული იქნება. შეამოწმეთ პროგრესი შეტყობინებების დაფაზე." + "ფაილის მიღება ვერ ხერხდება." + "„%1$s“-დან ფაილის მიღება შეჩერდა" + "„%1$s“-თან ფაილის გაგზავნა" + "%1$s ფაილის „%2$s“-თან გაგზავნა" + "„%1$s“-თან ფაილის გაგზავნა შეჩერდა" + "ფაილის შესანახად USB მეხსიერებაში საკმარისი სივრცე არ არის." + "ფაილის შესანახად SD ბარათზე საკმარისი სივრცე არ არის." + "საჭირო სივრცე: %1$s" + "მუშავდება ძალიან ბევრი პროცესი. შეეცადეთ მოგვიანებით." + "ფაილის ტრანსფერი ჯერ არ დაწყებულა." + "მიმდინარეობს ფაილის ტრანსფერი." + "ფაილის ტრანსფერი წარმატებით დასრულდა." + "კონტენტი მხარდაჭერილი არ არის." + "ტრანსფერი აკრძალულია სამიზნე მოწყობილობის მიერ." + "ტრანსფერი გაუქმდა მომხმარებლის მიერ." + "მეხსიერების შეცდომა." + "USB მეხსიერება არ არის." + "SD ბარათი არ არის. გადმოგზავნილი ფაილების შესანახად ჩადეთ SD ბარათი." + "კავშირი ვერ განხორციელდა." + "მოთხოვნის სწორად დამუშავება ვერ ხერხდება." + "უცნობი შეცდომა." + "Bluetooth-ით მიღებული" + "Bluetooth გაზიარება" + "%1$s მიღება დასრულდა." + "%1$s გაგზავნა დასრულდა." + "შემომავალი ტრანსფერები" + "გამავალი ტრანსფერები" + "ტრანსფერების ისტორია ცარიელია." + "სიიდან ყველა ერთეული ამოიშლება." + "Bluetooth გაზიარება: გაგზავნილი ფაილები" + "Bluetooth გაზიარება: მიღებული ფაილები" + + %1$d წარუმატებელი. + %1$d წარუმატებელი. + + + %1$d წარმატებული, %2$s + %1$d წარმატებული, %2$s + + "სიის გასუფთავება" + "გახსნა" + "სიიდან ამოშლა" + "ამოშლა" + "რა უკრავს" + "შენახვა" + "გაუქმება" + "აირჩიეთ ანგარიშები, რომელთა გაზიარებაც Bluetooth-ის მეშვეობით გსურთ. დაკავშირებისას ანგარიშებზე წვდომის დადასტურება მაინც მოგიწევთ." + "დარჩენილი სლოტი:" + "აპლიკაციის ხატულა" + "Bluetooth შეტყობინების გაზიარების პარამეტრები" + "ანგარიშის არჩევა ვერ ხერხდება. დარჩენილია 0 სლოტი" + "Bluetooth აუდიო დაკავშირებულია" + "Bluetooth აუდიო გათიშულია" + "Bluetooth აუდიო" + "4 გბაიტზე დიდი მოცულობის ფაილების გადატანა ვერ მოხერხდება" + "Bluetooth-თან დაკავშირება" + diff --git a/android/app/res/values-ka/strings_pbap.xml b/android/app/res/values-ka/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..65eef84b0cf86fe6fb348141b3e269de567fd597 --- /dev/null +++ b/android/app/res/values-ka/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "სესსის გასაღები %1$s-ისთვის" + "აუცილებელია Bluetooth სესიის გასაღები" + "%1$s-თან კავშირის მიღების დრო ამოიწურა" + "%1$s-თან სესიის გასაღების შეყვანის დრო ამოიწურა" + "Obex ავტენთიფიკაციის მოთხოვნა" + "სესიის გასაღები" + "სესსის გასაღები %1$s-ისთვის" + "მანქანის ნაკრები" + "უცნობი სახელი" + "ჩემი სახელი" + "000000" + "Bluetooth კონტაქტის გაზიარება" + diff --git a/android/app/res/values-ka/strings_pbap_client.xml b/android/app/res/values-ka/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ka/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ka/strings_sap.xml b/android/app/res/values-ka/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..7b49d77148a6366be31eebfbae78d8bd35a2ebe3 --- /dev/null +++ b/android/app/res/values-ka/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM წვდომა" + "Bluetooth SIM წვდომა" + "ვთხოვოთ კლიენტს კავშირის გაწყვეტა?" + "ელოდება კლიენტს, რომ გაწყვიტოს კავშირი." + "კავშირის გაწყვეტა" + "კავშირის იძულებითი გაწყვეტა" + diff --git a/android/app/res/values-ka/test_strings.xml b/android/app/res/values-ka/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c20a45d1edee83f6df26845a28db3c61ab009524 --- /dev/null +++ b/android/app/res/values-ka/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "ჩანაწერის ჩასმა" + "ჩანაწერის დადასტურება" + "Ack ჩანაწერი" + "ყველა ჩანაწერის წაშლა" + "კარგი" + "ჩანაწერის წაშლა" + "TCP სერვერის დაწყება" + "TCP სერვერის შეტყობინება" + diff --git a/android/app/res/values-kk/config.xml b/android/app/res/values-kk/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-kk/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-kk/strings.xml b/android/app/res/values-kk/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c5f1ad1aa9836701554ae2bee171a3427629d00c --- /dev/null +++ b/android/app/res/values-kk/strings.xml @@ -0,0 +1,137 @@ + + + + + "Жүктеу менеджеріне қол жетімділік." + "Қолданбаға BluetoothБөлісу менеджеріне кіріп, оны файлдары аудару үшін қолдану мүмкіндігін береді." + "Bluetooth құрылғысын рұқсатқа ие тізімге қосу" + "Қолданба Bluetooth құрылғысына уақытша рұқсат беріп, файлдарды сол құрылғыдан осы құрылғыға пайдаланушының растауынсыз жібере алады." + "Bluetooth" + "Белгісіз құрылғы" + "Белгісіз" + "Ұшақ режимі" + "Bluetooth байланысын ұшақ режимінде қолдану мүмкін емес." + + "Bluetooth қызметтерін қолдану үшін, алдымен Bluetooth байланысын қосу қажет." + "Bluetooth байланысы қазір қосылсын ба?" + "Жабу" + "Қосу" + "Файл жіберу" + "Кіріс файлды қабылдау керек пе?" + "Бас тарту" + "Қабылдау" + "Жарайды" + "\"%1$s\" жіберген файлды қабылдау барысында уақыт аяқталды." + "Кіріс файл" + "%1$s %2$s жіберуге дайын" + "Bluetooth бөлісу: %1$s файлын қабылдауда" + "Bluetooth бөлісу: %1$s файлы қабылданды" + "Bluetooth бөлісу: %1$s файлы қабылданбады" + "Bluetooth бөлісу: %1$s файлын жіберуде" + "Bluetooth бөлісу: %1$s файлы жіберілді" + "100% аяқталды" + "Bluetooth бөлісу: %1$s файлы жіберілмеді." + "Файл аудару" + "Кімнен: \"%1$s\"" + "Файл: %1$s" + "Файл өлшемі: %1$s" + + "Файлды қабылдауда…" + "Тоқтату" + "Жасыру" + "Кімнен" + "Файл атауы" + "Өлшем" + "Файл қабылданбады" + "Файл: %1$s" + "Себеп: %1$s" + "Жарайды" + "Файл қабылданды" + "Ашу" + "Кімге: \"%1$s\"" + "Файл түрі: %1$s (%2$s)" + "Файлды жіберуде…" + "Файл жіберілді" + "Жарайды" + "Файл \"%1$s\" құрылғысына жіберілмеді." + "Файл: %1$s" + "Жабу" + "Жарайды" + "Белгісіз файл" + "Файлдың бұл түрін танитын қолданба жоқ. \n" + "Ешқандай файл жоқ" + "Ондай файл жоқ. \n" + "Күте тұрыңыз…" + "Bluetooth қосылуда…" + "Файл қабылданады. Орындалуын Хабарлар тақтасы арқылы бақылаңыз." + "Файлды қабылдау мүмкін емес." + "\"%1$s\" жіберген файлдары қабылдауды доғарды." + "Файлды \"%1$s\" байланысына жіберуде" + "%1$s файл \"%2$s\" байланысына жіберілуде" + "Файлды \"%1$s\" байланысына жіберу доғарылды." + "Файл сақтауға USB жадында орын жеткіліксіз." + "Файл сақтауға SD картасында орын жеткіліксіз." + "Қажет орын мөлшері: %1$s" + "Тым көп өтініштер қаралуда. Кейінірек қайта әрекеттеніп көріңіз." + "Файлды аудару әлі басталған жоқ." + "Файлды аудару орындалуда." + "Файлды аудару сәтті орындалды." + "Мазмұн қолдауы жоқ." + "Аударуға қабылдайтын құрылғы тыйым салды." + "Тасымалды пайдаланушы тоқтатты." + "Жад ақаулығы." + "Ешқандай USB жады жоқ." + "SD картасы жоқ. Аударылған файлдарды сақтау үшін SD картасын енгізіңіз." + "Байланыс сәтсіз болды." + "Өтінішті дұрыс орындау мүмкін емес." + "Белгісіз қателік." + "Bluetooth арқылы алынғандар" + "Bluetooth бөлісу" + "%1$s Толығымен қабылданды." + "%1$s Жіберіліп болды." + "Келген аударым" + "Кеткен аударымдар" + "Тасымал туралы дерек жоқ." + "Тізімнен барлық элементтер алынады." + "Bluetooth бөлісу: Жіберілген файлдар" + "Bluetooth бөлісу: Қабылданған файлдар" + + %1$d сәтсіз. + %1$d сәтсіз. + + + %1$d сәтті, %2$s + %1$d сәтті, %2$s + + "Тізімді өшіру" + "Ашу" + "Тізімнен өшіру." + "Өшіру" + "Қазір ойнауда" + "Сақтау" + "Бас тарту" + "Bluetooth арқылы бөлісетін есептік жазбаларды таңдаңыз. Әлі де қосылу кезінде есептік жазбаларға кез келген қатынасуды қабылдау керек." + "Қалған слоттар:" + "Қолданба белгішесі" + "Bluetooth арқылы хабар бөлісу параметрлері" + "Есептік жазбаны таңдау мүмкін емес. 0 слот қалды" + "Bluetooth дыбысы қосылды" + "Bluetooth дыбысы ажыратылды" + "Bluetooth aудиосы" + "Көлемі 4 ГБ-тан асатын файлдар тасымалданбайды" + "Bluetooth-ге қосылу" + diff --git a/android/app/res/values-kk/strings_pbap.xml b/android/app/res/values-kk/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..11893b9ab092f8ce9ea7c39f539940cc4d668052 --- /dev/null +++ b/android/app/res/values-kk/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s үшін сессия кілтін теру" + "Bluetooth сессия кілті қажет" + "%1$s арқылы байланыс қабылдау уақыты өтіп кетті" + "%1$s арқылы сессия кілтін енгізу уақыты өтіп кетті" + "Obex растау талабы" + "Сессия кілті" + "%1$s үшін сессия кілтін теру" + "Carkit" + "Белгісіз атау" + "Mені атым" + "000000" + "Bluetooth арқылы контактіні бөлісу" + diff --git a/android/app/res/values-kk/strings_pbap_client.xml b/android/app/res/values-kk/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-kk/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-kk/strings_sap.xml b/android/app/res/values-kk/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..56443aab73eff8c1fe068a0e729311de3dfed815 --- /dev/null +++ b/android/app/res/values-kk/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth арқылы SIM картасына кіру" + "Bluetooth арқылы SIM картасына кіру" + "Клиенттен ажырауын сұрау керек пе?" + "Клиенттің ажырағаны күтілуде" + "Ажырату" + "Мәжбүрлеп ажырату" + diff --git a/android/app/res/values-kk/test_strings.xml b/android/app/res/values-kk/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f1076b3a11bc43f99068a97714349dd49a8c1148 --- /dev/null +++ b/android/app/res/values-kk/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Жазбаны енгізу" + "Жазбаны растау" + "Жазбаны тану" + "Барлық жазбаларды жою" + "Жарайды" + "Жазбаны жою" + "TCP серверін бастау" + "TCP (жібреуі басқару протоколы) серверін ескерту" + diff --git a/android/app/res/values-km/config.xml b/android/app/res/values-km/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-km/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-km/strings.xml b/android/app/res/values-km/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f0f2a13b4f572ab4842f8cd21a235bd4e9a5b0c0 --- /dev/null +++ b/android/app/res/values-km/strings.xml @@ -0,0 +1,137 @@ + + + + + "ចូល​ដំណើរ​ការ​កម្មវិធី​គ្រប់គ្រង​ការ​ទាញ​យក​។" + "អនុញ្ញាត​ឲ្យ​កម្មវិធី​ត្រូវ​​ចូល​ដំណើរការ​កម្មវិធី​គ្រប់គ្រង BluetoothShare ហើយ​​ប្រើ​វា​ដើម្បី​ផ្ទេរ​ឯកសារ។" + "សិទ្ធិ​ចូលប្រើ​ឧបករណ៍​ប៊្លូធូស​ក្នុងបញ្ជី​អនុញ្ញាត​។" + "អនុញ្ញាតឱ្យ​កម្មវិធី​ដាក់​ឧបករណ៍​ប៊្លូធូស​ក្នុង​បញ្ជីអនុញ្ញាតជា​បណ្ដោះអាសន្ន​ ដែល​អនុញ្ញាត​ឱ្យ​ឧបករណ៍​នោះ​ផ្ញើ​ឯកសារ​ទៅ​ឧបករណ៍​​នេះដោយ​មិន​ចាំបាច់​បញ្ជាក់​អ្នកប្រើប្រាស់​។" + "ប៊្លូធូស" + "មិន​ស្គាល់​ឧបករណ៍" + "មិន​ស្គាល់" + "ពេល​ជិះ​យន្តហោះ" + "អ្នក​មិន​អាច​ប្រើ​ប៊្លូ​ធូ​ស​ក្នុង​​​​​ពេល​ជិះ​យន្តហោះ​បាន​ទេ" + + "ដើម្បី​ប្រើ​​សេវា​ប៊្លូ​ធូ​ស ​ជា​ដំបូង​អ្នក​ត្រូវ​តែ​បើក​​ប៊្លូ​ធូ​ស​សិន​។" + "បើក​ប៊្លូធូស​ឥឡូវ​នេះ?" + "បោះ​បង់​" + "បើក" + "ការ​ផ្ទេរ​ឯកសារ" + "ទទួល​ឯកសារ​ចូល?" + "បោះបង់" + "ទទួល" + "យល់​ព្រម​" + "អស់​ពេល​ទទួល​​ឯកសារ​ចូល​ពី \"%1$s\"" + "ឯកសារចូល" + "%1$s ត្រៀមរួចរាល់ក្នុងការផ្ញើ %2$s" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ ទទួល %1$s" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ បាន​ទទួល​ %1$s" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ មិន​បាន​ទទួល​ឯកសារ %1$s" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ ផ្ញើ %1$s" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ បាន​ផ្ញើ %1$s" + "​បញ្ចប់ 100​%" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ មិន​បាន​ផ្ញើ​ឯកសារ %1$s" + "ការ​ផ្ទេរ​ឯកសារ" + "ពី៖ \"%1$s\"" + "ឯកសារ៖ %1$s" + "ទំហំ​ឯកសារ៖ %1$s" + + "កំពុង​ទទួល​ឯកសារ…" + "បញ្ឈប់" + "លាក់" + "ពី" + "ឈ្មោះ​ឯកសារ" + "ទំហំ" + "មិន​​បាន​ទទួល​​​ឯកសារ" + "ឯកសារ៖ %1$s" + "មូលហេតុ៖ %1$s" + "យល់​ព្រម​" + "បាន​ទទួល​​ឯកសារ" + "បើក" + "ទៅ៖ \"%1$s\"" + "ប្រភេទ​ឯកសារ៖ %1$s (%2$s)" + "កំពុង​ផ្ញើ​ឯកសារ…" + "បាន​ផ្ញើ​ឯកសារ" + "យល់​ព្រម​" + "មិន​បាន​ផ្ញើ​ឯកសារ​ទៅ \"%1$s\" ។" + "ឯកសារ៖ %1$s" + "បិទ​" + "យល់​ព្រម​" + "មិន​ស្គាល់​ឯកសារ" + "គ្មាន​កម្មវិធី​​សម្រាប់​គ្រប់គ្រង​ប្រភេទ​ឯកសារ​នេះ​ទេ។ \n" + "គ្មាន​ឯកសារ" + "គ្មាន​ឯកសារ។ \n" + "សូម​រង់ចាំ..." + "កំពុង​បើក​ប៊្លូធូស…" + "នឹង​​​​បាន​ទទួល​​​ឯកសារ។ ពិនិត្យមើល​វឌ្ឍនភាព​នៅ​ក្នុង​ផ្ទាំង​ជូនដំណឹង។" + "មិន​អាច​ទទួល​​ឯកសារ។" + "បាន​បញ្ឈប់​ការ​ទទួល​​ឯកសារ​ពី \"%1$s\"" + "ផ្ញើ​ឯកសារ​ទៅ \"%1$s\"" + "ផ្ញើ​ឯកសារ %1$s ទៅ \"%2$s\"" + "បាន​បញ្ឈប់​ការ​ផ្ញើ​ឯកសារ​ទៅ \"%1$s\"" + "មិនមាន​ទំហំផ្ទុក​គ្រប់គ្រាន់​នៅក្នុងឧបករណ៍​ផ្ទុក USB ដើម្បីរក្សាទុក​ឯកសារទេ។" + "មិនមាន​ទំហំផ្ទុក​គ្រប់គ្រាន់​នៅលើកាត SD ដើម្បីរក្សាទុក​ឯកសារទេ។" + "ទំហំ​ដែល​ត្រូវ​ការ៖ %1$s" + "កំពុង​ដំណើរការ​សំណើ​ជា​ច្រើន​​។ សូម​ព្យាយាម​ម្ដង​ទៀត​នៅ​ពេល​ក្រោយ​។" + "មិន​ទាន់​បាន​ចាប់ផ្ដើម​ផ្ទេរ​ឯកសារ​នៅ​ឡើយ​ទេ។" + "កំពុង​បន្ត​ការ​ផ្ទេរ​ឯកសារ។" + "​ការ​ផ្ទេរ​ឯកសារ​​​បាន​បញ្ចប់​ដោយ​ជោគជ័យ​។" + "មិន​បាន​គាំទ្រ​មាតិកា។" + "ឧបករណ៍​គោលដៅ​បាន​ហាមឃាត់​ការ​ផ្ទេរ​។" + "អ្នក​ប្រើ​បាន​បោះបង់​ការ​ផ្ទេរ។" + "បញ្ហា​​ឧបករណ៍​ផ្ទុក​។" + "គ្មាន​ឧបករណ៍​ផ្ទុក​ USB ទេ។" + "គ្មាន​កាត​ SD ទេ។ សូម​បញ្ចូល​កាត SD ដើម្បី​រក្សាទុក​ឯកសារ​ដែល​បាន​ផ្ទេរ។" + "ការ​តភ្ជាប់​​មិន​ជោគជ័យ​។" + "មិន​អាច​ដោះស្រាយ​សំណើ​​​​ដោយ​ត្រឹមត្រូវ​ទេ។" + "មិន​ស្គាល់​កំហុស។" + "​បាន​ទទួលតាម​​ប៊្លូ​ធូ​ស" + "ការ​ចែករំលែក​ប៊្លូ​ធូ​ស" + "បាន​ទទួល​ពេញ​លេញ %1$s ។" + "បាន​ផ្ញើ​ពេញ​លេញ %1$s ។" + "ការ​ផ្ទេរ​ចូល" + "ការ​ផ្ទេរ​ចេញ" + "មិន​មាន​ប្រវត្តិ​​ផ្ទេរ​។" + "នឹង​សម្អាត​ធាតុ​ទាំងអស់​ពី​បញ្ជី។" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ បាន​ផ្ញើ​ឯកសារ" + "ការ​ចែក​រំលែក​ប៊្លូ​ធូ​ស៖ បាន​ទទួល​​​ឯកសារ" + + មិនបានជោគជ័យ %1$d + មិនបានជោគជ័យ %1$d + + + បានជោគជ័យ %1$d , %2$s + បានជោគជ័យ %1$d , %2$s + + "សម្អាត​បញ្ជី" + "បើក" + "សម្អាត​ពី​បញ្ជី" + "សម្អាត" + "ឥឡូវកំពុងចាក់" + "រក្សាទុក" + "បោះបង់" + "ជ្រើសគណនីដែលអ្នកចង់ចែករំលែកតាមរយៈប៊្លូធូស។ អ្នកនៅតែត្រូវទទួលយកលទ្ធភាពចូលដំណើរការទាំងឡាយទៅកាន់គណនីនេះដដែល នៅពេលភ្ជាប់។" + "រន្ធនៅសល់៖" + "រូបតំណាងកម្មវិធី" + "កំណត់ការចែករំលែកសារតាមប៊្លូធូស" + "មិនអាចជ្រើសគណនីទេ អស់រន្ធនៅសល់ហើយ។" + "សំឡេងប៊្លូធូសត្រូវបានភ្ជាប់" + "សំឡេងប៊្លូធូសត្រូវបានផ្តាច់" + "សំឡេងប៊្លូធូស" + "ឯកសារ​ដែល​មាន​ទំហំ​ធំ​ជាង 4 GB មិន​អាចផ្ទេរ​បាន​ទេ" + "ភ្ជាប់​ប៊្លូធូស" + diff --git a/android/app/res/values-km/strings_pbap.xml b/android/app/res/values-km/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..63430eff0a784fc2f89fb807a0a8be635d02c8b8 --- /dev/null +++ b/android/app/res/values-km/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "បញ្ចូល​សោ​សម័យ​សម្រាប់ %1$s" + "បាន​ទាមទារ​សោ​សម័យ​ប៊្លូធូស" + "អស់​ពេល​វេលា​ក្នុង​ការ​ទទួល​យក​ការ​តភ្ជាប់​ជាមួយ %1$s" + "អស់​ពេល​​ដើម្បី​បញ្ចូល​សោ​សម័យ​ជាមួយ %1$s" + "សំណើ​ការ​ផ្ទៀងផ្ទាត់ Obex" + "សោ​សម័យ" + "បញ្ចូល​សោ​សម័យ​សម្រាប់ %1$s" + "Carkit" + "មិន​ស្គាល់​ឈ្មោះ" + "ឈ្មោះ​របស់​ខ្ញុំ" + "000000" + "ការចែករំលែក​ទំនាក់ទំនង​ប៊្លូធូស" + diff --git a/android/app/res/values-km/strings_pbap_client.xml b/android/app/res/values-km/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-km/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-km/strings_sap.xml b/android/app/res/values-km/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e6dbde9248e4f678ac78d14feab4632e7766579 --- /dev/null +++ b/android/app/res/values-km/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ការចូលដំណើរការស៊ីមតាមប៊្លូធូស" + "ការចូលដំណើរការស៊ីមតាមប៊្លូធូស" + "ស្នើឲ្យម៉ាស៊ីនកូនធ្វើការផ្តាច់?" + "កំពុងរង់ចាំម៉ាស៊ីនកូនឲ្យផ្តាច់" + "ផ្ដាច់" + "បង្ខំឲ្យផ្តាច់" + diff --git a/android/app/res/values-km/test_strings.xml b/android/app/res/values-km/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..bbbb5feaace19e09ed3fb60bb7a6d6e062fa4ba1 --- /dev/null +++ b/android/app/res/values-km/test_strings.xml @@ -0,0 +1,13 @@ + + + "ប៊្លូធូស" + "បញ្ចូល​កំណត់ត្រា" + "បញ្ជាក់​កំណត់ត្រា" + "កំណត់ត្រា​ការ​ទទួល​ស្គាល់" + "លុប​កំណត់​ត្រា​ទាំងអស់" + "យល់​ព្រម​" + "លុប​កំណត់​ត្រា" + "ចាប់ផ្ដើម​​ម៉ាស៊ីន​មេ TCP" + "ជូន​ដំណឹង​ម៉ាស៊ីន​មេ TCP" + diff --git a/android/app/res/values-kn/config.xml b/android/app/res/values-kn/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..26b6b6b36d562ae0eb81d474be2884f605a0d7b2 --- /dev/null +++ b/android/app/res/values-kn/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.ಸೆಟ್ಟಿಂಗ್‌ಗಳು" + diff --git a/android/app/res/values-kn/strings.xml b/android/app/res/values-kn/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3438298f55db5b1d96a945f797bebe8c641c6437 --- /dev/null +++ b/android/app/res/values-kn/strings.xml @@ -0,0 +1,137 @@ + + + + + "ಡೌನ್‌ಲೋಡ್‌ ನಿರ್ವಾಹಕವನ್ನು ಪ್ರವೇಶಿಸಿ." + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ ನಿರ್ವಾಹಕ ಮತ್ತು ಫೈಲ್‌ಗಳ ವರ್ಗಾವಣೆಯನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ ಅನುಮತಿಸುತ್ತದೆ." + "ಸಮ್ಮತಿಪಟ್ಟಿ ಬ್ಲೂಟೂತ್‌ ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಿ." + "ಬಳಕೆದಾರರ ದೃಢೀಕರಣ ಇಲ್ಲದೆ ಈ ಸಾಧನಕ್ಕೆ ಫೈಲ್‌ಗಳನ್ನು ಕಳುಹಿಸಲು ಬ್ಲೂಟೂತ್‌ ಸಾಧನವನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಸಮ್ಮತಿಪಟ್ಟಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ." + "ಬ್ಲೂಟೂತ್‌" + "ಅಪರಿಚಿತ ಸಾಧನ" + "ಅಪರಿಚಿತ" + "ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್" + "ಏರ್‌ಪ್ಲೇನ್‌ ಮೋಡ್‌ನಲ್ಲಿ ನೀವು ಬ್ಲೂಟೂತ್‌‌ ಬಳಸಲು ಸಾಧ್ಯವಿಲ್ಲ." + + "ಬ್ಲೂಟೂತ್‌ ಸೇವೆಗಳನ್ನು ಬಳಸಲು, ಮೊದಲು ನೀವದನ್ನು ಆನ್‌ ಮಾಡಬೇಕು." + "ಇದೀಗ ಬ್ಲೂಟೂತ್‌ ಆನ್‌ ಮಾಡುವುದೇ?" + "ರದ್ದುಮಾಡಿ" + "ಆನ್‌ ಮಾಡಿ" + "ಫೈಲ್ ವರ್ಗಾವಣೆ" + "ಒಳಬರುವ ಫೈಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸುವುದೇ?" + "ನಿರಾಕರಿಸಿ" + "ಸ್ವೀಕರಿಸಿ" + "ಸರಿ" + "\"%1$s\" ಅವರಿಂದ ಫೈಲ್‌ ಸ್ವೀಕರಿಸುವಾಗ ಕಾಲಾವಧಿ ಮುಕ್ತಾಯಗೊಂಡಿದೆ" + "ಒಳಬರುತ್ತಿರುವ ಫೈಲ್" + "%1$s ಅವರು %2$s ಫೈಲ್ ಕಳುಹಿಸಲು ಸಿದ್ಧವಾಗಿದ್ದಾರೆ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: %1$s ಸ್ವೀಕರಿಸಲಾಗುತ್ತಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: %1$s ಸ್ವೀಕರಿಸಲಾಗಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: ಫೈಲ್‌ %1$s ಸ್ವೀಕರಿಸಿಲ್ಲ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: %1$s ಕಳುಹಿಸಲಾಗುತ್ತಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: %1$s ಕಳುಹಿಸಲಾಗಿದೆ" + "100% ಪೂರ್ಣವಾಗಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: %1$s ಫೈಲ್‌‌ ಕಳುಹಿಸಲಾಗಿಲ್ಲ" + "ಫೈಲ್ ವರ್ಗಾವಣೆ" + "ಇಂದ: \"%1$s\"" + "ಫೈಲ್‌: %1$s" + "ಫೈಲ್‌ ಗಾತ್ರ: %1$s" + + "ಫೈಲ್‌ ಸ್ವೀಕರಿಸಲಾಗುತ್ತಿದೆ…" + "ನಿಲ್ಲಿಸಿ" + "ಮರೆಮಾಡು" + "ಇವರಿಂದ" + "ಫೈಲ್‌ಹೆಸರು" + "ಗಾತ್ರ" + "ಫೈಲ್‌ ಸ್ವೀಕರಿಸಿಲ್ಲ" + "ಫೈಲ್‌: %1$s" + "ಕಾರಣ: %1$s" + "ಸರಿ" + "ಫೈಲ್‌ ಸ್ವೀಕರಿಸಲಾಗಿದೆ" + "ತೆರೆಯಿರಿ" + "ಇವರಿಗೆ: \"%1$s\"" + "ಫೈಲ್‌ ಪ್ರಕಾರ: %1$s (%2$s)" + "ಫೈಲ್‌ ಕಳುಹಿಸಲಾಗುತ್ತಿದೆ…" + "ಫೈಲ್‌ ಕಳುಹಿಸಲಾಗಿದೆ" + "ಸರಿ" + "\"%1$s\" ಇವರಿಗೆ ಫೈಲ್‌ ಕಳುಹಿಸಲಾಗಲಿಲ್ಲ." + "ಫೈಲ್‌: %1$s" + "ಮುಚ್ಚು" + "ಸರಿ" + "ಅಪರಿಚಿತ ಫೈಲ್" + "ಈ ಪ್ರಕಾರದ ಫೈಲ್ ನಿರ್ವಹಿಸಲು ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್ ಇಲ್ಲ. \n" + "ಫೈಲ್ ಇಲ್ಲ" + "ಫೈಲ್‌ ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲ. \n" + "ದಯವಿಟ್ಟು ನಿರೀಕ್ಷಿಸಿ…" + "ಬ್ಲೂಟೂತ್‌ ಆನ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ…" + "ಫೈಲ್‌ ಸ್ವೀಕರಿಸಲಾಗುತ್ತದೆ. ಅಧಿಸೂಚನೆ ಫಲಕದಲ್ಲಿ ಪ್ರಗತಿಯನ್ನು ಪರಿಶೀಲಿಸಿ." + "ಫೈಲ್‌ ಸ್ವೀಕರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ." + "\"%1$s\" ರಿಂದ ಫೈಲ್‌ ಸ್ವೀಕರಿಸುವುದನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ" + "\"%1$s\" ಇವರಿಗೆ ಫೈಲ್‌‌ ಕಳುಹಿಸಲಾಗುತ್ತಿದೆ" + "\"%2$s\" ಇವರಿಗೆ %1$s ಫೈಲ್‌‌ಗಳನ್ನು ಕಳುಹಿಸಲಾಗುತ್ತಿದೆ" + "\"%1$s\" ಇವರಿಗೆ ಫೈಲ್‌ ಕಳುಹಿಸುವುದನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ" + "ಫೈಲ್ ಅನ್ನು ಉಳಿಸಲು USB ಸಂಗ್ರಹಣೆಯಲ್ಲಿ ಸಾಕಷ್ಟು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." + "ಫೈಲ್ ಅನ್ನು ಉಳಿಸಲು SD ಕಾರ್ಡ್‌ನಲ್ಲಿ ಸಾಕಷ್ಟು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." + "ಅಗತ್ಯವಿರುವ ಸ್ಥಳಾವಕಾಶ: %1$s" + "ಹಲವಾರು ವಿನಂತಿಗಳನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗುತ್ತಿದೆ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ." + "ಫೈಲ್‌‌ ವರ್ಗಾವಣೆ ಇನ್ನೂ ಪ್ರಾರಂಭಿಸಿಲ್ಲ." + "ಫೈಲ್‌‌ ವರ್ಗಾವಣೆಯು ಚಾಲ್ತಿಯಲ್ಲಿದೆ." + "ಫೈಲ್‌ ವರ್ಗಾವಣೆಯು ಸಂಪೂರ್ಣವಾಗಿ ಯಶಸ್ವಿಯಾಗಿದೆ." + "ವಿಷಯ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ." + "ಉದ್ದೇಶಿತ ಸಾಧನದಿಂದ ವರ್ಗಾವಣೆಯನ್ನು ನಿಷೇಧಿಸಲಾಗಿದೆ." + "ಬಳಕೆದಾರರ ಮೂಲಕ ವರ್ಗಾವಣೆಯನ್ನು ರದ್ದುಪಡಿಸಲಾಗಿದೆ." + "ಸಂಗ್ರಹಣೆಯ ಸಮಸ್ಯೆ." + "ಯಾವುದೇ USB ಸಂಗ್ರಹಣೆಯಿಲ್ಲ." + "ಯಾವುದೇ SD ಕಾರ್ಡ್‌ಗಳಿಲ್ಲ. ವರ್ಗಾವಣೆ ಮಾಡಲಾದ ಫೈಲ್‌ಗಳನ್ನು ಉಳಿಸಲು SD ಕಾರ್ಡ್‌ವೊಂದನ್ನು ಸೇರಿಸಿ." + "ಸಂಪರ್ಕವು ವಿಫಲವಾಗಿದೆ." + "ವಿನಂತಿಯನ್ನು ಸರಿಯಾಗಿ ನಿರ್ವಹಿಸಲಾಗುವುದಿಲ್ಲ." + "ಅಪರಿಚಿತ ದೋಷ." + "ಬ್ಲೂಟೂತ್‌ ಸ್ವೀಕರಿಸಲಾಗಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ" + "%1$s ಸ್ವೀಕರಿಸುವುದು ಪೂರ್ಣಗೊಂಡಿದೆ." + "%1$s ಕಳುಹಿಸುವುದು ಪೂರ್ಣಗೊಂಡಿದೆ." + "ಇನ್‌ಬೌಂಡ್‌ ವರ್ಗಾವಣೆಗಳು" + "ಔಟ್‌ಬೌಂಡ್‌ ವರ್ಗಾವಣೆಗಳು" + "ವರ್ಗಾವಣೆಯ ಇತಿಹಾಸವು ಖಾಲಿಯಾಗಿದೆ." + "ಪಟ್ಟಿಯಿಂದ ಎಲ್ಲ ಐಟಂಗಳನ್ನು ತೆರವುಗೊಳಿಸಲಾಗುವುದು." + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: ಕಳುಹಿಸಲಾಗಿರುವ ಫೈಲ್‌‌ಗಳು" + "ಬ್ಲೂಟೂತ್‌ ಹಂಚಿಕೆ: ಫೈಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ" + + %1$d ಯಶಸ್ವಿಯಾಗಿಲ್ಲ. + %1$d ಯಶಸ್ವಿಯಾಗಿಲ್ಲ. + + + %1$d ಯಶಸ್ವಿಯಾಗಿದೆ, %2$s + %1$d ಯಶಸ್ವಿಯಾಗಿದೆ, %2$s + + "ಪಟ್ಟಿಯನ್ನು ತೆರವುಗೊಳಿಸಿ" + "ತೆರೆಯಿರಿ" + "ಪಟ್ಟಿಯಿಂದ ತೆರವುಗೊಳಿಸಿ" + "ತೆರವುಗೊಳಿಸು" + "Now Playing" + "ಉಳಿಸಿ" + "ರದ್ದುಮಾಡಿ" + "ಬ್ಲೂಟೂತ್‌ ಮೂಲಕ ಹಂಚಿಕೊಳ್ಳಲು ಬಯಸುವ ಖಾತೆಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ. ಸಂಪರ್ಕಿಸುವಾಗ ಖಾತೆಗಳಿಗೆ ಯಾವುದೇ ಪ್ರವೇಶವನ್ನು ನೀವು ಈಗಲೂ ಸಮ್ಮತಿಸಬೇಕಾಗುತ್ತದೆ." + "ಉಳಿದಿರುವ ಸ್ಲಾಟ್‌ಗಳು:" + "ಅಪ್ಲಿಕೇಶನ್‌ ಐಕಾನ್‌" + "ಬ್ಲೂಟೂತ್ ಸಂದೇಶ ಹಂಚಿಕೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು" + "ಖಾತೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. 0 ಸ್ಲಾಟ್‌ಗಳು ಉಳಿದಿವೆ" + "ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ ಸಂಪರ್ಕಗೊಂಡಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ" + "ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ" + "4GB ಗಿಂತ ದೊಡ್ಡದಾದ ಫೈಲ್‌ಗಳನ್ನು ವರ್ಗಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ" + "ಬ್ಲೂಟೂತ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ" + diff --git a/android/app/res/values-kn/strings_pbap.xml b/android/app/res/values-kn/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..c0b8957771000abb5733028cd54477333495e5e0 --- /dev/null +++ b/android/app/res/values-kn/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s ಗಾಗಿ ಸೆಶನ್‌ ಕೀ ಟೈಪ್ ಮಾಡಿ" + "ಬ್ಲೂಟೂತ್‌ ಸೆಶನ್‌ ಕೀ ಅಗತ್ಯವಿದೆ" + "%1$s ರೊಂದಿಗೆ ಸಂಪರ್ಕವನ್ನು ಸ್ವೀಕರಿಸುವಲ್ಲಿ ಸಮಯ ಮುಕ್ತಾಯಗೊಂಡಿದೆ" + "%1$s ರೊಂದಿಗೆ ಸೆಶನ್‌ ಕೀಯನ್ನು ಇನ್‌ಪುಟ್‌ ಮಾಡುವಲ್ಲಿ ಸಮಯ ಮುಕ್ತಾಯಗೊಂಡಿದೆ" + "Obex ದೃಢೀಕರಣ ವಿನಂತಿ" + "ಸೆಶನ್‌ ಕೀ" + "%1$s ಗಾಗಿ ಸೆಶನ್‌ ಕೀ ಟೈಪ್ ಮಾಡಿ" + "ಕಾರ್‌ಕಿಟ್" + "ಅಪರಿಚಿತ ಹೆಸರು" + "ನನ್ನ ಹೆಸರು" + "000000" + "ಬ್ಲೂಟೂತ್ ಸಂಪರ್ಕ ಹಂಚಿಕೆ" + diff --git a/android/app/res/values-kn/strings_pbap_client.xml b/android/app/res/values-kn/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..ced3f079ef2e030f5639d10cae312af896479f7e --- /dev/null +++ b/android/app/res/values-kn/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.ಬ್ಲೂಟೂತ್.pbapsink" + diff --git a/android/app/res/values-kn/strings_sap.xml b/android/app/res/values-kn/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..e0b6caec246f6dfc2a4f2c7fe401a88d34fb1336 --- /dev/null +++ b/android/app/res/values-kn/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ಬ್ಲೂಟೂತ್ ಸಿಮ್ ಪ್ರವೇಶ" + "ಬ್ಲೂಟೂತ್ ಸಿಮ್ ಪ್ರವೇಶ" + "ಕಡಿತಗೊಳಿಸಲು ಕ್ಲೈಂಟ್ ಅನ್ನು ವಿನಂತಿಸುವುದೇ?" + "ಕಡಿತಗೊಳಿಸಲು ಕ್ಲೈಂಟ್‌ಗೆ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ" + "ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಿ" + "ಒತ್ತಾಯದ ಕಡಿತಗೊಳಿಸುವಿಕೆ" + diff --git a/android/app/res/values-kn/test_strings.xml b/android/app/res/values-kn/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..844476728b016346fcbaa93db1ce4cd61800a83f --- /dev/null +++ b/android/app/res/values-kn/test_strings.xml @@ -0,0 +1,13 @@ + + + "ಬ್ಲೂಟೂತ್‌" + "ರೆಕಾರ್ಡ್‌ ಸೇರಿಸಿ" + "ರೆಕಾರ್ಡ್‌ ಅನ್ನು ಖಚಿತಪಡಿಸಿ" + "Ack ರೆಕಾರ್ಡ್‌ ಮಾಡಿ" + "ಎಲ್ಲ ರೆಕಾರ್ಡ್‌ ಅನ್ನು ಅಳಿಸಿ" + "ಸರಿ" + "ರೆಕಾರ್ಡ್‌ ಅಳಿಸಿ" + "TCP ಸರ್ವರ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಿ" + "TCP ಸರ್ವರ್ ಅನ್ನು ಸೂಚಿಸಿ" + diff --git a/android/app/res/values-ko/config.xml b/android/app/res/values-ko/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ko/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ko/strings.xml b/android/app/res/values-ko/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..9c007753319e4138bc650ccbbb578f300be09bb5 --- /dev/null +++ b/android/app/res/values-ko/strings.xml @@ -0,0 +1,137 @@ + + + + + "다운로드 관리자에 액세스합니다." + "앱에서 BluetoothShare 관리자에 액세스하고 이를 사용하여 파일을 전송할 수 있도록 합니다." + "블루투스 기기 액세스 허용" + "일시적으로 앱에서 블루투스 기기를 허용하여 사용자 확인을 받지 않고 블루투스 기기에서 이 기기로 파일을 보내도록 허용합니다." + "블루투스" + "알 수 없는 장치" + "알 수 없음" + "비행기 모드" + "비행기 모드에서는 블루투스를 사용할 수 없습니다." + + "Bluetooth 서비스를 사용하려면 먼저 Bluetooth를 켜야 합니다." + "지금 블루투스를 사용하시겠습니까?" + "취소" + "사용" + "파일 전송" + "수신 파일을 수락하시겠습니까?" + "거부" + "수락" + "확인" + "\'%1$s\'님이 보내는 파일을 수락하는 동안 제한 시간을 초과했습니다." + "수신 파일" + "%1$s님이 %2$s을(를) 보낼 준비가 되었습니다." + "블루투스 공유: %1$s 받는 중" + "블루투스 공유: %1$s 받음" + "블루투스 공유: %1$s 파일이 수신되지 않았습니다." + "블루투스 공유: %1$s 보내는 중" + "블루투스 공유: %1$s 보냄" + "100% 완료" + "블루투스 공유: %1$s 파일을 보내지 못함" + "파일 전송" + "보낸사람: \'%1$s\'" + "파일: %1$s" + "파일 크기: %1$s" + + "파일을 수신하는 중..." + "중지" + "숨기기" + "보낸 사람" + "파일 이름" + "크기" + "파일이 수신되지 않았습니다." + "파일: %1$s" + "이유: %1$s" + "확인" + "파일을 수신했습니다." + "열기" + "받는사람: \'%1$s\'" + "파일 형식: %1$s (%2$s)" + "파일을 보내는 중..." + "파일을 보냈습니다." + "확인" + "\'%1$s\'에 파일을 보내지 못했습니다." + "파일: %1$s" + "닫기" + "확인" + "알 수 없는 파일" + "이러한 형식의 파일을 처리할 앱이 없습니다. \n" + "파일 없음" + "파일이 없습니다. \n" + "잠시 기다려 주세요." + "블루투스 켜는 중..." + "파일이 수신됩니다. 알림 패널에서 진행률을 확인하세요." + "파일을 받지 못했습니다." + "\'%1$s\'님으로부터 파일 받기를 중지했습니다." + "\'%1$s\'님에게 파일을 보내는 중" + "\'%2$s\'에 %1$s개 파일을 보내는 중" + "\'%1$s\'님에게 파일 보내기가 중지되었습니다." + "USB 저장소에 파일을 저장할 공간이 부족합니다." + "SD 카드에 파일을 저장할 공간이 부족합니다." + "필요한 공간: %1$s" + "처리 중인 요청이 너무 많습니다. 잠시 후에 다시 시도해 주세요." + "파일 전송을 시작하지 않았습니다." + "파일이 전송되는 중입니다." + "파일 전송이 완료되었습니다." + "콘텐츠는 지원되지 않습니다." + "상대 기기에서 전송을 금지했습니다." + "사용자가 전송을 취소했습니다." + "저장용량 문제" + "USB 저장소가 없습니다." + "SD 카드가 없습니다. 전송된 파일을 저장할 SD 카드를 삽입하세요." + "연결하지 못했습니다." + "요청을 제대로 처리할 수 없습니다." + "알 수 없는 오류입니다." + "블루투스로 받은 파일" + "블루투스 공유" + "%1$s 수신을 완료했습니다." + "%1$s 전송을 완료했습니다." + "수신 전송" + "발신 전송" + "전송 기록이 없습니다." + "목록에서 모든 항목이 삭제됩니다." + "블루투스 공유: 파일 보냄" + "블루투스 공유: 파일 받음" + + %1$d개 실패 + %1$d개 실패 + + + %1$d개 성공 %2$s + %1$d개 성공 %2$s + + "목록 지우기" + "열기" + "목록에서 지우기" + "지우기" + "지금 재생 중" + "저장" + "취소" + "블루투스를 통해 공유하려는 계정을 선택하세요. 연결할 때 계정에 대한 모든 액세스를 수락해야 합니다." + "남은 슬롯:" + "애플리케이션 아이콘" + "블루투스 메시지 공유 설정" + "계정을 선택할 수 없습니다. 남은 슬롯이 없습니다." + "블루투스 오디오가 연결됨" + "블루투스 오디오가 연결 해제됨" + "블루투스 오디오" + "4GB보다 큰 파일은 전송할 수 없습니다" + "블루투스에 연결" + diff --git a/android/app/res/values-ko/strings_pbap.xml b/android/app/res/values-ko/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..1f60062412d7d078276288bccc6cfb1306ab664d --- /dev/null +++ b/android/app/res/values-ko/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s의 세션 키 입력" + "블루투스 세션 키 요청" + "%1$s 연결 수락 제한 시간이 초과되었습니다." + "%1$s에 세션 키 입력 제한 시간이 초과되었습니다." + "Obex 인증 요청" + "세션 키" + "%1$s의 세션 키 입력" + "Carkit" + "알 수 없는 이름" + "내 이름" + "000000" + "블루투스 연락처 공유" + diff --git a/android/app/res/values-ko/strings_pbap_client.xml b/android/app/res/values-ko/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ko/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ko/strings_sap.xml b/android/app/res/values-ko/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..f4cb6e94f6c3603fc4c79e67348b5feed5f12344 --- /dev/null +++ b/android/app/res/values-ko/strings_sap.xml @@ -0,0 +1,10 @@ + + + "블루투스 SIM 액세스" + "블루투스 SIM 액세스" + "클라이언트가 연결 해제하도록 요청하시겠습니까?" + "클라이언트가 연결 해제하도록 대기 중" + "연결 끊기" + "강제 연결 해제" + diff --git a/android/app/res/values-ko/test_strings.xml b/android/app/res/values-ko/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a242892da0638c5b30a0a5b9b73f26113ca12b8 --- /dev/null +++ b/android/app/res/values-ko/test_strings.xml @@ -0,0 +1,13 @@ + + + "블루투스" + "기록 삽입" + "기록 확인" + "기록 인식" + "모든 기록 삭제" + "확인" + "기록 삭제" + "TCP 서버 시작" + "TCP 서버 알림" + diff --git a/android/app/res/values-ky/config.xml b/android/app/res/values-ky/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ky/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ky/strings.xml b/android/app/res/values-ky/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..926252a38336c8a47307144f52ef20e341bfa4be --- /dev/null +++ b/android/app/res/values-ky/strings.xml @@ -0,0 +1,137 @@ + + + + + "Жүктөө менежерине жетүү." + "Колдонмого BluetoothАлмашуу менежерине жетип, ал аркылуу файлдарды өткөрүү уруксатын берет." + "Bluetooth түзмөгүнө кирүү мүмкүнчүлүгүн уруксат берилгендердин тизмесине жайгаштыруу." + "Колдонмого Bluetooth түзмөгүн уруксат берилгендердин тизмесине убактылуу жайгаштырып, колдонуучунун уруксатысыз файлдарды ал түзмөктөн бул түзмөккө жөнөтүү мүмкүнчүлүгүн берет." + "Bluetooth" + "Белгисиз түзмөк" + "Белгисиз" + "Учак тартиби" + "Учак тартибинде Bluetooth колдоно албайсыз." + + "Bluetooth кызматтарын колдонуш үчүн, биринчи Bluetooth\'ду жандырышыңыз керек." + "Bluetooth жандырылсынбы?" + "Айнуу" + "Жандыруу" + "Файл өткөрүү" + "Келүүчү файл кабыл алынсынбы?" + "Баш тартуу" + "Кабыл алуу" + "OK" + "\"%1$s\" жөнөткөн файлды алуу мөөнөтү өтүп кетти." + "Кирүүчү файл" + "%1$s %2$s жөнөтүүгө даяр" + "Bluetooth бөлүшүү: %1$s алынууда" + "Bluetooth бөлүшүү: %1$s алынды" + "Bluetooth бөлүшүү: %1$s алынган жок" + "Bluetooth бөлүшүү: %1$s жөнөтүлүүдө" + "Bluetooth бөлүшүү: %1$s жөнөтүлдү" + "100% бүттү" + "Bluetooth бөлүшүү: %1$s жөнөтүлгөн жок" + "Файл өткөрүү" + "Жөнөтүүчү: \"%1$s\"" + "Файл: %1$s" + "Файл өлчөмү: %1$s" + + "Файл алынууда…" + "Токтотуу" + "Жашыруу" + "Кимден" + "Файлдын аталышы" + "Өлчөмү" + "Файл алынган жок" + "Файл: %1$s" + "Себеп: %1$s" + "OK" + "Файл алынды" + "Ачуу" + "Алуучу: \"%1$s\"" + "Файлдын түрү: %1$s (%2$s)" + "Файл жөнөтүлүүдө…" + "Файл жөнөтүлдү" + "OK" + "Файл бул алуучуга жөнөтүлгөн жок: \"%1$s\"." + "Файл: %1$s" + "Жабуу" + "OK" + "Белгисиз файл" + "Бул түрдөгү файлды иштетүүчү колдонмо жок. \n" + "Файл жок" + "Мындай файл жок. \n" + "Күтө туруңуз…" + "Bluetooth жандырылууда…" + "Файл алынат. Билдирмелер тактасынан жүрүшүн байкап турсаңыз болот." + "Файлды алуу мүмкүн эмес." + "\"%1$s\" жөнөткөн файлды алуу токтотулду" + "Кийинкиге файл жөнөтүлүүдө: \"%1$s\"" + "\"%2$s\" дарегине %1$s файл жөнөтүлүүдө" + "\"%1$s\" дарегине файл жөнөтүү токтотулду" + "Файлды сактоо үчүн USB сактагычында орун жетишсиз." + "Файлды сактоо үчүн SD-картада орун жетишсиз." + "Керектүү орун: %1$s" + "Өтө көп талаптар иштетилүүдө. Кайта аракеттениңиз." + "Файл өткөрүү баштала элек." + "Файл өткөрүү жүрүүдө." + "Файл өткөрүү ийгиликтүү аяктады." + "Мазмун колдоого алынбайт." + "Алуучу түзмөк өткөрүүгө тыюу салды." + "Өткөрүү колдонуучу тарабынан токтотулду." + "Сактагычта маселе бар." + "USB эстутуму жок." + "SD-карта жок. Өткөрүлгөн файлдарды сактоо үчүн SD-картаны салыңыз." + "Байланышкан жок." + "Сурамды туура иштетүү мүмкүн эмес." + "Белгисиз ката." + "Bluetooth аркылуу алынгандар" + "Bluetooth аркылуу бөлүшүү" + "Бардыгы кабыл алынды: %1$s." + "Бардыгы жөнөтүлдү: %1$s." + "Кириш өткөрүүлөр" + "Чыгуучу өткөрүүлөр" + "Эч нерсе алына элек" + "Тизмек толугу менен тазаланат." + "Bluetooth бөлүшүү: Файлдар жөнөтүлдү" + "Bluetooth бөлүшүү: Алынган файлдар" + + %1$d ийгиликсиз болду. + %1$d ийгиликсиз болду. + + + %1$d ийгиликтүү бүттү, %2$s + %1$d ийгиликтүү бүттү, %2$s + + "Тизмекти тазалоо" + "Ачуу" + "Тизмектен алып салуу" + "Тазалоо" + "Эмне ойноп жатат?" + "Сактоо" + "Жокко чыгаруу" + "Bluetooth аркылуу бөлүшө турган каттоо эсептерин тандаңыз. Туташкан сайын аккаунттарына кирүү мүмкүнчүлүгүн ырастап турушуңуз керек." + "Калган көзөнөктөр:" + "Колдонмонун сүрөтчөсү" + "Bluetooth билдирүү бөлүшүү жөндөөлөрү" + "Аккаунт тандалбай жатат: 0 орун калды" + "Bluetooth аудио туташты" + "Bluetooth аудио ажыратылды" + "Bluetooth аудио" + "4Гб чоң файлдарды өткөрүү мүмкүн эмес" + "Bluetooth\'га туташуу" + diff --git a/android/app/res/values-ky/strings_pbap.xml b/android/app/res/values-ky/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..63d050fe1eea7d3b243b2ce8e6397f7f304aeb3c --- /dev/null +++ b/android/app/res/values-ky/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s үчүн сессиянын ачкычын териңиз" + "Bluetooth сессиясынын ачкычы талап кылынат" + "%1$s сунуштаган туташууну кабыл алуу мөөнөтү өтүп кетти." + "%1$s менен сессия ачкычын киргизүү мөөнөтү өтү кетти" + "Obex аутентификация талабы" + "Сессия Ачкычы" + "%1$s сессиясынын ачкычын териңиз" + "Автоунаа гарнитурасы" + "Белгисиз ат" + "Менин атым" + "000000" + "Bluetooth аркылуу байланыш менен бөлүшүү" + diff --git a/android/app/res/values-ky/strings_pbap_client.xml b/android/app/res/values-ky/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ky/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ky/strings_sap.xml b/android/app/res/values-ky/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..de4b50ef151a2d6283c6c38510e13a4b8771efb4 --- /dev/null +++ b/android/app/res/values-ky/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM уруксаты" + "Bluetooth SIM Уруксаты" + "Кардардан ажыратуу өтүнүлсүнбү?" + "Кардардын ажыратышы күтүлүүдө" + "Ажыратуу" + "Мажбурлап ажыратуу" + diff --git a/android/app/res/values-ky/test_strings.xml b/android/app/res/values-ky/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..1943811ecbaccdc6842b0222e6d1b29a0bcde825 --- /dev/null +++ b/android/app/res/values-ky/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Жазуу киргизүү" + "Жазууну ырастоо" + "Ack record" + "Бардык жазууларды жок кылуу" + "OK" + "Жазууну жок кылуу" + "TCP серверин баштоо" + "TCP серверин маалымдоо" + diff --git a/android/app/res/values-lo/config.xml b/android/app/res/values-lo/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-lo/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-lo/strings.xml b/android/app/res/values-lo/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..61f20f7894018f5f4f55ee51ea038827e49ec73c --- /dev/null +++ b/android/app/res/values-lo/strings.xml @@ -0,0 +1,137 @@ + + + + + "ເຂົ້າເຖິງໂຕຈັດການການດາວໂຫລດ​." + "ອະນຸຍາດໃຫ້ແອັບຯນີ້ເຂົາເຖິງໂຕຈັດການ BluetoothShare ແລະໃຊ້ມັນເພື່ອສົ່ງໄຟລ໌." + "ສ້າງລາຍຊື່ຍອມຮັບການເຂົ້າເຖິງອຸປະກອນ Bluetooth." + "ອະນຸຍາດໃຫ້ແອັບສ້າງລາຍຊື່ຍອມຮັບການເຂົ້າເຖິງອຸປະກອນ Bluetooth ຊົ່ວຄາວໄດ້, ເຊິ່ງຈະເຮັດໃຫ້ອຸປະກອນນັ້ນສົ່ງໄຟລ໌ໄປຫາອຸປະກອນນີ້ໄດ້ໂດຍບໍ່ຕ້ອງໃຫ້ຜູ້ໃຊ້ຢືນຢັນ." + "Bluetooth" + "ອຸປະກອນທີ່ບໍ່ຮູ້ຈັກ" + "ບໍ່ຮູ້ຈັກ" + "ໂໝດຢູ່ເທິງຍົນ" + "ທ່ານບໍ່ສາມາດໃຊ້ Bluetooth ໃນໂໝດຢູ່ເທິງຍົນໄດ້." + + "ໃນການນຳໃຊ້ບໍລິການ Bluetooth, ທ່ານຕ້ອງເປີດ Bluetooth ກ່ອນ." + "ເປີດ Bluetooth ດຽວນີ້ບໍ່?" + "ຍົກເລີກ" + "ເປີດ" + "ການໂອນໄຟລ໌" + "ຮັບເອົາ​ໄຟລ໌​ທີ່​ເຂົາ​ມາ​ບໍ?" + "ປະຕິເສດ" + "ຮັບເອົາ" + "ຕົກລົງ" + "ເກີດມີການໝົດເວລາໃນລະຫວ່າງທີ່ກຳລັງຮັບເອົາໄຟລ໌ທີ່ເຂົ້າມາຈາກ \"%1$s\"" + "ໄຟ​ລ໌​ເຂົ້າ​ມາ" + "%1$s ພ້ອມ​ທີ່​ຈະ​ສົ່ງ %2$s" + "ແບ່ງປັນໃນ Bluetooth: ກຳລັງຮັບເອົາ %1$s" + "ແບ່ງປັນໃນ Bluetooth: ໄດ້ຮັບ %1$s ແລ້ວ" + "ແບ່ງປັນໃນ Bluetooth: ບໍ່ໄດ້ຮັບ %1$s" + "ແບ່ງປັນໃນ Bluetooth: ກຳລັງສົ່ງ %1$s" + "ແບ່ງປັນໃນ Bluetooth: ສົ່ງ %1$s ແລ້ວ" + "100​% ສໍາ​ເລັດແລ້ວ" + "ແບ່ງປັນໃນ Bluetooth: ໄຟລ໌%1$s ບໍ່ໄດ້ຖືກສົ່ງ" + "ໂອນໄຟລ໌" + "ຈາກ: \"%1$s\"" + "ໄຟລ໌: %1$s" + "ຂະໜາດໄຟລ໌: %1$s" + + "ກຳລັງຮັບເອົາໄຟລ໌..." + "ຢຸດ" + "ເຊື່ອງ" + "ຈາກ" + "ຊື່ໄຟລ໌" + "ຂະໜາດ" + "ໄຟລ໌ບໍ່ໄດ້ຮັບ" + "ໄຟລ໌: %1$s" + "ເຫດ​ຜົນ​: %1$s" + "ຕົກລົງ" + "ໄດ້ຮັບໄຟລ໌ແລ້ວ" + "ເປີດ" + "ເຖິງ: \"%1$s\"" + "ປະເພດຂອງໄຟລ໌: %1$s (%2$s)" + "ກຳລັງສົ່ງໄຟລ໌..." + "ໄຟລ໌ຖືກສົ່ງແລ້ວ" + "ຕົກລົງ" + "ໄຟລ໌ດັ່ງກ່າວບໍ່ໄດ້ຖືກສົ່ງໄປທີ່ \"%1$s." + "ໄຟລ໌​: %1$s" + "ປິດ" + "OK" + "ໄຟລ໌ທີ່ບໍ່ຮູ້ຈັກ" + "ບໍ່ມີແອັບຯໃດຈັດການໄຟລ໌ປະເພດນີ້ໄດ້. \n" + "ບໍ່ມີໄຟລ໌" + "ໄຟລ໌ດັ່ງກ່າວບໍ່ມີຢູ່ \n" + "ກະລຸນາລໍຖ້າ..." + "ກຳລັງເປີດ Bluetooth..." + "ຈະໄດ້ຮັບໄຟລ໌ດັ່ງກ່າວ. ກວດສອບການດຳເນີນການໃນແຜງການແຈ້ງເຕືອນ." + "ບໍ່ສາມາດຮັບໄຟລ໌ໄດ້." + "ຢຸດການຮັບໄຟລ໌ຈາກ \"%1$s\" ແລ້ວ" + "ກຳລັງສົ່ງໄຟລ໌ໄປຫາ \"%1$s\"" + "ກຳລັງສົ່ງ %1$s ໄຟລ​໌​ໄປຫາ \"%2$s\"" + "ຢຸດການສົ່ງໄຟລ໌ໄປຫາ \"%1$s\" ແລ້ວ" + "ມີພື້ນທີ່ຢູ່ບ່ອນຈັດເກັບຂໍ້ມູນ USB ບໍ່ພຽງພໍໃຫ້ບັນທຶກໄຟລ໌." + "ມີພື້ນທີ່ຢູ່ SD card ບໍ່ພຽງພໍໃຫ້ບັນທຶກໄຟລ໌." + "ພື້ນທີ່ທີ່ຕ້ອງການ​: %1$s" + "ມີການຮ້ອງຂໍຫຼາຍເກີນໄປ, ກະລຸນາລອງໃໝ່ພາຍຫຼັງ." + "ການໂອນໄຟລ໌ຍັງບໍ່ທັນໄດ້ເລີ່ມເທື່ອ." + "ການໂອນໄຟລ໌ກຳລັງດຳເນີນຢູ່." + "ການໂອນໄຟລ໌ສຳເລັດແລ້ວ." + "ເນື້ອຫາບໍ່ໄດ້ຖືກຮອງຮັບ." + "ການໂອນໄຟລ໌ຖືກປິດກັ້ນໂດຍອຸປະກອນປາຍທາງ." + "ການໂອນຖືກຍົກເລີກໂດຍຜູ່ໃຊ້." + "ປັນຫາພື້ນທີ່ເກັບຂໍ້ມູນ." + "ບໍ່ມີບ່ອນຈັດເກັບຂໍ້ມູນ USB." + "ບໍ່ມີ SD ກາດ. ໃສ່ SD ກາດເພື່ອບັນທຶກໄຟລ໌ທີ່ໂອນມາ." + "ການເຊື່ອມຕໍ່ບໍ່ສຳເລັດຜົນ" + "ການຮ້ອງຂໍບໍ່ສາມາດຖືກຈັດການໄດ້ຢ່າງຖືກຕ້ອງ." + "ຄວາມຜິດພາດທີ່ບໍ່ຮູ້ຈັກ." + "ໄຟລ໌ທີ່ໄດ້ຮັບແລ້ວຈາກ Bluetooth" + "ແບ່ງປັນໃນ Bluetooth" + "%1$s ໄດ້ຮັບຮຽບຮ້ອຍແລ້ວ." + "%1$s ຖືກສົ່ງສຳເລັດແລ້ວ." + "ການໂອນເຂົ້າ" + "ການໂອນອອກ" + "ປະຫວັດການໂອນຫວ່າງເປົ່າ." + "ລາຍການທັງໝົດຈະຖືກລຶບອອກຈາກລາຍການດັ່ງກ່າວ." + "ແບ່ງປັນໃນ Bluetooth: ໄຟລ໌ສົ່ງແລ້ວ" + "ແບ່ງປັນໃນ Bluetooth: ໄຟລ໌ໄດ້ຮັບແລ້ວ" + + %1$d ບໍ່​ສຳ​ເລັດ. + %1$d ບໍ່​ສຳ​ເລັດ. + + + %1$d ສຳ​ເລັດ, %2$s + %1$d ສຳ​ເລັດ, %2$s + + "ລຶບລາຍການ" + "ເປີດ" + "ລຶບອອກຈາກລາຍການ" + "ລຶບ" + "Now Playing" + "ບັນທຶກ" + "ຍົກເລີກ" + "ເລືອກ​ບັນ​ຊີ​​ທີ່​ທ່ານ​ຕ້ອງ​ການ​​ແບ່ງ​ປັນ​ຜ່ານ Bluetooth. ​ທ່ານ​ຍັງ​ຈຳເປັນຕ້ອງຍອມ​ຮັບທຸກ​ການ​ເຂົ້າ​ເຖິງ​ບັນ​ຊີ​ຕ່າງໆ​ໃນເວລາ​ເຊື່ອມ​ຕໍ່." + "ຊ່ອງ​ຊ້າຍ:" + "​ໄອ​ຄອນ​ແອັບ​ພລິ​ເຄ​ຊັນ" + "ການ​ຕັ້ງ​ຄ່າ​ແບ່ງ​ປັນ​ຂໍ້​ຄວາມ Bluetooth" + "ບໍ່​ສາ​ມາດ​ເລືອກ​ບັນ​ຊີ​ໄດ້. ​ເຫຼືອ 0 ຊ່ອງ" + "ເຊື່ອມຕໍ່ສຽງ Bluetooth ແລ້ວ" + "ຕັດການເຊື່ອມຕໍ່ສຽງ Bluetooth ແລ້ວ" + "ສຽງ Bluetooth" + "ບໍ່ສາມາດໂອນຍ້າຍໄຟລ໌ທີ່ໃຫຍກວ່າ 4GB ໄດ້" + "ເຊື່ອມຕໍ່ກັບ Bluetooth" + diff --git a/android/app/res/values-lo/strings_pbap.xml b/android/app/res/values-lo/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..fe89ccd4d1483406979c3d446352156bd7098230 --- /dev/null +++ b/android/app/res/values-lo/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "ພິມລະຫັດເຊສຊັນສຳລັບ %1$s" + "ຕ້ອງມີ Bluetooth ລະຫັດເຊສຊັນ" + "ໝົດເວລາທີ່ຈະຮັບການເຊື່ອມຕໍ່ກັບ %1$s" + "ເກີດໝົດເວລາກ່ອນທີ່ຈະໃສ່ລະຫັດເຊສຊັນກັບ %1$s" + "ການຮ້ອງຂໍພິສູດຢືນຢັນໂຕ Obex" + "ກະແຈເຊສຊັນ" + "ພິມລະຫັດເຊສຊັນສຳລັບ %1$s" + "Carkit" + "ບໍ່ຮູ້ຊື່" + "ຊື່ຂອງຂ້ອຍ" + "000000" + "ການແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ຜ່ານ Bluetooth" + diff --git a/android/app/res/values-lo/strings_pbap_client.xml b/android/app/res/values-lo/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-lo/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-lo/strings_sap.xml b/android/app/res/values-lo/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..c323cf373193d7e5ef0f7568d8b4da44104d6366 --- /dev/null +++ b/android/app/res/values-lo/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ການ​ເຂົ້າ​ຫາ Bluetooth SIM" + "ການ​ເຂົ້າ​ຫາ Bluetooth SIM" + "ຂໍ​ໃຫ້​ລູກ​ຂ່າຍ​ຕັດ​ການ​ເຊື່ອມ​ຕໍ່?" + "ກຳ​ລັງ​ລໍ​ຖ້າ​ໃຫ້​ລູກ​ຂ່າຍ​ຕັດ​ການ​ເຊື່ອມ​ຕໍ່" + "ຕັດການເຊື່ອມຕໍ່" + "ບັງ​ຄັບ​ໃຫ້​ຕັດ​ເຊື່ອມ​ຕໍ່" + diff --git a/android/app/res/values-lo/test_strings.xml b/android/app/res/values-lo/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b056846acb0f7778c67547374639623989cac509 --- /dev/null +++ b/android/app/res/values-lo/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "ໃສ່ບັນທຶກ" + "ຢືນຢັນການບັນທຶກ" + "ບັນທຶກ Ack" + "ລຶບບັນທຶກທັງໝົດ" + "ຕົກລົງ" + "ລຶບການບັນທຶກ" + "ເລີ່ມ TCP ເຊີບເວີ" + "ແຈ້ງເຕືອນ TCP ເຊີບເວີ" + diff --git a/android/app/res/values-lt/config.xml b/android/app/res/values-lt/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-lt/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-lt/strings.xml b/android/app/res/values-lt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..64676bafe993af57d6e569a8519a326b63a2ee6f --- /dev/null +++ b/android/app/res/values-lt/strings.xml @@ -0,0 +1,141 @@ + + + + + "Pasiekti atsisiuntimo tvarkytuvę." + "Leidžiama programai pasiekti „BluetoothShare“ tvarkyklę ir naudoti ją failams perkelti." + "Į leidžiamųjų sąrašą įtraukto „Bluetooth“ įrenginio prieiga." + "Programai leidžiama laikinai į leidžiamųjų sąrašą įtraukti „Bluetooth“ įrenginį, suteikiant jam galimybę siųsti failus į šį įrenginį be naudotojo patvirtinimo." + "Bluetooth" + "Nežinomas įrenginys" + "Nežinoma" + "Lėktuvo režimas" + "Negalima naudoti „Bluetooth“ lėktuvo režimu." + + "Jei norite naudotis „Bluetooth“ paslaugomis, pirmiausia reikia įjungti „Bluetooth“." + "Įjungti „Bluetooth“ dabar?" + "Atšaukti" + "Įjungti" + "Failo perkėlimas" + "Priimti gaunamą failą?" + "Atmesti" + "Priimti" + "Gerai" + "Baigėsi laikas priimant gaunamą failą iš %1$s" + "Gaunamas failas" + "%1$s pasiruošęs (-usi) siųsti %2$s" + "„Bluetooth“ bendrinimas: gaunamas %1$s" + "„Bluetooth“ bendrinimas: %1$s gautas" + "„Bluetooth“ bendrinimas: %1$s failas negautas" + "„Bluetooth“ bendrinimas: siunčiamas %1$s" + "„Bluetooth“ bendrinimas: %1$s išsiųstas" + "100 % baigta" + "„Bluetooth“ bendrinimas: %1$s failas neišsiųstas" + "Failo perkėlimas" + "Nuo: %1$s" + "Failas: %1$s" + "Failo dydis: %1$s" + + "Gaunamas failas..." + "Sustabdyti" + "Slėpti" + "Iš" + "Failo pavadinimas" + "Dydis" + "Failas negautas" + "Failas: %1$s" + "Priežastis: %1$s" + "Gerai" + "Failas gautas" + "Atidaryti" + "Kam: %1$s" + "Failo tipas: %1$s (%2$s)" + "Siunčiamas failas..." + "Failas išsiųstas" + "Gerai" + "Failas neišsiųstas į „%1$s“." + "Failas: %1$s" + "Uždaryti" + "Gerai" + "Nežinomas failas" + "Nėra programos, kurią naudojant būtų galima tvarkyti šio tipo failą. \n" + "Nėra failų" + "Failas neegzistuoja. \n" + "Palaukite..." + "Įjungiamas „Bluetooth“…" + "Failas bus gautas. Patikrinkite eigą skydelyje „Pranešimai“." + "Failo negalima gauti." + "Sustabdytas failo gavimas iš %1$s" + "%1$s siunčiamas failas" + "%2$s siunčiami (-a) %1$s failai (-ų)" + "Sustabdytas failo siuntimas %1$s" + "USB atmintyje nėra pakankamai vietos, kad būtų galima išsaugoti failą." + "SD kortelėje nepakanka vietos, kad būtų galima išsaugoti failą." + "Reikalinga vieta: %1$s" + "Apdorojama per daug užklausų. Vėliau bandykite dar kartą." + "Failo perkėlimas dar nepradėtas." + "Vyksta failo perkėlimas." + "Failas sėkmingai perkeltas." + "Turinys nepalaikomas." + "Perkėlimas draudžiamas paskirties įrenginyje." + "Perkėlimą atšaukė naudotojas." + "Atmintinės problema." + "Nėra USB atminties." + "Nėra SD kortelės. Įdėkite SD kortelę, kurioje išsaugosite perkeltus failus." + "Nepavyko užmegzti ryšio." + "Nepavyksta tinkamai apdoroti užklausos." + "Nežinoma klaida." + "„Bluetooth“ gauta" + "„Bluetooth“ bendrinimas" + "%1$s gauta." + "%1$s išsiųsta." + "Gaunami perkėlimai" + "Siunčiami perkėlimai" + "Perkėlimo istorija tuščia." + "Visi sąrašo elementai bus išvalyti." + "„Bluetooth“ bendrinimas: išsiųsti failai" + "„Bluetooth“ bendrinimas: gauti failai" + + %1$d nesėkmingas. + %1$d nesėkmingi. + %1$d nesėkmingo. + %1$d nesėkmingų. + + + %1$d sėkmingas, %2$s + %1$d sėkmingi, %2$s + %1$d sėkmingo, %2$s + %1$d sėkmingų, %2$s + + "Išvalyti sąrašą" + "Atidaryti" + "Išvalyti iš sąrašo" + "Išvalyti" + "Dabar leidžiama" + "Išsaugoti" + "Atšaukti" + "Pasirinkite o paskyras, kurias norite bendrinti per „Bluetooth“. Jungiantis vis tiek reikės suteikti prieigą prie paskyrų." + "Liko sričių:" + "Programos piktograma" + "„Bluetooth“ pranešimų bendrinimo nustatymai" + "Negalima pasirinkti paskyros. Neliko jokių sričių" + "„Bluetooth“ garsas prijungtas" + "„Bluetooth“ garsas atjungtas" + "„Bluetooth“ garsas" + "Negalima perkelti didesnių nei 4 GB failų" + "Prisijungti prie „Bluetooth“" + diff --git a/android/app/res/values-lt/strings_pbap.xml b/android/app/res/values-lt/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c59543cce12aad36dbba03f9a7426fdeba2faf3 --- /dev/null +++ b/android/app/res/values-lt/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Įveskite %1$s sesijos raktą" + "Reikalingas „Bluetooth“ sesijos raktas" + "Baigėsi skirtasis ryšio su %1$s priėmimo laikas" + "Baigėsi įvesties sesijos rakto su %1$s laikas" + "„Obex“ tapatybės nustatymo užklausa" + "Sesijos raktas" + "Įveskite %1$s sesijos raktą" + "Automobilinė įranga" + "Nežinomas pavadinimas" + "Mano vardas" + "000000" + "„Bluetooth“ kontaktų bendrinimas" + diff --git a/android/app/res/values-lt/strings_pbap_client.xml b/android/app/res/values-lt/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-lt/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-lt/strings_sap.xml b/android/app/res/values-lt/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..af11e0178272611dc6ffeb3f113b9dc1a53d4019 --- /dev/null +++ b/android/app/res/values-lt/strings_sap.xml @@ -0,0 +1,10 @@ + + + "„Bluetooth“ SIM prieiga" + "„Bluetooth“ SIM prieiga" + "Prašyti kliento atsijungti?" + "Laukiama, kol klientas atsijungs" + "Atsijungti" + "Priverstinai atjungti" + diff --git a/android/app/res/values-lt/test_strings.xml b/android/app/res/values-lt/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..2d5046ad667e7be0d95bb89478220865fd12a701 --- /dev/null +++ b/android/app/res/values-lt/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Įterpti įrašą" + "Patvirtinti įrašą" + "Įrašas apie patvirtinimą" + "Ištrinti visus įrašus" + "Gerai" + "Ištrinti įrašą" + "Paleisti TCP serverį" + "Pranešti TCP serveriui" + diff --git a/android/app/res/values-lv/config.xml b/android/app/res/values-lv/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-lv/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-lv/strings.xml b/android/app/res/values-lv/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f6f7f84e225e7eaecd16f4ef13526a6dbcc6be26 --- /dev/null +++ b/android/app/res/values-lv/strings.xml @@ -0,0 +1,139 @@ + + + + + "Piekļuve lietojumprogrammai Download Manager." + "Ļauj lietotnei piekļūt BluetoothShare pārvaldniekam, lai to izmantotu failu pārsūtīšanai." + "Iekļaut Bluetooth ierīces piekļuvi atļaušanas sarakstā." + "Ļauj lietotnei īslaicīgi iekļaut Bluetooth ierīci atļaušanas sarakstā, lai Bluetooth ierīce bez lietotāja apstiprinājuma varētu sūtīt failus uz pašreizējo ierīci." + "Bluetooth" + "Nezināma ierīce" + "Nezināms" + "Lidojuma režīms" + "Pakalpojumu Bluetooth nevar izmantot lidmašīnas režīmā." + + "Lai izmantotu Bluetooth pakalpojumus, vispirms jāieslēdz tehnoloģija Bluetooth." + "Vai ieslēgt tehnoloģiju Bluetooth?" + "Atcelt" + "Ieslēgt" + "Faila pārsūtīšana" + "Vai pieņemt ienākošo failu?" + "Noraidīt" + "Piekrist" + "Labi" + "Saņemot ienākošu failu no saņēmēja %1$s, radās noildze." + "Ienākošais fails" + "Sūtītājs %1$s ir gatavs nosūtīt failu %2$s." + "Kopīgošana, izmantojot Bluetooth: notiek faila %1$s saņemšana" + "Kopīgošana, izmantojot Bluetooth: saņemts fails %1$s" + "Kopīgošana, izmantojot Bluetooth: fails %1$s netika saņemts" + "Kopīgošana, izmantojot Bluetooth: notiek faila %1$s sūtīšana" + "Kopīgošana, izmantojot Bluetooth: nosūtīts fails %1$s" + "100% pabeigts" + "Kopīgošana, izmantojot Bluetooth: fails %1$s netika nosūtīts" + "Faila pārsūtīšana" + "No: %1$s" + "Fails: %1$s" + "Faila lielums: %1$s" + + "Notiek faila saņemšana..." + "Apturēt" + "Slēpt" + "No" + "Faila nosaukums" + "Lielums" + "Fails netika saņemts." + "Fails: %1$s" + "Iemesls: %1$s" + "Labi" + "Fails ir saņemts." + "Atvērt" + "Kam: %1$s" + "Faila tips: %1$s (%2$s)" + "Notiek faila sūtīšana..." + "Fails ir nosūtīts." + "Labi" + "Fails netika nosūtīts saņēmējam %1$s." + "Fails: %1$s" + "Aizvērt" + "Labi" + "Nezināms fails" + "Nav nevienas lietotnes, ar kuru var apstrādāt šī tipa failu. \n" + "Nav faila" + "Šāds fails nepastāv. \n" + "Lūdzu, uzgaidiet…" + "Tiek ieslēgts Bluetooth savienojums..." + "Fails tiks saņemts. Skatiet progresu paziņojumu panelī." + "Failu nevar saņemt." + "Faila saņemšana no sūtītāja %1$s tika apturēta." + "Notiek faila sūtīšana saņēmējam %1$s" + "Notiek %1$s failu sūtīšana saņēmējam %2$s." + "Faila sūtīšana saņēmējam %1$s tika apturēta." + "USB atmiņā nepietiek vietas, lai saglabātu failu." + "SD kartē nepietiek vietas, lai saglabātu failu." + "Nepieciešamā brīvā vieta: %1$s" + "Tiek apstrādāts pārāk liels pieprasījumu skaits. Vēlāk mēģiniet vēlreiz." + "Faila pārsūtīšana vēl nav sākta." + "Notiek faila pārsūtīšana." + "Faila pārsūtīšana ir veiksmīgi pabeigta." + "Saturs netiek atbalstīts." + "Adresāta ierīcē tika aizliegta pārsūtīšana." + "Lietotājs atcēla pārsūtīšanu." + "Atmiņas problēma." + "Nav USB atmiņas." + "Nav SD kartes. Ievietojiet SD karti, lai saglabātu pārsūtītos failus." + "Neizdevās izveidot savienojumu." + "Pieprasījumu nevar pareizi apstrādāt." + "Nezināma kļūda." + "Bluetooth saņemtie faili" + "Bluetooth kopīgošana" + "Faila %1$s saņemšana pabeigta." + "Faila %1$s sūtīšana ir pabeigta." + "Pārsūtīšana: ienākošie faili" + "Pārsūtīšana: izejošie faili" + "Pārsūtīšanas vēsture ir tukša." + "No saraksta tiks notīrīti visi vienumi." + "Bluetooth Share: nosūtītie faili" + "Bluetooth Share: saņemtie faili" + + %1$d neveiksmīgu. + %1$d neveiksmīgs. + %1$d neveiksmīgi. + + + %1$d veiksmīgu, %2$s + %1$d veiksmīgs, %2$s + %1$d veiksmīgi, %2$s + + "Notīrīt sarakstu" + "Atvērt" + "Notīrīt no saraksta" + "Notīrīšana" + "Tagad atskaņo" + "Saglabāt" + "Atcelt" + "Atlasiet kontus, kurus vēlaties koplietot, izmantojot Bluetooth. Kad tiks izveidots savienojums, jums būs arī jāapstiprina piekļuve kontiem." + "Atlikušie sloti:" + "Lietojumprogrammas ikona" + "Bluetooth ziņojumu kopīgošanas iestatījumi" + "Nevar atlasīt kontu. Atlicis 0 slotu." + "Izveidots savienojums ar Bluetooth audio" + "Pārtraukts savienojums ar Bluetooth audio" + "Bluetooth audio" + "Nevar pārsūtīt failus, kas lielāki par 4 GB." + "Izveidot savienojumu ar Bluetooth" + diff --git a/android/app/res/values-lv/strings_pbap.xml b/android/app/res/values-lv/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5ea9f37c3ccb704ae67f3ae63dd1e8896e22031 --- /dev/null +++ b/android/app/res/values-lv/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Ievadiet ierīces “%1$s” sesijas atslēgu" + "Nepieciešama Bluetooth sesijas atslēga." + "Radās noildze, apstiprinot savienojumu ar ierīci “%1$s”." + "Radās noildze, ievadot ierīces “%1$s” sesijas atslēgu." + "Obex autentifikācijas pieprasījums" + "Sesijas atslēga" + "Ievadiet ierīces “%1$s” sesijas atslēgu" + "Automobiļa komplekts" + "Nezināms nosaukums" + "Mans vārds" + "000000" + "Kontaktpersonu kopīgošana, izmantojot Bluetooth" + diff --git a/android/app/res/values-lv/strings_pbap_client.xml b/android/app/res/values-lv/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-lv/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-lv/strings_sap.xml b/android/app/res/values-lv/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..dcea8364d7b222206203b5f4730708d7c2fd26d3 --- /dev/null +++ b/android/app/res/values-lv/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM piekļuve" + "Bluetooth SIM piekļuve" + "Vai pieprasīt, lai klients atvienotos?" + "Gaida, kad klients atvienosies" + "Atvienot" + "Atvienot piespiedu kārtā" + diff --git a/android/app/res/values-lv/test_strings.xml b/android/app/res/values-lv/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e0e5259e2f04bda3fd98c9e660650eadddcf985a --- /dev/null +++ b/android/app/res/values-lv/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Ievietot ierakstu" + "Ieraksta apstiprinājums" + "Ack ieraksts" + "Dzēst visus ierakstus" + "Labi" + "Dzēst ierakstu" + "Palaist TCP serveri" + "Paziņot TCP serverim" + diff --git a/android/app/res/values-mk/config.xml b/android/app/res/values-mk/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-mk/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-mk/strings.xml b/android/app/res/values-mk/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b09c701dc20e4a237829979046b2d954831b3654 --- /dev/null +++ b/android/app/res/values-mk/strings.xml @@ -0,0 +1,137 @@ + + + + + "Пристапи кон управувач за преземања." + "Овозможува апликацијата да пристапи до менаџерот на BluetoothShare и да го користи за пренос на датотеки." + "Пристап до уредот со Bluetooth на белата листа." + "Дозволува апликацијата привремено да стави на белата листа уред со Bluetooth на којшто ќе му овозможи испраќање датотеки до овој уред без потврда од корисникот." + "Bluetooth" + "Непознат уред" + "Непознат" + "Авионски режим" + "Не можете да користите Bluetooth во режим на авион." + + "За Bluetooth услуги, прво мора да вклучите Bluetooth." + "Вклучи Bluetooth сега?" + "Откажи" + "Вклучи" + "Пренос на датотека" + "Да се прифати дојдовната датотека?" + "Одбиј" + "Прифати" + "Во ред" + "Времето истече додека се прифаќаше дојдовната датотека од „%1$s“." + "Дојдовна датотека" + "%1$s е подготвен за испраќање на %2$s" + "Сподели преку Bluetooth: Се прима %1$s" + "Сподели преку Bluetooth: Примена %1$s" + "Сподели преку Bluetooth: Датотеката %1$s не е примена." + "Сподели преку Bluetooth: Се испраќа %1$s" + "Сподели преку Bluetooth: Испратена %1$s" + "100% завршено" + "Сподели преку Bluetooth: Датотеката %1$s не е испратена" + "Пренос на датотеки" + "Од: „%1$s“" + "Датотека: %1$s" + "Големина на датотеката: %1$s" + + "Примање датотеки..." + "Запри" + "Сокриј" + "Од" + "Име на датотека" + "Големина" + "Датотеката не е примена" + "Датотека: %1$s" + "Причина: %1$s" + "Во ред" + "Датотеката е примена" + "Отвори" + "До: „%1$s“" + "Тип на датотека: %1$s (%2$s)" + "Испраќање датотека..." + "Датотеката е испратена" + "Во ред" + "Датотеката не беше испратена до „%1$s“." + "Датотека: %1$s" + "Затвори" + "Во ред" + "Непозната датотека" + "Нема апликација за справување со овој тип на датотека. \n" + "Нема датотека" + "Датотеката не постои. \n" + "Почекајте..." + "Вклучување Bluetooth..." + "Датотеката ќе биде примена. Напредокот проверете го во таблата за известувања." + "Датотеката не може да се прими." + "Примањето на датотеката од „%1$s“ запре" + "Се испраќа датотека до „%1$s“" + "Се испраќаат %1$s датотеки до „%2$s“" + "Испраќањето на датотеката до „%1$s“ запре" + "Нема доволно простор на USB-меморијата за да се зачува датотеката." + "Нема доволно простор на SD-картичката за да се зачува датотеката." + "Потребен простор: %1$s" + "Се обработуваат премногу барања. Обидете се повторно подоцна." + "Преносот на датотека сè уште не е започнат." + "Во тек е пренос на датотека." + "Преносот на датотека е успешно завршен." + "Содржината не е поддржана." + "Преносот е забранет од целниот уред." + "Корисникот го откажа преносот." + "Проблем со меморија." + "Нема USB-меморија." + "Нема SD-картичка. Вметнете SD-картичка за да се зачуваат пренесените датотеки." + "Врската е неуспешна." + "Барањето не може да се обработи правилно." + "Непозната грешка." + "Прием од Bluetooth" + "Споделено преку Bluetooth" + "%1$s Примањето е завршено." + "%1$s Праќањето е завршено." + "Влезни преноси" + "Излезни преноси" + "Историјата на пренос е празна." + "Сите ставки ќе бидат избришани од списокот." + "Сподели преку Bluetooth: Пратени датотеки" + "Сподели преку Bluetooth: Примени датотеки" + + %1$d неуспешен. + %1$d неуспешни. + + + %1$d успешен, %2$s + %1$d успешни, %2$s + + "Исчисти список" + "Отвори" + "Исчисти од списокот" + "Исчисти" + "Now Playing" + "Зачувај" + "Откажи" + "Изберете ги сметките што сакате да ги споделите преку Bluetooth. Сепак, ќе мора да го прифаќате секој пристап до сметките при поврзување." + "Слободни отвори:" + "Икона на апликацијата" + "Поставки за споделување пораки преку Bluetooth" + "Не може да се избере сметка. 0 слободни отвори" + "Аудиото преку Bluetooth е поврзано" + "Аудиото преку Bluetooth е исклучено" + "Аудио преку Bluetooth" + "Не може да се пренесуваат датотеки поголеми од 4 GB" + "Поврзи се со Bluetooth" + diff --git a/android/app/res/values-mk/strings_pbap.xml b/android/app/res/values-mk/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..51199473026908562125b380f925b39187992297 --- /dev/null +++ b/android/app/res/values-mk/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Внеси клуч на сесија за %1$s" + "Потребен е клуч на сесија за Bluetooth" + "Времето за да се прифати конекција со %1$s истече" + "Времето за да се внеси клучот на сесијата со %1$s истече" + "Барање за OBEX автентикација" + "Клуч на сесија" + "Впиши клуч на сесија за %1$s" + "Carkit" + "Непознато име" + "Моето име" + "000000" + "Споделување контакти преку Bluetooth" + diff --git a/android/app/res/values-mk/strings_pbap_client.xml b/android/app/res/values-mk/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-mk/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-mk/strings_sap.xml b/android/app/res/values-mk/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..f2f45a348ce21838da1b763fdba845da0894f722 --- /dev/null +++ b/android/app/res/values-mk/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Пристап до SIM преку Bluetooth" + "Пристап до SIM преку Bluetooth" + "Побарајте да се исклучи клиентот?" + "Се чека да се исклучи клиентот" + "Прекини врска" + "Присилно исклучување" + diff --git a/android/app/res/values-mk/test_strings.xml b/android/app/res/values-mk/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7748f915379c8acfef22f60d5bd76b0eebb32e78 --- /dev/null +++ b/android/app/res/values-mk/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Вметни запис" + "Потврди запис" + "Ack запис" + "Избриши ги сите записи" + "Во ред" + "Избриши запис" + "Започни TCP сервер" + "Извести TCP сервер" + diff --git a/android/app/res/values-ml/config.xml b/android/app/res/values-ml/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ml/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ml/strings.xml b/android/app/res/values-ml/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c9320300b21644024f0c64ff8100f34f3603571 --- /dev/null +++ b/android/app/res/values-ml/strings.xml @@ -0,0 +1,137 @@ + + + + + "ഡൗൺലോഡ് മാനേജർ ആക്‌സസ്സ് ചെയ്യുക." + "BluetoothShare മാനേജർ ആക്‌സസ്സുചെയ്യാനും ഫയലുകൾ കൈമാറാൻ അത് ഉപയോഗിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." + "Bluetooth ഉപകരണത്തിലേക്കുള്ള ആക്സസിന്റെ അനുമതി ലിസ്റ്റ്." + "ഒരു Bluetooth ഉപകരണം താൽക്കാലികമായി അനുമതി ലിസ്റ്റിൽ ചേർക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു, അത് ഉപയോക്താവിന്റെ സ്ഥിരീകരണമില്ലാതെ ഈ ഉപകരണത്തിലേക്ക് ഫയലുകൾ അയയ്‌ക്കാൻ ആ ഉപകരണത്തെ അനുവദിക്കുന്നു." + "Bluetooth" + "അജ്ഞാത ഉപകരണം" + "അറിയില്ല" + "ഫ്ലൈറ്റ് മോഡ്" + "നിങ്ങൾക്ക് വിമാന മോഡിൽ ബ്ലൂടൂത്ത് ഉപയോഗിക്കാനാകില്ല." + + "ബ്ലൂടൂത്ത് സേവനങ്ങൾ ഉപയോഗിക്കാൻ, നിങ്ങൾ ആദ്യം ബ്ലൂടൂത്ത് ഓൺ ചെയ്യണം." + "ഇപ്പോൾ ബ്ലൂടൂത്ത് ഓണാക്കണോ?" + "റദ്ദാക്കുക" + "ഓൺ ചെയ്യുക" + "ഫയൽ കൈമാറൽ" + "ഇൻകമിംഗ് ഫയൽ സ്വീകരിക്കണോ?" + "നിരസിക്കുക" + "സ്വീകരിക്കുക" + "ശരി" + "\"%1$s\" -ൽ നിന്നും ഇൻകമിംഗ് ഫയൽ സ്വീകരിക്കുമ്പോൾ സമയപരിധി കഴിഞ്ഞിരുന്നു" + "ഇൻകമിംഗ് ഫയൽ" + "%2$s അയയ്ക്കാൻ %1$s തയ്യാറാണ്" + "ബ്ലൂടൂത്ത് പങ്കിടൽ: %1$s എന്ന ഫയൽ ലഭിക്കുന്നു" + "ബ്ലൂടൂത്ത് പങ്കിടൽ: %1$s ലഭിച്ചു" + "ബ്ലൂടൂത്ത് പങ്കിടൽ: %1$s എന്ന ഫയൽ ലഭിച്ചില്ല." + "ബ്ലൂടൂത്ത് പങ്കിടൽ: %1$s എന്ന ഫയൽ അയയ്‌ക്കുന്നു" + "ബ്ലൂടൂത്ത് പങ്കിടൽ: %1$s എന്ന ഫയൽ അയച്ചു" + "100% പൂർത്തിയായി" + "ബ്ലൂടൂത്ത് പങ്കിടൽ: %1$s എന്ന ഫയൽ അയച്ചില്ല" + "ഫയൽ കൈമാറൽ" + "അയച്ചത്: \"%1$s\"" + "ഫയൽ: %1$s" + "ഫയൽ വലുപ്പം: %1$s" + + "ഫയൽ നേടുന്നു…" + "നിര്‍ത്തുക" + "മറയ്‌ക്കുക" + "അയച്ചത്" + "ഫയല്‍നാമം" + "വലുപ്പം" + "ഫയൽ ലഭിച്ചില്ല" + "ഫയൽ: %1$s" + "കാരണം: %1$s" + "ശരി" + "ഫയൽ ലഭിച്ചു" + "തുറക്കുക" + "സ്വീകർത്താവ്: \"%1$s\"" + "ഫയൽ തരം: %1$s (%2$s)" + "ഫയൽ അയയ്‌ക്കുന്നു…" + "ഫയൽ അയച്ചു" + "ശരി" + "ഈ ഫയൽ \"%1$s\" -ലേക്ക് അയച്ചില്ല." + "ഫയൽ: %1$s" + "അവസാനിപ്പിക്കുക" + "ശരി" + "അജ്ഞാത ഫയൽ" + "ഇത്തരം ഫയൽ കൈകാര്യം ചെയ്യാനാകുന്ന അപ്ലിക്കേഷനുകളൊന്നുമില്ല. \n" + "ഫയലൊന്നുമില്ല" + "ഈ ഫയൽ നിലവിലില്ല. \n" + "കാത്തിരിക്കുക…" + "ബ്ലൂടൂത്ത് ഓൺ ചെയ്യുന്നു…" + "ഫയൽ ലഭിക്കും. അറിയിപ്പ് പാനലിൽ പുരോഗതി പരിശോധിക്കുക." + "ഫയൽ നേടാനാകില്ല." + "\"%1$s\" എന്നയാളിൽ നിന്നും ഫയൽ നേടുന്നത് നിർത്തി" + "\"%1$s\" എന്നയാൾക്ക് ഫയൽ അയയ്‌ക്കുന്നു" + "\"%2$s\" എന്നയാൾക്ക് %1$s ഫയലുകൾ അയയ്‌ക്കുന്നു" + "\"%1$s\" എന്നയാൾക്ക് ഫയൽ അയയ്‌ക്കുന്നത് നിർത്തി" + "ഫയൽ സംരക്ഷിക്കാൻ ആവശ്യമായ ഇടം USB സ്‌റ്റോറേജിൽ ഇല്ല." + "ഫയൽ സംരക്ഷിക്കാൻ ആവശ്യമായ ഇടം SD കാർഡിൽ ഇല്ല." + "ആവശ്യമായ ഇടം: %1$s" + "നിരവധി അഭ്യർത്ഥനകൾ പ്രോസസ്സ് ചെയ്യുന്നു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക." + "ഫയൽ കൈമാറ്റം ഇതുവരെ ആരംഭിച്ചിട്ടില്ല." + "ഫയൽ കൈമാറ്റം നടക്കുന്നു." + "ഫയൽ കൈമാറ്റം പൂർത്തിയായി." + "ഉള്ളടക്കത്തെ പിന്തുണയ്‌ക്കുന്നില്ല." + "ലക്ഷ്യസ്ഥാന ഉപകരണം, കൈമാറൽ നിരോധിച്ചിരിക്കുന്നു." + "കൈമാറൽ ഉപയോക്താവ് റദ്ദാക്കി." + "സംഭരണ പ്രശ്‌നം." + "USB സ്‌റ്റോറേജൊന്നുമില്ല." + "SD കാർഡൊന്നുമില്ല. കൈമാറിയ ഫയലുകൾ സംരക്ഷിക്കാൻ ഒരു SD കാർഡ് ചേർക്കുക." + "കണക്ഷൻ പരാജയപ്പെട്ടു." + "അഭ്യർത്ഥന ശരിയായി കൈകാര്യം ചെയ്യാനാകില്ല." + "അജ്ഞാത പിശക്." + "Bluetooth-ലൂടെ ലഭിച്ചവ" + "Bluetooth പങ്കിടൽ" + "%1$s നേടൽ പൂർത്തിയായി." + "%1$s അയച്ചത് പൂർത്തിയായി." + "ഇൻബൗണ്ട് കൈമാറലുകൾ" + "ഔട്ട്‌ബൗണ്ട് കൈമാറലുകൾ" + "കൈമാറൽ ചരിത്രം ശൂന്യമാണ്." + "ലിസ്റ്റിൽ നിന്നും എല്ലാ ഇനങ്ങളും മായ്‌ക്കും." + "ബ്ലൂടൂത്ത് പങ്കിടൽ: അയച്ച ഫയലുകൾ" + "ബ്ലൂടൂത്ത് പങ്കിടൽ: ലഭിച്ച ഫയലുകൾ" + + %1$d പരാജയം. + %1$d പരാജയം. + + + %1$d വിജയകരം, %2$s + %1$d വിജയകരം, %2$s + + "ലിസ്റ്റ് മായ്‌ക്കുക" + "തുറക്കുക" + "ലിസ്റ്റിൽ നിന്നും മായ്‌ക്കുക" + "മായ്‌ക്കുക" + "ഇപ്പോൾ പ്ലേ ചെയ്യുന്നത്" + "സംരക്ഷിക്കുക" + "റദ്ദാക്കുക" + "നിങ്ങൾക്ക് Bluetooth വഴി പങ്കിടേണ്ട അക്കൗണ്ടുകൾ തിരഞ്ഞെടുക്കുക. കണക്‌റ്റുചെയ്യുമ്പോൾ അക്കൗണ്ടുകളിലേക്കുള്ള ഏത് ആക്‌സസും നിങ്ങൾ തുടർന്നും അംഗീകരിക്കേണ്ടതാണ്." + "ശേഷിക്കുന്ന സ്ലോട്ടുകൾ:" + "അപ്ലിക്കേഷൻ ഐക്കൺ" + "Bluetooth സന്ദേശ പങ്കിടൽ ക്രമീകരണം" + "അക്കൗണ്ട് തിരഞ്ഞെടുക്കാനാകില്ല. 0 സ്ലോട്ടുകൾ ശേഷിക്കുന്നു" + "Bluetooth ഓഡിയോ കണക്‌റ്റുചെയ്തു" + "Bluetooth ഓഡിയോ വിച്ഛേദിച്ചു" + "Bluetooth ഓഡിയോ" + "4GB-യിൽ കൂടുതലുള്ള ഫയലുകൾ കൈമാറാനാവില്ല" + "Bluetooth-ലേക്ക് കണക്‌റ്റ് ചെയ്യുക" + diff --git a/android/app/res/values-ml/strings_pbap.xml b/android/app/res/values-ml/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..707be30aed7403b70164ad24a855b04453229b23 --- /dev/null +++ b/android/app/res/values-ml/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s എന്നതിനുള്ള സെഷൻ കീ ടൈപ്പുചെയ്യുക" + "ബ്ലൂടൂത്ത് സെഷൻ കീ ആവശ്യമാണ്" + "%1$s എന്നതുമായുള്ള കണക്ഷൻ അംഗീകരിക്കുന്നത് കാലഹരണപ്പെട്ടു" + "%1$s എന്നതിൽ സെഷൻ കീ നൽകുന്നത് കാലഹരണപ്പെട്ടു" + "Obex പ്രാമാണീകരണ അഭ്യർത്ഥന" + "സെഷൻ കീ" + "%1$s എന്നതിനുള്ള സെഷൻ കീ ടൈപ്പുചെയ്യുക" + "കാർകിറ്റ്" + "പേര് അജ്ഞാതമാണ്" + "എന്റെ പേര്" + "000000" + "Bluetooth കോണ്‍‌ടാക്റ്റ് പങ്കിടൽ" + diff --git a/android/app/res/values-ml/strings_pbap_client.xml b/android/app/res/values-ml/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ml/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ml/strings_sap.xml b/android/app/res/values-ml/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..1b1e53264b0ccaa4bdbec230e535736e22fc2120 --- /dev/null +++ b/android/app/res/values-ml/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM ആക്‌സസ്സ്" + "Bluetooth SIM ആക്‌സസ്സ്" + "വിച്‌ഛേദിക്കുന്നതിന് ക്ലയന്റിനോട് അഭ്യർത്ഥിക്കണോ?" + "ക്ലയന്റ് വിച്‌ഛേദിക്കുന്നതിനായി കാത്തിരിക്കുന്നു" + "വിച്ഛേദിക്കുക" + "നിർബന്ധിതമായി വിച്‌ഛേദിക്കുക" + diff --git a/android/app/res/values-ml/test_strings.xml b/android/app/res/values-ml/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..db53782f9ed716c3ca41d90378ac360fa148329a --- /dev/null +++ b/android/app/res/values-ml/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "റെക്കോർഡ് ചേർക്കുക" + "റെക്കോർഡ് സ്ഥിരീകരിക്കുക" + "Ack റെക്കോർഡ്" + "എല്ലാ റെക്കോർഡും ഇല്ലാതാക്കുക" + "ശരി" + "റെക്കോർഡ് ഇല്ലാതാക്കുക" + "TCP സെർവർ ആരംഭിക്കുക" + "TCP സെർവറിനെ അറിയിക്കുക" + diff --git a/android/app/res/values-mn/config.xml b/android/app/res/values-mn/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-mn/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-mn/strings.xml b/android/app/res/values-mn/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..eda157f4638b75a8b412adf8701d214ff83361bf --- /dev/null +++ b/android/app/res/values-mn/strings.xml @@ -0,0 +1,137 @@ + + + + + "Татан авалтын менежерт хандалт хийх." + "Апп-д BluetoothShare менежерт хандах, үүнийг ашиглан файл дамжуулах боломж олгоно." + "Зөвшөөрөх жагсаалтын bluetooth төхөөрөмжийн хандалт." + "Аппад Bluetooth төхөөрөмжийг түр хугацаанд зөвшөөрөх жагсаалтад оруулж, хэрэглэгчийн баталгаажуулалтгүйгээр энэ төхөөрөмж рүү файл илгээх боломж олгоно." + "Bluetooth" + "Үл мэдэгдэх төхөөрөмж" + "Тодорхойгүй" + "Нислэгийн горим" + "Та Нислэгийн горимд Bluetooth ашиглах боломжгүй." + + "Bluetooth үйлчилгээг ашиглахын тулд та эхлээд Bluetooth-ээ асаах шаардлагатай." + "Bluetooth-г одоо асаах уу?" + "Цуцлах" + "Асаах" + "Файл дамжуулалт" + "Ирж байгаа файлыг авах уу?" + "Татгалзах" + "Зөвшөөрөх" + "OK" + "\"%1$s\"-с ирж байгаа файлыг зөвшөөрөх явцад хугацаа хэтэрсэн байна" + "Ирж буй файл" + "%1$s нь %2$s-г илгээхэд бэлэн" + "Bluetooth хуваалцах: Хүлээн авч байна %1$s" + "Bluetooth хуваалцах: Хүлээн авсан %1$s" + "Bluetooth хуваалцах: Файл %1$s хүлээж аваагүй" + "Bluetooth хуваалцах: Илгээж байна %1$s" + "Bluetooth хуваалцах: Илгээсэн %1$s" + "100% дууссан" + "Bluetooth хуваалцах: %1$s файл илгээгдээгүй" + "Файл дамжуулалт" + "Илгээгч: \"%1$s\"" + "Файл: %1$s" + "Файлын хэмжээ: %1$s" + + "Файлыг хүлээн авч байна…" + "Зогсоох" + "Нуух" + "Хэнээс" + "Файлын нэр" + "Хэмжээ" + "Файлыг хүлээж аваагүй" + "Файл: %1$s" + "Шалтгаан: %1$s" + "OK" + "Файлыг хүлээж авсан" + "Нээх" + "Хэнд: \"%1$s\"" + "Файлын хэлбэр: %1$s (%2$s)" + "Файл илгээж байна…" + "Файлыг илгээсэн" + "OK" + "Файл \"%1$s\" руу илгээгдсэнгүй." + "Файл: %1$s" + "Хаах" + "OK" + "Үл мэдэгдэх файл" + "Ийм төрлийн файлыг нээх апп байхгүй байна. \n" + "Файл байхгүй" + "Файл байхгүй байна. \n" + "Түр хүлээнэ үү..." + "Bluetooth-г асааж байна…" + "Файлыг хүлээж авах болно. Мэдэгдлийн самбар дээрээс явцыг нь шалгана уу." + "Файлыг хүлээн авах боломжгүй." + "\"%1$s\"-с файл хүлээн авахыг зогсоосон" + "\"%1$s\" руу файлыг илгээж байна" + "%1$s файлуудыг \"%2$s\" руу илгээж байна" + "\"%1$s\" руу файл илгээхийн зогсоосон" + "USB сан дээр файлыг хадгалах хангалттай зай алга." + "SD карт дээр файлыг хадгалах хангалттай зай алга." + "Шаардлагатай зай: %1$s" + "Хэт олон хүсэлтийг боловсруулж байна. Дараа дахин оролдоно уу." + "Файл дамжуулалт хараахан эхлээгүй байна." + "Файл дамжуулалт үргэлжилж байна." + "Файл дамжуулалт амжилттай дууслаа." + "Агуулга дэмжигдэхгүй байна." + "Дамжуулалтыг хүлээн авагч төхөөрөмжөөс хориглосон." + "Дамжуулалтыг хэрэглэгч цуцалсан." + "Хадгалах сангийн асуудал." + "USB сан алга." + "SD карт алга. Шилжүүлсэн файлуудыг хадгалахын тулд SD картыг оруулна уу." + "Холболт амжилтгүй." + "Хүсэлтийг зөв гүйцэтгэх боломжгүй." + "Тодорхойгүй алдаа." + "Bluetooth хүлээж авсан" + "Bluetooth хуваалцах" + "%1$s Бүрэн хүлээж авсан." + "%1$s Илгээж дууссан." + "Дотогш чиглэсэн дамжуулалт" + "Гадагш чиглэсэн дамжуулалт" + "Дамжуулсан түүх хоосон байна." + "Жагсаалтаас бүгдийг нь арилгах болно." + "Bluetooth хуваалцах: Илгээсэн файлууд" + "Bluetooth хуваалцах: Хүлээн авсан файлууд" + + %1$d амжилтгүй. + %1$d амжилтгүй. + + + %1$d амжилттай, %2$s + %1$d амжилттай, %2$s + + "Жагсаалтыг арилгах" + "Нээх" + "Жагсаалтаас арилгах" + "Арилгах" + "Now Playing" + "Хадгалах" + "Цуцлах" + "Bluetooth-ээр хуваалцахыг хүсч буй дансаа сонго. Холболт хийх үед данс руу нэвтрэх аливаа хандалтыг хүлээн зөвшөөрөх хэрэгтэй болно." + "Үлдсэн слот:" + "Аппликейшний дүрс" + "Bluetooth мессеж хуваалцах тохиргоо" + "Дансыг сонгох боломжгүй. 0 слот үлдсэн" + "Bluetooth аудиог холбосон" + "Bluetooth аудиог салгасан" + "Bluetooth Аудио" + "4ГБ-с дээш хэмжээтэй файлыг шилжүүлэх боломжгүй" + "Bluetooth-тэй холбогдох" + diff --git a/android/app/res/values-mn/strings_pbap.xml b/android/app/res/values-mn/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..62d9e23b2140842a8a6ebc829a787022f65934ae --- /dev/null +++ b/android/app/res/values-mn/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s-н горимын түлхүүрийг оруулна уу" + "Bluetooth горимын түлхүүр шаардлагатай" + "%1$s-тай холболт хийхийг зөвшөөрөх явцад хугацаа хэтэрсэн байна" + "%1$s-д горимын түлхүүр оруулах явцад хугацаа хэтэрсэн байна" + "Obex гэрчлэлтийн хүсэлт" + "Горимын Түлхүүр" + "%1$s-н горимын түлхүүрийг оруулна уу" + "Carkit" + "Тодорхойгүй нэр" + "Миний нэр" + "000000" + "Bluetooth харилцагч хуваалцах" + diff --git a/android/app/res/values-mn/strings_pbap_client.xml b/android/app/res/values-mn/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-mn/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-mn/strings_sap.xml b/android/app/res/values-mn/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..95d0b0134c16bc2e28ca0a5ace9c092f3a69a09e --- /dev/null +++ b/android/app/res/values-mn/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM хандалт" + "Bluetooth SIM хандалт" + "Үйлчлүүлэгчээс салгахыг хүсэх үү?" + "Үйлчлүүлэгчийг салгахыг хүлээж байна" + "Салгах" + "Хүчээр салгах" + diff --git a/android/app/res/values-mn/test_strings.xml b/android/app/res/values-mn/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..cb66e39934958aea04b748b6d143cab27542c0d4 --- /dev/null +++ b/android/app/res/values-mn/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Бичлэг оруулах" + "Бичлэгийг баталгаажуулах" + "Ack бичлэг" + "Бүх бичлэгийг устгах" + "OK" + "Бичлэгийг устгах" + "TCP серверийг эхлүүлэх" + "TCP серверт мэдэгдэх" + diff --git a/android/app/res/values-mr/config.xml b/android/app/res/values-mr/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-mr/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-mr/strings.xml b/android/app/res/values-mr/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..dbdecdabc2ea9a586f2426db117c63eb673eea7b --- /dev/null +++ b/android/app/res/values-mr/strings.xml @@ -0,0 +1,137 @@ + + + + + "डाउनलोड व्यवस्थापक अ‍ॅक्सेस करा." + "अ‍ॅपला BluetoothShare व्‍यवस्‍थापकामध्‍ये प्रवेश करण्‍याची आणि फाइल स्‍थानांतरित करण्‍यासाठी त्याचा वापर करण्‍याची अनुमती देते." + "ब्लूटूथ डिव्‍हाइस अ‍ॅक्सेस ॲक्सेप्टलिस्ट करा." + "त्या डिव्हाइसला वापरकर्ता पुष्‍टीशिवाय या डिव्हाइसवर फाइल पाठवण्‍याची अनुमती देऊन ब्लूटूथ डिव्‍हाइसला तात्पुरते ॲक्सेप्टलिस्ट करण्‍याची अ‍ॅपला अनुमती देते." + "ब्लूटूथ" + "अज्ञात डिव्हाइस" + "अज्ञात" + "विमान मोड" + "तुम्ही विमान मोड मध्ये ब्लूटूथ वापरू शकत नाही." + + "ब्लूटूथ सेवांचा वापर करण्‍यासाठी, तुम्ही प्रथम ब्लूटूथ सुरू करा." + "आता ब्लूटूथ सुरू करायचे?" + "रद्द करा" + "सुरू करा" + "फाइल स्थानांतरण" + "येणारी फाइल स्‍वीकारायची?" + "नकार द्या" + "स्वीकारा" + "ठीक" + "\"%1$s\" कडील फाइल स्‍वीकार करताना वेळ संपली." + "येणारी फाइल" + "%2$s पाठविण्‍यासाठी %1$s तयार आहे" + "ब्लूटूथ शेअर: %1$s मिळवत आहे" + "ब्लूटूथ शेअर: %1$s प्राप्त केली" + "ब्लूटूथ शेअर: %1$s फाइल प्राप्त केली नाही" + "ब्लूटूथ शेअर: %1$s पाठवत आहे" + "ब्लूटूथ शेअर: %1$s पाठवली" + "100% पूर्ण" + "ब्लूटूथ शेअर: %1$s फाइल पाठवली नाही" + "फाइल स्थानांतरण" + "प्रेेषक: \"%1$s\"" + "फाइल: %1$s" + "फाइल आकार: %1$s" + + "फाइल प्राप्त करत आहे..." + "थांबा" + "लपवा" + "प्रेषक" + "फाईलनाव" + "आकार" + "फाइल प्राप्त केली नाही" + "फाइल: %1$s" + "कारण: %1$s" + "ठीक" + "फाइल प्राप्त केली" + "उघडा" + "प्रति: \"%1$s\"" + "फाइल प्रकार: %1$s (%2$s)" + "फाइल पाठवत आहे…" + "फाइल पाठविली" + "ठीक" + "\"%1$s\" वर फाइल पाठविली नाही." + "फाइल: %1$s" + "बंद करा" + "ठीक" + "अज्ञात फाइल" + "या प्रकारची फाइल हाताळण्यासाठी कोणताही अ‍ॅप नाही. \n" + "फाइल नाही" + "फाइल अस्‍तित्वात नाही. \n" + "कृपया प्रतीक्षा करा..." + "ब्लूटूथ सुरू करत आहे…" + "फाइल मिळेल. सूचना पॅनल मधील प्रगती तपासा." + "फाइल प्राप्त होऊ शकत नाही." + "\"%1$s\" कडील फाइल प्राप्त करणे थांबविले" + "\"%1$s\" ना फाइल पाठवित आहे" + "\"%2$s\" ना %1$s फाइल पाठवित आहे" + "\"%1$s\" ना फाइल पाठविणे थांबविले" + "फाइल सेव्ह करण्‍यासाठी USB स्टोरेजमध्ये पुरेशी जागा नाही." + "फाइल सेव्ह करण्‍यासाठी SD कार्डवर पुरेशी जागा नाही." + "स्‍थान आवश्यक: %1$s" + "बर्‍याच विनंत्यांवर प्रक्रिया होत आहे. नंतर पुन्हा प्रयत्न करा." + "फाइल स्थानांतरणाचा अद्याप सुरू झाला नाही." + "फाइल स्थानांतर सुरू आहे." + "फाइल स्थानांतरण यशस्वीरित्या पूर्ण झाले." + "आशय समर्थित नाही." + "लक्ष्‍य डिव्‍हाइसद्वारे स्‍थानांतरण निषिद्ध केले." + "वापरकर्त्याने स्‍थानांतरण रद्द केले." + "संचयन समस्‍या." + "USB स्टोरेज नाही." + "SD कार्ड नाही. ट्रान्सफर केलेल्‍या फाइल सेव्ह करण्‍यासाठी SD कार्ड घाला." + "कनेक्‍शन अयशस्‍वी." + "विनंती योग्य रीतीने हाताळली जाऊ शकत नाही." + "अज्ञात एरर" + "ब्लूटूथ मिळवले" + "ब्लूटूथ शेअर" + "%1$s प्राप्त करणे पूर्ण." + "%1$s पाठविणे पूर्ण." + "इनबाउंड स्‍थानांतरणे" + "आउटबाउंड स्‍थानांतरणे" + "ट्रान्सफर इतिहास रिक्त आहे." + "सूचीमधून सर्व आयटम साफ केले जातील." + "ब्लूटूथ शेअर: पाठवलेल्या फाइल" + "ब्लूटूथ शेअर: मिळवलेल्‍या फाइल" + + %1$d अयशस्वी झाले. + %1$d अयशस्वी झाले. + + + %1$d यशस्वी झाले, %2$s + %1$d यशस्वी झाले, %2$s + + "सूची साफ करा" + "उघडा" + "सूचीमधून साफ करा" + "साफ करा" + "आता प्ले करत आहे" + "सेव्ह करा" + "रद्द करा" + "तुम्ही ब्लूटूथद्वारे शेअर करू इच्छित असलेली खाती निवडा. कनेक्ट करताना अद्याप तुम्ही खात्यांमधील कोणताही अ‍ॅक्सेस स्वीकारण्याची आवश्यकता आहे." + "स्लॉट शिल्लक:" + "ॲप्लिकेशन आयकन" + "ब्लूटूथ मेसेज सामायिकरण सेटिंग्ज" + "खाते निवडू शकत नाही. 0 स्लॉट शिल्लक" + "ब्लूटूथ ऑडिओ कनेक्ट केला" + "ब्लूटूथ ऑडिओ डिस्कनेक्ट केला" + "ब्लूटूथ ऑडिओ" + "4 GB हून मोठ्या फाइल ट्रान्सफर करता येणार नाहीत" + "ब्लूटूथशी कनेक्ट करा" + diff --git a/android/app/res/values-mr/strings_pbap.xml b/android/app/res/values-mr/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..6775f6f45d4e1997d6a5bfe3b7d8ce1e71043fa7 --- /dev/null +++ b/android/app/res/values-mr/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$s साठी सत्र की टाईप करा" + "ब्लूटूथ सेशन की आवश्‍यक आहे" + "%1$s सह कनेक्शन स्वीकारणे टाइमआउट झाले" + "%1$s सह सेशन की इनपुट करणे टाइमआउट झाले" + "Obex प्रमाणीकरण विनंती" + "सत्र की" + "%1$s साठी सत्र की टाईप करा" + "कारकिट" + "अज्ञात नाव" + "माझे नाव" + "000000" + "ब्लूटूथ संपर्क शेअर" + diff --git a/android/app/res/values-mr/strings_pbap_client.xml b/android/app/res/values-mr/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..471d6f971d1f7eb5fcd7a9bfffba04329e5d1d5c --- /dev/null +++ b/android/app/res/values-mr/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.ब्लूटूथ.pbapsink" + diff --git a/android/app/res/values-mr/strings_sap.xml b/android/app/res/values-mr/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..560d04f23e6274c3026c8dc14890754fdf6e55e5 --- /dev/null +++ b/android/app/res/values-mr/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ब्लूटूथ सिम अ‍ॅक्सेस" + "ब्लूटूथ सिम अ‍ॅक्सेस" + "डिस्कनेक्‍ट करण्याची क्लायंटला विनंती करायची?" + "क्लायंटने डिस्कनेक्ट करण्‍याची प्रतीक्षा करत आहे" + "‍डिस्कनेक्ट करा" + "डिस्कनेक्‍ट करण्‍याची सक्ती करा" + diff --git a/android/app/res/values-mr/test_strings.xml b/android/app/res/values-mr/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..84d9b0eb3b73604c3d34fdadeca8daa277024bfe --- /dev/null +++ b/android/app/res/values-mr/test_strings.xml @@ -0,0 +1,13 @@ + + + "ब्लूटूथ" + "रेकॉर्ड घाला" + "रेकॉर्डची पुष्टी करा" + "Ack रेकॉर्ड" + "सर्व रेकॉर्ड हटवा" + "ठीक" + "रेकॉर्ड हटवा" + "TCP सर्व्‍हर सुरू करा" + "TCP सर्व्‍हरला सूचित करा" + diff --git a/android/app/res/values-ms/config.xml b/android/app/res/values-ms/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ms/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ms/strings.xml b/android/app/res/values-ms/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..9c808de087a8bdab1cff2fea666bfd63d8f11944 --- /dev/null +++ b/android/app/res/values-ms/strings.xml @@ -0,0 +1,137 @@ + + + + + "Akses pengurus muat turun." + "Membenarkan aplikasi mengakses pengurus Perkongsian Bluetooth dan menggunakannya untuk memindahkan fail." + "Senarai terima akses peranti bluetooth." + "Membenarkan apl untuk menyenarai terima peranti Bluetooth sementara waktu, membolehkan peranti itu menghantar fail ke peranti ini tanpa pengesahan pengguna." + "Bluetooth" + "Peranti tidak diketahui" + "Tidak diketahui" + "Mod pesawat" + "Anda tidak boleh menggunakan Bluetooth dalam mod Pesawat." + + "Untuk menggunakan perkhidmatan Bluetooth, anda perlu menghidupkan Bluetooth terlebih dahulu." + "Hidupkan Bluetooth sekarang?" + "Batal" + "Hidupkan" + "Pemindahan fail" + "Terima fail masuk?" + "Tolak" + "Terima" + "OK" + "Berlaku tamat masa semasa menerima fail masuk daripada \"%1$s\"" + "Fail masuk" + "%1$s sudah bersedia untuk menghantar %2$s" + "Perkongsian Bluetooth: Menerima %1$s" + "Perkongsian Bluetooth: Diterima %1$s" + "Perkongsian Bluetooth: Fail %1$s tidak diterima" + "Perkongsian Bluetooth: Menghantar %1$s" + "Perkongsian Bluetooth: Dihantar %1$s" + "Selesai 100%" + "Perkongsian Bluetooth: Fail %1$s tidak dihantar" + "Pemindahan fail" + "Daripada: \"%1$s\"" + "Fail: %1$s" + "Saiz fail: %1$s" + + "Menerima fail..." + "Berhenti" + "Sembunyi" + "Daripada" + "Nama fail" + "Saiz" + "Fail tidak diterima" + "Fail: %1$s" + "Sebab: %1$s" + "OK" + "Fail diterima" + "Buka" + "Kepada: \"%1$s\"" + "Jenis fail: %1$s (%2$s)" + "Menghantar fail..." + "Fail telah dihantar" + "OK" + "Fail tidak dihantarkan kepada \"%1$s\"." + "Fail: %1$s" + "Tutup" + "OK" + "Fail tidak diketahui" + "Tiada aplikasi untuk mengendalikan fail jenis ini. \n" + "Tiada fail" + "Fail tidak wujud. \n" + "Sila tunggu..." + "Menghidupkan Bluetooth..." + "Fail akan diterima. Semak kemajuan dalam panel Pemberitahuan." + "Fail tidak boleh diterima." + "Penerimaan fail daripada \"%1$s\" dihentikan" + "Menghantar fail kepada \"%1$s\"" + "Menghantar %1$s fail kepada \"%2$s\"" + "Penghantaran fail ke \"%1$s\" dihentikan" + "Ruang pada storan USB tidak mencukupi untuk menyimpan fail." + "Ruang pada kad SD tidak mencukupi untuk menyimpan fail." + "Ruang diperlukan: %1$s" + "Terlalu banyak permintaan sedang diproses. Cuba sebentar lagi." + "Pemindahan fail belum lagi dimulakan." + "Pemindahan fail sedang berlangsung." + "Pemindahan fail berjaya diselesaikan." + "Kandungan tidak disokong." + "Pemindahan dilarang oleh peranti sasaran." + "Pemindahan dibatalkan oleh pengguna." + "Isu storan." + "Tiada storan USB." + "Tiada kad SD. Masukkan kad SD untuk menyimpan fail yang dipindahkan." + "Sambungan tidak berjaya." + "Permintaan tidak dapat dikendalikan dengan betul." + "Ralat tidak diketahui." + "Bluetooth diterima" + "Perkongsian Bluetooth" + "%1$s Penerimaan selesai." + "%1$s Penghantaran selesai." + "Pemindahan masuk" + "Pemindahan keluar" + "Sejarah pemindahan kosong." + "Semua item akan dipadam bersih daripada senarai." + "Perkongsian Bluetooth: Fail diterima" + "Perkongsian Bluetooth: Fail diterima" + + %1$d tidak berjaya. + %1$d tidak berjaya. + + + %1$d berjaya, %2$s + %1$d berjaya, %2$s + + "Padam bersih senarai" + "Buka" + "Padam bersih daripada senarai" + "Padam bersih" + "Now Playing" + "Simpan" + "Batal" + "Pilih akaun yang anda ingin kongsikan melalui Bluetooth. Anda masih perlu menerima sebarang akses kepada akaun semasa menyambung." + "Slot yang tinggal:" + "Ikon Aplikasi" + "Tetapan Perkongsian Mesej Bluetooth" + "Tidak boleh memilih akaun. 0 slot yang tinggal" + "Audio Bluetooth disambungkan" + "Audio Bluetooth diputuskan sambungannya" + "Audio Bluetooth" + "Fail lebih besar daripada 4GB tidak boleh dipindahkan" + "Sambung ke Bluetooth" + diff --git a/android/app/res/values-ms/strings_pbap.xml b/android/app/res/values-ms/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..b2d133895e49f8bf14ce890496f592acf62f741c --- /dev/null +++ b/android/app/res/values-ms/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Taipkan kunci sesi untuk %1$s" + "Kunci sesi Bluetooth diperlukan" + "Berlaku tamat masa semasa menerima sambungan dengan %1$s" + "Tamat masa berlaku semasa memasukkan kunci sesi dengan %1$s" + "Permintaan pengesahan Obex" + "Kunci Sesi" + "Taipkan kunci sesi untuk %1$s" + "Kit kereta" + "Nama tidak diketahui" + "Nama saya" + "000000" + "Perkongsian Kenalan melalui Bluetooth" + diff --git a/android/app/res/values-ms/strings_pbap_client.xml b/android/app/res/values-ms/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ms/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ms/strings_sap.xml b/android/app/res/values-ms/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..48f764c834b0d08ddef9fa9de2b3e32ece5f0724 --- /dev/null +++ b/android/app/res/values-ms/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Akses SIM Bluetooth" + "Akses SIM Bluetooth" + "Minta pelanggan memutuskan sambungan?" + "Menunggu pelanggan untuk memutuskan sambungan" + "Putus sambungan" + "Paksa putuskan sambungan" + diff --git a/android/app/res/values-ms/test_strings.xml b/android/app/res/values-ms/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7591514821cac464df8cd8f5914eb8c7ce1f09b1 --- /dev/null +++ b/android/app/res/values-ms/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Masukkan rekod" + "Sahkan rekod" + "Rekod Ack" + "Padamkan semua rekod" + "OK" + "Padamkan rekod" + "Mulakan pelayan TCP" + "Beritahu pelayan TCP" + diff --git a/android/app/res/values-my/config.xml b/android/app/res/values-my/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-my/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-my/strings.xml b/android/app/res/values-my/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..75e676a13dea6b624f5c998ec10dbe5bbc65040a --- /dev/null +++ b/android/app/res/values-my/strings.xml @@ -0,0 +1,137 @@ + + + + + "ဒေါင်းလုပ်မန်နေဂျာကို အသုံးပြုနိုင်မည်" + "အပလီကေးရှင်းအား ဘလူးတုသ်မျှဝေမှု မန်နေဂျာကို အသုံးပြုခွင့်ပေးပြီး ဖိုင်လွှဲရန် အသုံးပြုပါ" + "ဘလူးတုသ်ယာယီလက်ခံစာရင်းကို အသုံးပြုခွင့်။" + "အက်ပ်အား ဘလူးတုသ်စက်ကို ယာယီလက်ခံစာရင်းထဲ ထည့်ရန်ခွင့်ပြုကာ အသုံးပြုသူ၏ အတည်ပြုချက်မရယူဘဲ ဖိုင်များကို စက်ထဲသို့ ပို့ခွင့်ပြုမည်။" + "ဘလူးတုသ်" + "မသိသော စက်" + "မသိ" + "လေယာဉ်ပျံမုဒ်" + "လေယာဥ်ပျံပေါ်သုံးစနစ်တွင် ဘလူးတုသ်အသုံးပြုမရပါ" + + "ဘလူးတုသ်ဆားဗစ်ကိုအသုံးပြုရန် ပထမဦးစွာ ဘလူးတုသ်ကိုဖွင့်ပါ" + "ယခုပင် ဘလူးတုသ်ကိုဖွင့်မည်လား?" + "မလုပ်တော့" + "ဖွင့်မည်" + "ဖိုင်လွှဲပြောင်းခြင်း" + "ဝင်လာသည့် ဖိုင်ကို လက်ခံမလား?" + "လက်မခံပါ" + "လက်ခံရန်" + "OK" + "\"%1$s\"မှ အဝင်ဖိုင်ကို လက်ခံနေစဥ် အချိန်ကုန်ဆုံးသွားပါပြီ" + "အဝင် ဖိုင်" + "%1$s က %2$sကို ပို့ရန် အသင့်ပါ" + "ဘလူးတုသ် ဝေမျှမှု - %1$s လက်ခံနေသည်" + "ဘလူးတုသ် ဝေမျှမှု - ရရှိပြီး%1$s" + "ဘလူးတုသ် ဝေမျှမှု - ဖိုင် %1$s မရရှိပါ" + "ဘလူးတုသ် ဝေမျှမှု - %1$s ပို့နေသည်" + "ဘလူးတုသ် ဝေမျှမှု - %1$s ပို့ပြီး" + "၁၀၀%ပြီးပါပြီ" + "ဘလူးတုသ် ဝေမျှမှု - ဖိုင်%1$s မပို့ပါ" + "ဖိုင်လွှဲပြောင်းခြင်း" + "မှ - \"%1$s\"" + "ဖိုင် - %1$s" + "ဖိုင်အရွယ်အစား - %1$s" + + "ဖိုင် လက်ခံနေပြီ" + "ရပ်ဆိုင်းရန်" + "ဖျောက်ထားမည်" + "မှ" + "ဖိုင်အမည်" + "ဆိုက်" + "ဖိုင် မရရှိပါ" + "ဖိုင် - %1$s" + "အကြောင်းပြချက် - %1$s" + "OK" + "လက်ခံရပြီးဖိုင်" + "ဖွင့်ရန်" + "သို့ - \"%1$s\"" + "ဖိုင်အမျိုးအစား - %1$s (%2$s)" + "ဖိုင်ပို့နေပါသည်" + "ဖိုင်ပို့ပြီး" + "OK" + "\"%1$s\"ထံသို့ ဖိုင်ကို မပို့ပါ" + "ဖိုင် - %1$s" + "ပိတ်ရန်" + "OK" + "အမျိုးအမည် မသိသောဖိုင်" + "ဤဖိုင်အမျိုးအစားကို ကိုင်တွယ်ရန် အပလီကေးရှင်းမရှိပါ\n" + "ဖိုင်မရှိပါ" + "ထိုဖိုင်မှာ မရှိပါ \n" + "ကျေးဇူးပြု၍ ခဏစောင့်ပါ…" + "ဘလူးတုသ် ဖွင့်နေသည်" + "ဖိုင်ကို လက်ခံပါမည် အကြောင်းကြားချက် အကန့်ထဲတွင် တိုးတက်မှုကိုကြည့်ပါ" + "ဤဖိုင်ကိုလက်ခံမရပါ" + "\"%1$s\"မှ ဖိုင်ကို လက်ခံခြင်းအား ရပ်ပါ" + "\"%1$s\"ထံသို့ ဖိုင်ကိုပို့ပါ" + "%1$s ဖိုင်ကို \"%2$s\"ထံသို့ ပို့ပါ" + "\"%1$s\"ထံသို့ ဖိုင်ကိုပို့ခြင်းအား ရပ်ဆိုင်းပါ" + "ဖိုင်သိမ်းရန် USB သိုလှောင်ခန်းတွင် နေရာမလောက်ပါ။" + "ဖိုင်သိမ်းရန် SD ကတ်ပေါ်တွင် နေရာမလောက်ပါ။" + "လိုအပ်သော နေရာပမာဏ - %1$s" + "တောင်းခံမှုများစွာကို ပြုလုပ်နေသည်၊ ခဏနေ ပြန်ကြိုးစားကြည့်ပါ" + "ဖိုင်လွှဲပြောင်းခြင်း မစရသေးပါ" + "ဖိုင်လွှဲပြောင်းခြင်းမှာ ပြုလုပ်နေဆဲဖြစ်သည်" + "ဖိုင်လွှဲပြောင်းခြင်း အောင်မြင်စွာပြီးဆုံးပါပြီ" + "အကြောင်းအရာသည် အထောက်အပံ့မဖြစ်ပါ" + "ဦးတည်ရာစက်မှ လွှဲပြောင်းခြင်းကို တားမြစ်ခြင်း" + "အသုံးပြုသူမှ လွှဲပြောင်းခြင်းကို ဖယ်ဖျက်ရန်" + "သိုလှောင်မှု အချက်" + "USB သိုလှောင်ခန်း မရှိပါ။" + "SD ကတ်မရှိပါ။ လွှဲပြောင်းထားသော ဖိုင်များကို သိမ်းရန် SD ကတ်ထည့်ပါ။" + "ချိတ်ဆက်ခြင်း မအောင်မြင်ပါ" + "တောင်းခံခြင်းကို မှန်ကန်စွာကိုင်တွယ်မရပါ" + "အမည်မသိသော မှားယွင်းမှု" + "ဘလူးတုသ် လက်ခံရပြီး" + "ဘလူးတုသ် မျှဝေမှု" + "%1$s လက်ခံရရှိပြီး" + "%1$s ပို့ခြင်း ပြီးဆုံးပြီး" + "ပြန်လည်ရောက်လာသော လွှဲမှုများ" + "ပြန်လည်ထွက်မည့် လွှဲမှုများ" + "လွှဲပြောင်းခြင်း မှတ်တမ်းတွင် ဘာမျှမရှိပါ" + "အရာများအားလုံး စာရင်းမှ ရှင်းလင်းပစ်လိမ့်မည်" + "ဘလူးတုသ် ဝေမျှမှု - ဖိုင်ပို့ပြီး" + "ဘလူးတုသ် ဝေမျှမှု - ရရှိပြီးဖိုင်များ" + + %1$d မအောင်မြင်ပါ။ + %1$d မအ​ောင်မြင်ပါ။ + + + %1$d အောင်မြင်ပါသည်၊ %2$s + %1$d အောင်မြင်ပါသည်၊ %2$s + + "စာရင်းကို ဖယ်ရှားရန်" + "ဖွင့်ရန်" + "စာရင်းမှ ရှင်းပစ်မည်" + "ရှင်းရန်" + "ယခု ဖွင့်နေသည်" + "သိမ်းရန်" + "မလုပ်တော့" + "ဘလူးတုသ်မှ မျှဝေလိုသော အကောင့်များကို ရွေးပါ။ ချိတ်ဆက်သည့်အခါ အကောင့်များအား ဝင်ခွင့်ပြုဖို့ လက်ခံပေးရပါလိမ့်ဦးမည်။" + "ကျန်နေသည့် အပေါက်များ:" + "အပလီကေးရှင်း သင်္ကေတ" + "ဘလူးတုသ် စာ မျှဝေရေး ဆက်တင်များ" + "ရွေးထားသည့် အကောင့်ကို မရွေးနိုင်ပါ။ သုည အပေါက်များ ကျန်နေ" + "ဘလူးတုသ်အသံ ချိတ်ဆက်ပြီးပါပြီ" + "ဘလူးတုသ်အသံ မချိတ်ဆက်ထားပါ" + "ဘလူးတုသ် အသံ" + "4GB ထက်ပိုကြီးသည့် ဖိုင်များကို လွှဲပြောင်းမရနိုင်ပါ" + "ဘလူးတုသ်သို့ ချိတ်ဆက်ရန်" + diff --git a/android/app/res/values-my/strings_pbap.xml b/android/app/res/values-my/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..b202f1125a3b6e06e59e6e42b3db34f53a8a4915 --- /dev/null +++ b/android/app/res/values-my/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$sအတွက် ဆက်ရှင်ကီး တစ်ခုကို ရိုက်သွင်းပါ" + "ဘလူးတုသ် ဆက်ရှင်ကီး လိုအပ်ပါသည်" + "%1$sနှင့် ဆက်သွယ်မှုကိုလက်ခံရန် အချိန်ကုန်သွားပါသည်" + "%1$sနှင့် ဆက်ရှင်ကီးကို ရိုက်သွင်းရန်အချိန် ကုန်သွားပါသည်" + "Obex (အချင်းအချင်းဖလှယ်ရန်) စစ်မှန်ကြောင်းတောင်းခံခြင်း" + "ကဏ္ဏတစ်ခု၏ ကီး" + "%1$sအတွက် ဆက်ရှင်ကီးကို ရိုက်ပါ" + "ကားကစ်" + "အမည်မသိ" + "ကျွန်ုပ်နာမည်" + "၀၀၀၀၀၀" + "ဘလူးတုသ်ဖြင့် အဆက်အသွယ်မျှဝေခြင်း" + diff --git a/android/app/res/values-my/strings_pbap_client.xml b/android/app/res/values-my/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-my/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-my/strings_sap.xml b/android/app/res/values-my/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..189b9d3a8d1759968db8989610983b203a326d1d --- /dev/null +++ b/android/app/res/values-my/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ဘလူးတုသ် SIM ဝင်ရောက်သုံးမှု" + "ဘလူးတုသ် SIM ဝင်ရောက်သုံးမှု" + "ကလိုင်းယင့်အား ချိတ်ဆက်မှု ဖြုတ်ရန် တောင်းဆိုမည်လား?" + "ချိတ်ဆက်မှုဖြတ်ရန် ကလိုင်းယင့်များအား စောင့်ဆိုင်းနေစဉ်" + "ချိတ်ဆက်မှုဖြုတ်ရန်" + "ချိတ်ဆက်မှု မဖြစ်မနေ ဖြတ်တောက်ရန်" + diff --git a/android/app/res/values-my/test_strings.xml b/android/app/res/values-my/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e9cf314b5200b362c4486aa807b84865b5a41b7a --- /dev/null +++ b/android/app/res/values-my/test_strings.xml @@ -0,0 +1,13 @@ + + + "ဘလူးတုသ်" + "မှတ်တမ်း ထည့်သွင်းရန်" + "မှတ်တမ်းအတည်ပြုရန်" + "အသိအမှတ်ပြု မှတ်တမ်း" + "မှတ်တမ်းအားလုံးကို ဖျက်ရန်" + "OK" + "မှတ်တမ်းကို ဖျက်ရန်" + "TCP ဆာဗာ စတင်ရန်" + "TCP ဆာဗာကို အကြောင်းကြားရန်" + diff --git a/android/app/res/values-nb/config.xml b/android/app/res/values-nb/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-nb/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-nb/strings.xml b/android/app/res/values-nb/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b62b6eb4cd1c82fde58819285b3d22c83910c32e --- /dev/null +++ b/android/app/res/values-nb/strings.xml @@ -0,0 +1,137 @@ + + + + + "Åpne nedlastingsbehandling." + "Gir appen tilgang til BluetoothShare-administratoren, og tillatelse til å bruke det til filoverføring." + "Godkjenn tilgang for Bluetooth-enheter." + "Appen gis tillatelse til å godkjenne en Bluetooth-enhet midlertidig, noe som gjør at Bluetooth-enheten kan sende filer til enheten du bruker, uten brukerbekreftelse." + "Bluetooth" + "Ukjent enhet" + "Ukjent" + "Flymodus" + "Du kan ikke bruke Bluetooth i flymodus." + + "Du må slå på Bluetooth for å bruke Bluetooth-tjenestene." + "Vil du slå på Bluetooth nå?" + "Avbryt" + "Slå på" + "Filoverføring" + "Vil du godta den innkommende filen?" + "Avslå" + "Godta" + "OK" + "Det oppstod et tidsavbrudd under mottak av fil fra «%1$s»" + "Innkommende fil" + "%1$s er klar til å sende %2$s" + "Bluetooth-deling: Mottar %1$s" + "Bluetooth-deling: %1$s er mottatt" + "Bluetooth-deling: Filen %1$s er ikke mottatt" + "Bluetooth-deling: Sender %1$s" + "Bluetooth-deling: Sendt %1$s" + "100 % fullført" + "Bluetooth-deling: Filen %1$s ble ikke sendt" + "Filoverføring" + "Fra: «%1$s»" + "Fil: %1$s" + "Filstørrelse: %1$s" + + "Mottar fil ..." + "Stopp" + "Skjul" + "Fra" + "Filnavn" + "Størrelse" + "Filen ble ikke mottatt" + "Fil: %1$s" + "Årsak: %1$s" + "OK" + "Fil mottatt" + "Åpne" + "Til: «%1$s»" + "Filtype: %1$s (%2$s)" + "Sender filen ..." + "Fil sendt" + "OK" + "Filen ble ikke sendt til «%1$s»." + "Fil: %1$s" + "Lukk" + "OK" + "Ukjent fil" + "Det finnes ingen app på enheten som kan håndtere denne filtypen. \n" + "Ingen fil" + "Filen finnes ikke. \n" + "Vent litt ..." + "Aktiverer Bluetooth …" + "Filen vil bli mottatt. Du kan se fremdriften i varselpanelet." + "Filen kan ikke mottas." + "Stoppet mottak av fil fra «%1$s»" + "Sender fil til «%1$s»" + "Sender %1$s filer til «%2$s»" + "Stoppet sendingen av fil til «%1$s»" + "Det er ikke nok plass på USB-lagringen til å lagre filen." + "Det er ikke nok plass på SD-kortet til å lagre filen." + "Nødvendig plass: %1$s" + "For mange forespørsler behandles for øyeblikket. Prøv på nytt senere." + "Filoverføringen har ikke startet ennå." + "Filoverføring pågår." + "Filoverføring fullført." + "Innholdet støttes ikke." + "Overføring nektes av mottakerenheten." + "Overføring avbrutt av bruker." + "Problem med lagring." + "Ingen USB-lagring." + "Mangler SD-kort. Sett inn et SD-kort for å lagre overførte filer." + "Tilkobling mislyktes." + "Kan ikke behandle forespørsel på riktig måte." + "Ukjent feil." + "Bluetooth mottatt" + "Bluetooth-deling" + "%1$s mottatt – ferdig." + "%1$s sendt." + "Innkommende overføringer" + "Utgående overføringer" + "Overføringsloggen er tom." + "Alle elementer fjernes fra listen." + "Bluetooth-deling: Filer sendt" + "Bluetooth-deling: Filer mottatt" + + %1$d mislyktes. + %1$d mislyktes. + + + %1$d vellykket, %2$s + %1$d vellykket, %2$s + + "Tøm listen" + "Åpne" + "Fjern fra listen" + "Tøm" + "Spilles nå" + "Lagre" + "Avbryt" + "Velg kontoene du vil dele via Bluetooth. Du må fortsatt godta eventuell tilgang til kontoene når du kobler til." + "Plasser igjen:" + "Appikon" + "Innstillinger for meldingsdeling via Bluetooth" + "Kan ikke velge kontoen. Det er 0 plasser igjen" + "Bluetooth-lyd er tilkoblet" + "Bluetooth-lyd er frakoblet" + "Bluetooth-lyd" + "Filer som er større enn 4 GB, kan ikke overføres" + "Koble til Bluetooth" + diff --git a/android/app/res/values-nb/strings_pbap.xml b/android/app/res/values-nb/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..c49b2ba03f2b3fe0fa7fd45bd54cac142127db06 --- /dev/null +++ b/android/app/res/values-nb/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "Skriv inn øktsnøkkel for %1$s" + "Bluetooth-øktsnøkkel kreves" + "Det·tok·for·lang·tid·å·godta·forbindelsen·med·«%1$s»" + "Det·tok·for·lang·tid·å·legge·inn·øktsnøkkel·med·%1$s" + "Autentiseringsforespørsel fra Obex" + "Øktsnøkkel" + "Skriv inn øktsnøkkel for %1$s" + "Bilsett" + "Ukjent navn" + "Navnet mitt" + "000000" + "Bluetooth Contact-deling" + diff --git a/android/app/res/values-nb/strings_pbap_client.xml b/android/app/res/values-nb/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-nb/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-nb/strings_sap.xml b/android/app/res/values-nb/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..2e99f843caff1fc3a125c9f952ac4b83581ad0fc --- /dev/null +++ b/android/app/res/values-nb/strings_sap.xml @@ -0,0 +1,10 @@ + + + "Bluetooth SIM access" + "Bluetooth SIM Access" + "Vil du be klienten om å koble fra?" + "Venter på at klienten skal koble fra" + "Koble fra" + "Tving frakobling" + diff --git a/android/app/res/values-nb/test_strings.xml b/android/app/res/values-nb/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..cf3c95a88472b4964200abc8b4b54990ec754f04 --- /dev/null +++ b/android/app/res/values-nb/test_strings.xml @@ -0,0 +1,13 @@ + + + "Bluetooth" + "Sett inn oppføring" + "Bekreft oppføring" + "Ack-oppføring" + "Slett hele oppføringen" + "OK" + "Slett oppføring" + "Start TPC-tjener" + "Varsle TCP-tjener" + diff --git a/android/app/res/values-ne/config.xml b/android/app/res/values-ne/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f96544b67afeaba2c5e363d2f65e01bd9e11263 --- /dev/null +++ b/android/app/res/values-ne/config.xml @@ -0,0 +1,19 @@ + + + + + "com.android.settings" + diff --git a/android/app/res/values-ne/strings.xml b/android/app/res/values-ne/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd48fb1eb39e6ae521508e99d3bc2f79593d4457 --- /dev/null +++ b/android/app/res/values-ne/strings.xml @@ -0,0 +1,137 @@ + + + + + "डाउनलोड म्यानेजर पहुँच गर्नुहोस्।" + "एपलाई ब्लुटुथसाझेदारी प्रबन्धक पहुँचको अनुमति दिन्छ र यसलाई फाइलहरू स्थानान्तरण गर्न प्रयोग गर्दछ।" + "ब्लुटूथ चल्ने यन्त्रको एक्सेस श्वेतसूचीमा राख्नुहोस्।" + "यो सेटिङले यस एपलाई केही समयका लागि ब्लुटुथ चल्ने कुनै यन्त्र श्वेतसूचीमा राख्ने अनुमति दिन्छ। यस प्रकार श्वेतसूचीमा राखिएको उक्त यन्त्रले प्रयोगकर्ताको अनुमति नलिइकन यो डिभाइसमा फाइलहरू पठाउन सक्छ।" + "ब्लुटुथ" + "अज्ञात उपकरण" + "अज्ञात" + "हवाइजहाज मोड" + "हवाइजहाज मोडमा तपाईँले ब्लुटुथ प्रयोग गर्न पाउनु हुन्न।" + + "ब्लुटुथ सेवाहरू चलाउन तपाईँले पहिले ब्लुटुथ सक्रिय पार्नु पर्छ।" + "अहिले ब्लुटुथ सक्रिय पार्ने हो?" + "रद्द गर्नुहोस्" + "सक्रिय पार्नुहोस्" + "फाइल स्थानान्तरण" + "आगमन फाइल स्वीकार गर्नुहुन्छ?" + "अस्वीकार गर्नुहोस्" + "स्वीकार्नुहोस्" + "ठिक छ" + "\"%1$s\" बाट आगमन फाइल स्वीकार्दा समय सकिएको थियो" + "आगमन फाइल" + "%1$s ले %2$s पठाउन तयार छ" + "ब्लुटुथ साझेदारी:%1$s प्राप्त गर्दै" + "ब्लुटुथ साझेदारी: %1$s प्राप्त भयो" + "ब्लुटुथ साझेदारी: फाइल: %1$s प्राप्त भएन" + "ब्लुटुथ साझेदारी: %1$s पठाउँदै" + "ब्लुटुथ साझेदारी: %1$s पठाइयो" + "१००% पुरा" + "ब्लुटुथ साझेदारी: फाइल%1$s पठाइएन" + "फाइल स्थानान्तरण" + "बाट: \"%1$s\"" + "फाइल: %1$s" + "फाइलको आकार: %1$s" + + "फाइल प्राप्त गर्दै..." + "रोक्नुहोस्" + "लुकाउनुहोस्" + "प्रेषक" + "फाइलको नाम" + "आकार" + "फाइल प्राप्त भएन" + "फाइल: %1$s" + "कारण: %1$s" + "ठिक छ" + "फाइल प्राप्त भयो" + "खोल्नुहोस्" + "लाई:\"%1$s \"" + "फाइल प्रकार: %1$s ( %2$s )" + "फाइल पठाउँदै..." + "फाइल पठाइयो" + "ठिक छ" + "फाइल\"%1$s\" लाई पठाइएन।" + "फाइल: %1$s" + "बन्द गर्नुहोस्" + "ठिक छ" + "अज्ञात फाइल" + "यस प्रकारको फाइल सम्हालन कुनै एप छैन।\n" + "फाइल छैन" + "फाइल अवस्थित छैन। \n" + "कृपया प्रतीक्षा गर्नुहोस्..." + "ब्लुटुथलाई सक्रिय पार्दै..." + "फाइल प्राप्त गरिने छ। सूचना प्यानलमा प्रगति जाँच गर्नुहोस्।" + "फाइल प्राप्त गर्न सकिँदैन।" + "\"%1$s \"बाट फाइल प्राप्त गर्न बन्द भयो" + "\"%1$s\"लाई फाइल पठाउँदै" + "%1$s फाइल \"%2$s\"लाई पठाउँदै" + "\"%1$s \"लाई फाइल पठाउन बन्द भयो" + "USB भण्डारणमा यो फाइल सुरक्षित गर्न पुग्ने ठाउँ छैन।" + "SD कार्डमा यो फाइल सुरक्षित गर्न पुग्ने ठाउँ छैन।" + "ठाउँ चाहियो: %1$s" + "निकै धेरै अनुरोधहरू प्रसोधिन भइरहेका छन्। पछि फेरि प्रयास गर्नुहोस्।" + "फाइल स्थानान्तरण अहिलेसम्म सुरु भएको छैन।" + "फाइल स्थानान्तरण चलिरहेको छ।" + "फाइल स्थानान्तरण सफलतापूर्वक समाप्त भयो।" + "सामग्री समर्थित छैन।" + "स्थानान्तरण लक्षित उपकरणद्वारा निषेध गरिएको छ।" + "स्थानान्तरण प्रयोगकर्ताद्वारा रद्द गरियो।" + "भण्डारण सवाल।" + "कुनै पनि USB भण्डारण छैन।" + "कुनै पनि SD कार्ड छैन। स्थानान्तरण गरिएका फाइलहरू सुरक्षित गर्न SD कार्ड छिराउनुहोस्।" + "जडान असफल।" + "अनुरोधलाई सही रूपमा सम्हाल्न सकिँदैन।" + "अज्ञात त्रुटि।" + "ब्लुटुथबाट प्राप्त भएको" + "ब्लुटुथमार्फत गरिने आदान प्रदान" + "%1$s प्राप्त गर्ने कार्य सम्पन्न भयो।" + "%1$s पठाउने कार्य पुरा भयो।" + "इनबाउन्ड स्थानान्तरण" + "आउटबाउन्ड स्थानान्तरण" + "अहिलेसम्म कुनै वस्तु ट्रान्सफर गरिएको छैन।" + "सूचीबाट सम्पूर्ण वस्तुहरू मेटाइने छन्।" + "ब्लुटुथ साझेदारी: पठाइएका फाइलहरू" + "ब्लुटुथ साझेदारी: प्राप्त फाइलहरू" + + %1$d असफल। + %1$d असफल। + + + %1$d सफल, %2$s + %1$d सफल, %2$s + + "सूची हटाउनुहोस्" + "खोल्नुहोस्" + "सूचीबाट हटाउनुहोस्" + "हटाउनुहोस्" + "Now Playing" + "सेभ गर्नुहोस्" + "रद्द गर्नुहोस्" + "तपाईंले ब्लुटुथ मार्फत साझेदारी गर्न चाहेका खाताहरू चयन गर्नुहोस्। तपाईंले अझै पनि खाताहरूमा जडान गर्दा कुनै पनि पहुँच स्वीकार गर्नुपर्छ।" + "स्लटहरू बाँकी:" + "एप आइकन" + "ब्लुटुथ सन्देश साझेदारी सेटिङहरू" + "खाता चयन गर्न सक्दैन। ० स्लटहरू बाँकी" + "ब्लुटुथ सम्बन्धी अडियो यन्त्रलाई जडान गरियो" + "ब्लुटुथ सम्बन्धी अडियो यन्त्रलाई विच्छेद गरियो" + "ब्लुटुथको अडियो" + "४ जि.बि. भन्दा ठूला फाइलहरूलाई स्थानान्तरण गर्न सकिँदैन" + "ब्लुटुथमा कनेक्ट गर्नुहोस्" + diff --git a/android/app/res/values-ne/strings_pbap.xml b/android/app/res/values-ne/strings_pbap.xml new file mode 100644 index 0000000000000000000000000000000000000000..8d3167e25f8378f9e08316cf8d3a25f44ad0e187 --- /dev/null +++ b/android/app/res/values-ne/strings_pbap.xml @@ -0,0 +1,16 @@ + + + "%1$sका लागि सत्र कुञ्जी टाइप गर्नुहोस्" + "ब्लुटुथ सत्र कुञ्जी आवाश्यक" + "समय %1$sका साथ जडान मञ्जुर गर्न समय सकियो" + "%1$sका साथ सत्र कुञ्जी इनपुट गर्न समय सकियो" + "Obex प्रमाणीकरण अनुरोध" + "सत्र कुञ्जी" + "%1$s का लागि सत्र कुञ्जी टाइप गर्नुहोस्" + "कारकिट" + "अज्ञात नाम" + "मेरो नाम" + "००००००" + "ब्लुटुथमार्फत सम्पर्कको आदान प्रदान" + diff --git a/android/app/res/values-ne/strings_pbap_client.xml b/android/app/res/values-ne/strings_pbap_client.xml new file mode 100644 index 0000000000000000000000000000000000000000..186b23ae38377ec40d77582e55d8944f665b7849 --- /dev/null +++ b/android/app/res/values-ne/strings_pbap_client.xml @@ -0,0 +1,5 @@ + + + "com.android.bluetooth.pbapsink" + diff --git a/android/app/res/values-ne/strings_sap.xml b/android/app/res/values-ne/strings_sap.xml new file mode 100644 index 0000000000000000000000000000000000000000..954869e87093af7869e2a4ff77c6d585fd71a3a7 --- /dev/null +++ b/android/app/res/values-ne/strings_sap.xml @@ -0,0 +1,10 @@ + + + "ब्लुटुथ SIM पहुँच" + "ब्लुटुथ SIM पहुँच" + "विच्छेदन गर्न ग्राहकलाई अनुरोध गर्ने हो?" + "ग्राहकको विच्छेदन प्रतीक्षा गर्दै" + "डिस्कनेक्ट गर्नुहोस्" + "बलपूर्वक डिस्कनेक्ट गर्नुहोस्" + diff --git a/android/app/res/values-ne/test_strings.xml b/android/app/res/values-ne/test_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..df5a45feddf652904c027cce809812bc3ad97a15 --- /dev/null +++ b/android/app/res/values-ne/test_strings.xml @@ -0,0 +1,13 @@ + + + "ब्लुटुथ" + "रेकर्ड राख्नुहोस्" + "रेकर्ड निश्चित गर्नुहोस्" + "Ack रेकर्ड" + "सबै रेकर्ड मेटाउनुहोस्" + "ठिक छ" + "रेकर्ड मेटाउनुहोस्" + "TCP सर्भर सुरु गर्नुहोस्" + "TCP सर्भर सूचित गर्नुहोस्" + diff --git a/android/app/res/values-night/styles.xml b/android/app/res/values-night/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..a251ecb6a98e8400bbef38c2d28293065f40813c --- /dev/null +++ b/android/app/res/values-night/styles.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + +