Loading android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +193 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016-2017 The Linux Foundation * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); Loading Loading @@ -1854,6 +1855,186 @@ static jboolean isLogRedactionEnabled(JNIEnv* env, jobject obj) { return bluetooth::os::should_log_be_redacted(); } static jboolean interopMatchAddrNative(JNIEnv* env, jclass clazz, jstring feature_name, jstring address) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, NULL); if (!tmp_addr) { ALOGW("%s: address is null.", __func__); return JNI_FALSE; } RawAddress bdaddr; bool success = RawAddress::FromString(tmp_addr, bdaddr); env->ReleaseStringUTFChars(address, tmp_addr); if (!success) { ALOGW("%s: address is invalid.", __func__); return JNI_FALSE; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return JNI_FALSE; } bool matched = sBluetoothInterface->interop_match_addr(feature_name_str, &bdaddr); env->ReleaseStringUTFChars(feature_name, feature_name_str); return matched ? JNI_TRUE : JNI_FALSE; } static jboolean interopMatchNameNative(JNIEnv* env, jclass clazz, jstring feature_name, jstring name) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return JNI_FALSE; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return JNI_FALSE; } const char* name_str = env->GetStringUTFChars(name, NULL); if (!name_str) { ALOGW("%s: name is null.", __func__); env->ReleaseStringUTFChars(feature_name, feature_name_str); return JNI_FALSE; } bool matched = sBluetoothInterface->interop_match_name(feature_name_str, name_str); env->ReleaseStringUTFChars(feature_name, feature_name_str); env->ReleaseStringUTFChars(name, name_str); return matched ? JNI_TRUE : JNI_FALSE; } static jboolean interopMatchAddrOrNameNative(JNIEnv* env, jclass clazz, jstring feature_name, jstring address) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, NULL); if (!tmp_addr) { ALOGW("%s: address is null.", __func__); return JNI_FALSE; } RawAddress bdaddr; bool success = RawAddress::FromString(tmp_addr, bdaddr); env->ReleaseStringUTFChars(address, tmp_addr); if (!success) { ALOGW("%s: address is invalid.", __func__); return JNI_FALSE; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return JNI_FALSE; } bool matched = sBluetoothInterface->interop_match_addr_or_name( feature_name_str, &bdaddr); env->ReleaseStringUTFChars(feature_name, feature_name_str); return matched ? JNI_TRUE : JNI_FALSE; } static void interopDatabaseAddRemoveAddrNative(JNIEnv* env, jclass clazz, jboolean do_add, jstring feature_name, jstring address, jint length) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return; } if ((do_add == JNI_TRUE) && (length <= 0 || length > 6)) { ALOGE("%s: address length %d is invalid, valid length is [1,6]", __func__, length); return; } const char* tmp_addr = env->GetStringUTFChars(address, NULL); if (!tmp_addr) { ALOGW("%s: address is null.", __func__); return; } RawAddress bdaddr; bool success = RawAddress::FromString(tmp_addr, bdaddr); env->ReleaseStringUTFChars(address, tmp_addr); if (!success) { ALOGW("%s: address is invalid.", __func__); return; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return; } sBluetoothInterface->interop_database_add_remove_addr( (do_add == JNI_TRUE), feature_name_str, &bdaddr, (int)length); env->ReleaseStringUTFChars(feature_name, feature_name_str); } static void interopDatabaseAddRemoveNameNative(JNIEnv* env, jclass clazz, jboolean do_add, jstring feature_name, jstring name) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return; } const char* name_str = env->GetStringUTFChars(name, NULL); if (!name_str) { ALOGW("%s: name is null.", __func__); env->ReleaseStringUTFChars(feature_name, feature_name_str); return; } sBluetoothInterface->interop_database_add_remove_name( (do_add == JNI_TRUE), feature_name_str, name_str); env->ReleaseStringUTFChars(feature_name, feature_name_str); env->ReleaseStringUTFChars(name, name_str); } static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, Loading Loading @@ -1898,6 +2079,18 @@ static JNINativeMethod sMethods[] = { {"allowLowLatencyAudioNative", "(Z[B)Z", (void*)allowLowLatencyAudioNative}, {"metadataChangedNative", "([BI[B)V", (void*)metadataChangedNative}, {"isLogRedactionEnabled", "()Z", (void*)isLogRedactionEnabled}, {"interopMatchAddrNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)interopMatchAddrNative}, {"interopMatchNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)interopMatchNameNative}, {"interopMatchAddrOrNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)interopMatchAddrOrNameNative}, {"interopDatabaseAddRemoveAddrNative", "(ZLjava/lang/String;Ljava/lang/String;I)V", (void*)interopDatabaseAddRemoveAddrNative}, {"interopDatabaseAddRemoveNameNative", "(ZLjava/lang/String;Ljava/lang/String;)V", (void*)interopDatabaseAddRemoveNameNative}, }; int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { Loading android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ public final class AbstractionLayer { static final int BT_PROPERTY_DYNAMIC_AUDIO_BUFFER = 0x10; static final int BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER = 0x11; static final int BT_PROPERTY_WL_MEDIA_PLAYERS_LIST = 0x14; public static final int BT_DEVICE_TYPE_BREDR = 0x01; public static final int BT_DEVICE_TYPE_BLE = 0x02; Loading android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * Copyright (C) 2016-2017 The Linux Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading Loading @@ -96,6 +97,8 @@ class AdapterProperties { private static final int SCAN_MODE_CHANGES_MAX_SIZE = 10; private EvictingQueue<String> mScanModeChanges; private CopyOnWriteArrayList<String> mAllowlistedPlayers = new CopyOnWriteArrayList<String>(); private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState = Loading Loading @@ -263,6 +266,7 @@ class AdapterProperties { mBondedDevices.clear(); mScanModeChanges.clear(); invalidateBluetoothCaches(); mAllowlistedPlayers.clear(); } private static void invalidateGetProfileConnectionStateCache() { Loading Loading @@ -700,6 +704,24 @@ class AdapterProperties { } } /** * @return the mAllowlistedPlayers */ String[] getAllowlistedMediaPlayers() { String[] AllowlistedPlayersList = new String[0]; try { AllowlistedPlayersList = mAllowlistedPlayers.toArray(AllowlistedPlayersList); } catch (ArrayStoreException ee) { errorLog("Error retrieving Allowlisted Players array"); } Log.d(TAG, "getAllowlistedMediaPlayers: numAllowlistedPlayers = " + AllowlistedPlayersList.length); for (int i = 0; i < AllowlistedPlayersList.length; i++) { Log.d(TAG, "players :" + AllowlistedPlayersList[i]); } return AllowlistedPlayersList; } long discoveryEndMillis() { return mDiscoveryEndMs; } Loading Loading @@ -911,6 +933,15 @@ class AdapterProperties { } } void updateAllowlistedMediaPlayers(String playername) { Log.d(TAG, "updateAllowlistedMediaPlayers "); if (!mAllowlistedPlayers.contains(playername)) { Log.d(TAG, "Adding to Allowlisted Players list:" + playername); mAllowlistedPlayers.add(playername); } } @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) void adapterPropertyChangedCallback(int[] types, byte[][] values) { Intent intent; Loading Loading @@ -999,6 +1030,23 @@ class AdapterProperties { debugLog("mLocalIOCapabilityBLE set to " + mLocalIOCapabilityBLE); break; case AbstractionLayer.BT_PROPERTY_WL_MEDIA_PLAYERS_LIST: int pos = 0; for (int j = 0; j < val.length; j++) { if (val[j] != 0) { continue; } int name_len = j - pos; byte[] buf = new byte[name_len]; System.arraycopy(val, pos, buf, 0, name_len); String player_name = new String(buf, 0, name_len); Log.d(TAG, "player_name: " + player_name); updateAllowlistedMediaPlayers(player_name); pos += (name_len + 1); } break; default: errorLog("Property change not handled in Java land:" + type); } Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +43 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * Copyright (C) 2016-2017 The Linux Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading Loading @@ -108,6 +109,7 @@ import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.bas.BatteryService; import com.android.bluetooth.bass_client.BassClientService; import com.android.bluetooth.btservice.InteropUtil.InteropFeature; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; import com.android.bluetooth.btservice.activityattribution.ActivityAttributionService; import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreService; Loading Loading @@ -5468,6 +5470,10 @@ public class AdapterService extends Service { return mAdapterProperties.isA2dpOffloadEnabled(); } public String[] getAllowlistedMediaPlayers() { return mAdapterProperties.getAllowlistedMediaPlayers(); } @VisibleForTesting BluetoothActivityEnergyInfo reportActivityInfo() { if (mAdapterProperties.getState() != BluetoothAdapter.STATE_ON Loading Loading @@ -6246,6 +6252,35 @@ public class AdapterService extends Service { mRemoteDevices.updateBatteryLevel(device, batteryLevel); } public boolean interopMatchAddr(InteropFeature feature, String address) { return interopMatchAddrNative(feature.name(), address); } public boolean interopMatchName(InteropFeature feature, String name) { return interopMatchNameNative(feature.name(), name); } public boolean interopMatchAddrOrName(InteropFeature feature, String address) { return interopMatchAddrOrNameNative(feature.name(), address); } public void interopDatabaseAddAddr(InteropFeature feature, String address, int length) { interopDatabaseAddRemoveAddrNative(true, feature.name(), address, length); } public void interopDatabaseRemoveAddr(InteropFeature feature, String address) { interopDatabaseAddRemoveAddrNative(false, feature.name(), address, 0); } public void interopDatabaseAddName(InteropFeature feature, String name) { interopDatabaseAddRemoveNameNative(true, feature.name(), name); } public void interopDatabaseRemoveName(InteropFeature feature, String name) { interopDatabaseAddRemoveNameNative(false, feature.name(), name); } static native void classInitNative(); native boolean initNative(boolean startRestricted, boolean isCommonCriteriaMode, Loading Loading @@ -6343,6 +6378,14 @@ public class AdapterService extends Service { private native void metadataChangedNative(byte[] address, int key, byte[] value); private native boolean interopMatchAddrNative(String featureName, String address); private native boolean interopMatchNameNative(String featureName, String name); private native boolean interopMatchAddrOrNameNative(String featureName, String address); private native void interopDatabaseAddRemoveAddrNative(boolean doAdd, String featureName, String address, int length); private native void interopDatabaseAddRemoveNameNative(boolean doAdd, String featureBame, String name); // Returns if this is a mock object. This is currently used in testing so that we may not call // System.exit() while finalizing the object. Otherwise GC of mock objects unfortunately ends up // calling finalize() which in turn calls System.exit() and the process crashes. Loading android/app/src/com/android/bluetooth/btservice/InteropUtil.java 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * Copyright (c) 2022 The Android Open Source Project * Copyright (c) 2020 The Linux Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.btservice; import android.util.Log; /** * APIs of interoperability workaround utilities. * These APIs will call stack layer's interop APIs of interop.cc to do matching * or entry adding/removing. */ public class InteropUtil { private static final String TAG = "InteropUtil"; /** * Add interop feature from device/include/interop.h to below InteropFeature if * this feature needs to be matched at java layer. Feature's name will be passed to * stack layer to do matching, so make sure that the added feature's name is exactly * same as that in device/include/interop.h. */ public enum InteropFeature { INTEROP_NOT_UPDATE_AVRCP_PAUSED_TO_REMOTE, INTEROP_PHONE_POLICY_INCREASED_DELAY_CONNECT_OTHER_PROFILES, INTEROP_PHONE_POLICY_REDUCED_DELAY_CONNECT_OTHER_PROFILES, INTEROP_HFP_FAKE_INCOMING_CALL_INDICATOR, INTEROP_HFP_SEND_CALL_INDICATORS_BACK_TO_BACK, INTEROP_SETUP_SCO_WITH_NO_DELAY_AFTER_SLC_DURING_CALL, INTEROP_RETRY_SCO_AFTER_REMOTE_REJECT_SCO; } /** * Check if a given address matches a known interoperability workaround * identified by the interop feature. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be matched. * @return true if matched, false otherwise. */ public static boolean interopMatchAddr(InteropFeature feature, String address) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopMatchAddr: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return false; } Log.d(TAG, "interopMatchAddr: feature=" + feature.name() + ", address=" + address); if (address == null) { return false; } boolean matched = adapterService.interopMatchAddr(feature, address); Log.d(TAG, "interopMatchAddr: matched=" + matched); return matched; } /** * Check if a given name matches a known interoperability workaround * identified by the interop feature. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param name a given name to be matched. * @return true if matched, false otherwise. */ public static boolean interopMatchName(InteropFeature feature, String name) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopMatchName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return false; } Log.d(TAG, "interopMatchName: feature=" + feature.name() + ", name=" + name); if (name == null) { return false; } boolean matched = adapterService.interopMatchName(feature, name); Log.d(TAG, "interopMatchName: matched=" + matched); return matched; } /** * Check if a given address or remote device name matches a known interoperability workaround * identified by the interop feature. remote device name will be fetched internally based on * the given address at stack layer. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be matched. * @return true if matched, false otherwise */ public static boolean interopMatchAddrOrName(InteropFeature feature, String address) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopMatchAddrOrName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return false; } Log.d(TAG, "interopMatchAddrOrName: feature=" + feature.name() + ", address=" + address); if (address == null) { return false; } boolean matched = adapterService.interopMatchAddrOrName(feature, address); Log.d(TAG, "interopMatchAddrOrName: matched=" + matched); return matched; } /** * Add a dynamic address interop database entry identified by the interop feature * for a device matching the first length bytes of addr. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be added. * @param length the number of bytes of address to be stored, * length must be in [1,6], and usually it is 3. */ public static void interopDatabaseAddAddr(InteropFeature feature, String address, int length) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseAddAddr: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseAddAddr: feature=" + feature.name() + ", address=" + address + ", length=" + length); if (address == null || (length <= 0 || length > 6)) { return; } adapterService.interopDatabaseAddAddr(feature, address, length); } /** * Remove a dynamic address interop database entry identified by the interop feature * for a device matching the addr. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be removed. */ public static void interopDatabaseRemoveAddr(InteropFeature feature, String address) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseRemoveAddr: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseRemoveAddr: feature=" + feature.name() + ", address=" + address); if (address == null) { return; } adapterService.interopDatabaseRemoveAddr(feature, address); } /** * Add a dynamic name interop database entry identified by the interop feature for the name. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param name a given name to be added. */ public static void interopDatabaseAddName(InteropFeature feature, String name) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseAddName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseAddName: feature=" + feature.name() + ", name=" + name); if (name == null) { return; } adapterService.interopDatabaseAddName(feature, name); } /** * Remove a dynamic name interop database entry identified by the interop feature for the name. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param name a given name to be removed. */ public static void interopDatabaseRemoveName(InteropFeature feature, String name) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseRemoveName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseRemoveName: feature=" + feature.name() + ", name=" + name); if (name == null) { return; } adapterService.interopDatabaseRemoveName(feature, name); } } Loading
android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +193 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016-2017 The Linux Foundation * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); Loading Loading @@ -1854,6 +1855,186 @@ static jboolean isLogRedactionEnabled(JNIEnv* env, jobject obj) { return bluetooth::os::should_log_be_redacted(); } static jboolean interopMatchAddrNative(JNIEnv* env, jclass clazz, jstring feature_name, jstring address) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, NULL); if (!tmp_addr) { ALOGW("%s: address is null.", __func__); return JNI_FALSE; } RawAddress bdaddr; bool success = RawAddress::FromString(tmp_addr, bdaddr); env->ReleaseStringUTFChars(address, tmp_addr); if (!success) { ALOGW("%s: address is invalid.", __func__); return JNI_FALSE; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return JNI_FALSE; } bool matched = sBluetoothInterface->interop_match_addr(feature_name_str, &bdaddr); env->ReleaseStringUTFChars(feature_name, feature_name_str); return matched ? JNI_TRUE : JNI_FALSE; } static jboolean interopMatchNameNative(JNIEnv* env, jclass clazz, jstring feature_name, jstring name) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return JNI_FALSE; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return JNI_FALSE; } const char* name_str = env->GetStringUTFChars(name, NULL); if (!name_str) { ALOGW("%s: name is null.", __func__); env->ReleaseStringUTFChars(feature_name, feature_name_str); return JNI_FALSE; } bool matched = sBluetoothInterface->interop_match_name(feature_name_str, name_str); env->ReleaseStringUTFChars(feature_name, feature_name_str); env->ReleaseStringUTFChars(name, name_str); return matched ? JNI_TRUE : JNI_FALSE; } static jboolean interopMatchAddrOrNameNative(JNIEnv* env, jclass clazz, jstring feature_name, jstring address) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, NULL); if (!tmp_addr) { ALOGW("%s: address is null.", __func__); return JNI_FALSE; } RawAddress bdaddr; bool success = RawAddress::FromString(tmp_addr, bdaddr); env->ReleaseStringUTFChars(address, tmp_addr); if (!success) { ALOGW("%s: address is invalid.", __func__); return JNI_FALSE; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return JNI_FALSE; } bool matched = sBluetoothInterface->interop_match_addr_or_name( feature_name_str, &bdaddr); env->ReleaseStringUTFChars(feature_name, feature_name_str); return matched ? JNI_TRUE : JNI_FALSE; } static void interopDatabaseAddRemoveAddrNative(JNIEnv* env, jclass clazz, jboolean do_add, jstring feature_name, jstring address, jint length) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return; } if ((do_add == JNI_TRUE) && (length <= 0 || length > 6)) { ALOGE("%s: address length %d is invalid, valid length is [1,6]", __func__, length); return; } const char* tmp_addr = env->GetStringUTFChars(address, NULL); if (!tmp_addr) { ALOGW("%s: address is null.", __func__); return; } RawAddress bdaddr; bool success = RawAddress::FromString(tmp_addr, bdaddr); env->ReleaseStringUTFChars(address, tmp_addr); if (!success) { ALOGW("%s: address is invalid.", __func__); return; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return; } sBluetoothInterface->interop_database_add_remove_addr( (do_add == JNI_TRUE), feature_name_str, &bdaddr, (int)length); env->ReleaseStringUTFChars(feature_name, feature_name_str); } static void interopDatabaseAddRemoveNameNative(JNIEnv* env, jclass clazz, jboolean do_add, jstring feature_name, jstring name) { ALOGV("%s", __func__); if (!sBluetoothInterface) { ALOGW("%s: sBluetoothInterface is null.", __func__); return; } const char* feature_name_str = env->GetStringUTFChars(feature_name, NULL); if (!feature_name_str) { ALOGW("%s: feature name is null.", __func__); return; } const char* name_str = env->GetStringUTFChars(name, NULL); if (!name_str) { ALOGW("%s: name is null.", __func__); env->ReleaseStringUTFChars(feature_name, feature_name_str); return; } sBluetoothInterface->interop_database_add_remove_name( (do_add == JNI_TRUE), feature_name_str, name_str); env->ReleaseStringUTFChars(feature_name, feature_name_str); env->ReleaseStringUTFChars(name, name_str); } static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, Loading Loading @@ -1898,6 +2079,18 @@ static JNINativeMethod sMethods[] = { {"allowLowLatencyAudioNative", "(Z[B)Z", (void*)allowLowLatencyAudioNative}, {"metadataChangedNative", "([BI[B)V", (void*)metadataChangedNative}, {"isLogRedactionEnabled", "()Z", (void*)isLogRedactionEnabled}, {"interopMatchAddrNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)interopMatchAddrNative}, {"interopMatchNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)interopMatchNameNative}, {"interopMatchAddrOrNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)interopMatchAddrOrNameNative}, {"interopDatabaseAddRemoveAddrNative", "(ZLjava/lang/String;Ljava/lang/String;I)V", (void*)interopDatabaseAddRemoveAddrNative}, {"interopDatabaseAddRemoveNameNative", "(ZLjava/lang/String;Ljava/lang/String;)V", (void*)interopDatabaseAddRemoveNameNative}, }; int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { Loading
android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ public final class AbstractionLayer { static final int BT_PROPERTY_DYNAMIC_AUDIO_BUFFER = 0x10; static final int BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER = 0x11; static final int BT_PROPERTY_WL_MEDIA_PLAYERS_LIST = 0x14; public static final int BT_DEVICE_TYPE_BREDR = 0x01; public static final int BT_DEVICE_TYPE_BLE = 0x02; Loading
android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * Copyright (C) 2016-2017 The Linux Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading Loading @@ -96,6 +97,8 @@ class AdapterProperties { private static final int SCAN_MODE_CHANGES_MAX_SIZE = 10; private EvictingQueue<String> mScanModeChanges; private CopyOnWriteArrayList<String> mAllowlistedPlayers = new CopyOnWriteArrayList<String>(); private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState = Loading Loading @@ -263,6 +266,7 @@ class AdapterProperties { mBondedDevices.clear(); mScanModeChanges.clear(); invalidateBluetoothCaches(); mAllowlistedPlayers.clear(); } private static void invalidateGetProfileConnectionStateCache() { Loading Loading @@ -700,6 +704,24 @@ class AdapterProperties { } } /** * @return the mAllowlistedPlayers */ String[] getAllowlistedMediaPlayers() { String[] AllowlistedPlayersList = new String[0]; try { AllowlistedPlayersList = mAllowlistedPlayers.toArray(AllowlistedPlayersList); } catch (ArrayStoreException ee) { errorLog("Error retrieving Allowlisted Players array"); } Log.d(TAG, "getAllowlistedMediaPlayers: numAllowlistedPlayers = " + AllowlistedPlayersList.length); for (int i = 0; i < AllowlistedPlayersList.length; i++) { Log.d(TAG, "players :" + AllowlistedPlayersList[i]); } return AllowlistedPlayersList; } long discoveryEndMillis() { return mDiscoveryEndMs; } Loading Loading @@ -911,6 +933,15 @@ class AdapterProperties { } } void updateAllowlistedMediaPlayers(String playername) { Log.d(TAG, "updateAllowlistedMediaPlayers "); if (!mAllowlistedPlayers.contains(playername)) { Log.d(TAG, "Adding to Allowlisted Players list:" + playername); mAllowlistedPlayers.add(playername); } } @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) void adapterPropertyChangedCallback(int[] types, byte[][] values) { Intent intent; Loading Loading @@ -999,6 +1030,23 @@ class AdapterProperties { debugLog("mLocalIOCapabilityBLE set to " + mLocalIOCapabilityBLE); break; case AbstractionLayer.BT_PROPERTY_WL_MEDIA_PLAYERS_LIST: int pos = 0; for (int j = 0; j < val.length; j++) { if (val[j] != 0) { continue; } int name_len = j - pos; byte[] buf = new byte[name_len]; System.arraycopy(val, pos, buf, 0, name_len); String player_name = new String(buf, 0, name_len); Log.d(TAG, "player_name: " + player_name); updateAllowlistedMediaPlayers(player_name); pos += (name_len + 1); } break; default: errorLog("Property change not handled in Java land:" + type); } Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +43 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * Copyright (C) 2016-2017 The Linux Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading Loading @@ -108,6 +109,7 @@ import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.bas.BatteryService; import com.android.bluetooth.bass_client.BassClientService; import com.android.bluetooth.btservice.InteropUtil.InteropFeature; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; import com.android.bluetooth.btservice.activityattribution.ActivityAttributionService; import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreService; Loading Loading @@ -5468,6 +5470,10 @@ public class AdapterService extends Service { return mAdapterProperties.isA2dpOffloadEnabled(); } public String[] getAllowlistedMediaPlayers() { return mAdapterProperties.getAllowlistedMediaPlayers(); } @VisibleForTesting BluetoothActivityEnergyInfo reportActivityInfo() { if (mAdapterProperties.getState() != BluetoothAdapter.STATE_ON Loading Loading @@ -6246,6 +6252,35 @@ public class AdapterService extends Service { mRemoteDevices.updateBatteryLevel(device, batteryLevel); } public boolean interopMatchAddr(InteropFeature feature, String address) { return interopMatchAddrNative(feature.name(), address); } public boolean interopMatchName(InteropFeature feature, String name) { return interopMatchNameNative(feature.name(), name); } public boolean interopMatchAddrOrName(InteropFeature feature, String address) { return interopMatchAddrOrNameNative(feature.name(), address); } public void interopDatabaseAddAddr(InteropFeature feature, String address, int length) { interopDatabaseAddRemoveAddrNative(true, feature.name(), address, length); } public void interopDatabaseRemoveAddr(InteropFeature feature, String address) { interopDatabaseAddRemoveAddrNative(false, feature.name(), address, 0); } public void interopDatabaseAddName(InteropFeature feature, String name) { interopDatabaseAddRemoveNameNative(true, feature.name(), name); } public void interopDatabaseRemoveName(InteropFeature feature, String name) { interopDatabaseAddRemoveNameNative(false, feature.name(), name); } static native void classInitNative(); native boolean initNative(boolean startRestricted, boolean isCommonCriteriaMode, Loading Loading @@ -6343,6 +6378,14 @@ public class AdapterService extends Service { private native void metadataChangedNative(byte[] address, int key, byte[] value); private native boolean interopMatchAddrNative(String featureName, String address); private native boolean interopMatchNameNative(String featureName, String name); private native boolean interopMatchAddrOrNameNative(String featureName, String address); private native void interopDatabaseAddRemoveAddrNative(boolean doAdd, String featureName, String address, int length); private native void interopDatabaseAddRemoveNameNative(boolean doAdd, String featureBame, String name); // Returns if this is a mock object. This is currently used in testing so that we may not call // System.exit() while finalizing the object. Otherwise GC of mock objects unfortunately ends up // calling finalize() which in turn calls System.exit() and the process crashes. Loading
android/app/src/com/android/bluetooth/btservice/InteropUtil.java 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * Copyright (c) 2022 The Android Open Source Project * Copyright (c) 2020 The Linux Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.btservice; import android.util.Log; /** * APIs of interoperability workaround utilities. * These APIs will call stack layer's interop APIs of interop.cc to do matching * or entry adding/removing. */ public class InteropUtil { private static final String TAG = "InteropUtil"; /** * Add interop feature from device/include/interop.h to below InteropFeature if * this feature needs to be matched at java layer. Feature's name will be passed to * stack layer to do matching, so make sure that the added feature's name is exactly * same as that in device/include/interop.h. */ public enum InteropFeature { INTEROP_NOT_UPDATE_AVRCP_PAUSED_TO_REMOTE, INTEROP_PHONE_POLICY_INCREASED_DELAY_CONNECT_OTHER_PROFILES, INTEROP_PHONE_POLICY_REDUCED_DELAY_CONNECT_OTHER_PROFILES, INTEROP_HFP_FAKE_INCOMING_CALL_INDICATOR, INTEROP_HFP_SEND_CALL_INDICATORS_BACK_TO_BACK, INTEROP_SETUP_SCO_WITH_NO_DELAY_AFTER_SLC_DURING_CALL, INTEROP_RETRY_SCO_AFTER_REMOTE_REJECT_SCO; } /** * Check if a given address matches a known interoperability workaround * identified by the interop feature. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be matched. * @return true if matched, false otherwise. */ public static boolean interopMatchAddr(InteropFeature feature, String address) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopMatchAddr: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return false; } Log.d(TAG, "interopMatchAddr: feature=" + feature.name() + ", address=" + address); if (address == null) { return false; } boolean matched = adapterService.interopMatchAddr(feature, address); Log.d(TAG, "interopMatchAddr: matched=" + matched); return matched; } /** * Check if a given name matches a known interoperability workaround * identified by the interop feature. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param name a given name to be matched. * @return true if matched, false otherwise. */ public static boolean interopMatchName(InteropFeature feature, String name) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopMatchName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return false; } Log.d(TAG, "interopMatchName: feature=" + feature.name() + ", name=" + name); if (name == null) { return false; } boolean matched = adapterService.interopMatchName(feature, name); Log.d(TAG, "interopMatchName: matched=" + matched); return matched; } /** * Check if a given address or remote device name matches a known interoperability workaround * identified by the interop feature. remote device name will be fetched internally based on * the given address at stack layer. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be matched. * @return true if matched, false otherwise */ public static boolean interopMatchAddrOrName(InteropFeature feature, String address) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopMatchAddrOrName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return false; } Log.d(TAG, "interopMatchAddrOrName: feature=" + feature.name() + ", address=" + address); if (address == null) { return false; } boolean matched = adapterService.interopMatchAddrOrName(feature, address); Log.d(TAG, "interopMatchAddrOrName: matched=" + matched); return matched; } /** * Add a dynamic address interop database entry identified by the interop feature * for a device matching the first length bytes of addr. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be added. * @param length the number of bytes of address to be stored, * length must be in [1,6], and usually it is 3. */ public static void interopDatabaseAddAddr(InteropFeature feature, String address, int length) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseAddAddr: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseAddAddr: feature=" + feature.name() + ", address=" + address + ", length=" + length); if (address == null || (length <= 0 || length > 6)) { return; } adapterService.interopDatabaseAddAddr(feature, address, length); } /** * Remove a dynamic address interop database entry identified by the interop feature * for a device matching the addr. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param address a given address to be removed. */ public static void interopDatabaseRemoveAddr(InteropFeature feature, String address) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseRemoveAddr: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseRemoveAddr: feature=" + feature.name() + ", address=" + address); if (address == null) { return; } adapterService.interopDatabaseRemoveAddr(feature, address); } /** * Add a dynamic name interop database entry identified by the interop feature for the name. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param name a given name to be added. */ public static void interopDatabaseAddName(InteropFeature feature, String name) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseAddName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseAddName: feature=" + feature.name() + ", name=" + name); if (name == null) { return; } adapterService.interopDatabaseAddName(feature, name); } /** * Remove a dynamic name interop database entry identified by the interop feature for the name. * * @param feature a given interop feature defined in {@link InteropFeature}. * @param name a given name to be removed. */ public static void interopDatabaseRemoveName(InteropFeature feature, String name) { AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService == null) { Log.d(TAG, "interopDatabaseRemoveName: feature=" + feature.name() + ", adapterService is null or vendor intf is not enabled"); return; } Log.d(TAG, "interopDatabaseRemoveName: feature=" + feature.name() + ", name=" + name); if (name == null) { return; } adapterService.interopDatabaseRemoveName(feature, name); } }