Loading android/app/src/com/android/bluetooth/btservice/MedicalDeviceBloomfilterGenerator.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.btservice; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** Class to generate a medical device bloomfilter */ public class MedicalDeviceBloomfilterGenerator { public static final String BLOOM_FILTER_DEFAULT = "01070000003C01002106584044800850055" + "002308488410020D9A00001138410510000" + "000042004200000000000C2000000040064" + "0120080020110412A500090520210040C40" + "4002601040005004400148414006198A041" + "00890000600400000800210041810600800" + "0142208000721A030000028102448201110" + "0002007120020101448C211490A2B000084" + "C010004004C00C080808200026210608110" + "200011200000015000000212C4400040802" + "00111114840000001002080040186000404" + "81C064400052381109017039900000200C9" + "C0002E6480000101C40000601064001A018" + "40440280A810001000040455A0404617034" + "50000140040D020020C6204100804041600" + "80840002000800804280028000440000122" + "00808409905022000590000110448080400" + "561004210020430092602000040C0090C00" + "C18480020000519C1482100111011120390" + "02C0000228208104800C050440000004040" + "00871400882400140080000005308124900" + "104000040002410508CA349000200000202" + "90200920181890100800110220A20874820" + "0428080054A0005101C0820060090080040" + "6820C480F40081014010201800000018101" + "208914100321008006520002030010800C4" + "1022C000048206002010041220000008021" + "002080314040000100030002008"; /** Provide byte[] version of a given string */ public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } /** Generate bloom filter file given filePath */ public static void generateDefaultBloomfilter(String filePath) throws IOException { File outputFile = new File(filePath); outputFile.createNewFile(); // if file already exists will do nothing FileOutputStream fos = new FileOutputStream(filePath); fos.write(hexStringToByteArray(BLOOM_FILTER_DEFAULT)); fos.close(); } } android/app/src/com/android/bluetooth/btservice/MetricsLogger.java +99 −12 Original line number Diff line number Diff line Loading @@ -96,7 +96,10 @@ public class MetricsLogger { private static final String TAG = "BluetoothMetricsLogger"; private static final String BLOOMFILTER_PATH = "/data/misc/bluetooth"; private static final String BLOOMFILTER_FILE = "/devices_for_metrics_v3"; private static final String MEDICAL_DEVICE_BLOOMFILTER_FILE = "/medical_devices_for_metrics_v1"; public static final String BLOOMFILTER_FULL_PATH = BLOOMFILTER_PATH + BLOOMFILTER_FILE; public static final String MEDICAL_DEVICE_BLOOMFILTER_FULL_PATH = BLOOMFILTER_PATH + MEDICAL_DEVICE_BLOOMFILTER_FILE; // 6 hours timeout for counter metrics private static final long BLUETOOTH_COUNTER_METRICS_ACTION_DURATION_MILLIS = 6L * 3600L * 1000L; Loading @@ -114,6 +117,10 @@ public class MetricsLogger { private BloomFilter<byte[]> mBloomFilter = null; protected boolean mBloomFilterInitialized = false; private BloomFilter<byte[]> mMedicalDeviceBloomFilter = null; protected boolean mMedicalDeviceBloomFilterInitialized = false; private AlarmManager.OnAlarmListener mOnAlarmListener = new AlarmManager.OnAlarmListener() { @Override Loading Loading @@ -185,10 +192,48 @@ public class MetricsLogger { return true; } /** Initialize medical device bloom filter */ public boolean initMedicalDeviceBloomFilter(String path) { try { File medicalDeviceFile = new File(path); if (!medicalDeviceFile.exists()) { Log.w(TAG, "MetricsLogger is creating a new medical device Bloomfilter file"); MedicalDeviceBloomfilterGenerator.generateDefaultBloomfilter(path); } FileInputStream inputStream = new FileInputStream(new File(path)); mMedicalDeviceBloomFilter = BloomFilter.readFrom(inputStream, Funnels.byteArrayFunnel()); mMedicalDeviceBloomFilterInitialized = true; } catch (IOException e1) { Log.w(TAG, "MetricsLogger can't read the medical device BloomFilter file."); byte[] bloomfilterData = MedicalDeviceBloomfilterGenerator.hexStringToByteArray( MedicalDeviceBloomfilterGenerator.BLOOM_FILTER_DEFAULT); try { mMedicalDeviceBloomFilter = BloomFilter.readFrom( new ByteArrayInputStream(bloomfilterData), Funnels.byteArrayFunnel()); mMedicalDeviceBloomFilterInitialized = true; Log.i(TAG, "The medical device bloomfilter is used"); return true; } catch (IOException e2) { Log.w(TAG, "The medical device bloomfilter can't be used."); } return false; } return true; } protected void setBloomfilter(BloomFilter bloomfilter) { mBloomFilter = bloomfilter; } protected void setMedicalDeviceBloomfilter(BloomFilter bloomfilter) { mMedicalDeviceBloomFilter = bloomfilter; } void init(AdapterService adapterService, RemoteDevices remoteDevices) { if (mInitialized) { return; Loading @@ -203,6 +248,12 @@ public class MetricsLogger { // We still want to use this class even if the bloomfilter isn't initialized // so still return true here. } if (!initMedicalDeviceBloomFilter(MEDICAL_DEVICE_BLOOMFILTER_FULL_PATH)) { Log.w(TAG, "MetricsLogger can't initialize the medical device bloomfilter"); // The class is for multiple metrics tasks. // We still want to use this class even if the bloomfilter isn't initialized // so still return true here. } IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); Loading Loading @@ -289,7 +340,8 @@ public class MetricsLogger { BluetoothDevice device = connIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state = connIntent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); int metricId = mAdapterService.getMetricId(device); byte[] remoteDeviceInfoBytes = getRemoteDeviceInfoProto(device); boolean includeMedicalDevices = false; byte[] remoteDeviceInfoBytes = getRemoteDeviceInfoProto(device, includeMedicalDevices); if (state == BluetoothProfile.STATE_CONNECTING) { String deviceName = mRemoteDevices.getName(device); BluetoothStatsLog.write( Loading Loading @@ -417,6 +469,7 @@ public class MetricsLogger { mAdapterService = null; mInitialized = false; mBloomFilterInitialized = false; mMedicalDeviceBloomFilterInitialized = false; } protected void cancelPendingDrain() { Loading Loading @@ -446,15 +499,31 @@ public class MetricsLogger { /** * Retrieves a byte array containing serialized remote device information for the specified * BluetoothDevice. This data can be used for remote device identification and logging. * BluetoothDevice. This data can be used for remote device identification and logging. Does not * include medical remote devices. * * @param device The BluetoothDevice for which to retrieve device information. * @return A byte array containing the serialized remote device information. */ public byte[] getRemoteDeviceInfoProto(BluetoothDevice device) { if (!mInitialized) { return null; return mInitialized ? buildRemoteDeviceInfoProto(device, false) : null; } /** * Retrieves a byte array containing serialized remote device information for the specified * BluetoothDevice. This data can be used for remote device identification and logging. * * @param device The BluetoothDevice for which to retrieve device information. * @param includeMedicalDevices Should be true only if logging as de-identified metric, * otherwise false. * @return A byte array containing the serialized remote device information. */ public byte[] getRemoteDeviceInfoProto(BluetoothDevice device, boolean includeMedicalDevices) { return mInitialized ? buildRemoteDeviceInfoProto(device, includeMedicalDevices) : null; } private byte[] buildRemoteDeviceInfoProto( BluetoothDevice device, boolean includeMedicalDevices) { ProtoOutputStream proto = new ProtoOutputStream(); // write Allowlisted Device Name Hash Loading @@ -463,7 +532,8 @@ public class MetricsLogger { ProtoOutputStream.FIELD_TYPE_STRING, ProtoOutputStream.FIELD_COUNT_SINGLE, BluetoothRemoteDeviceInformation.ALLOWLISTED_DEVICE_NAME_HASH_FIELD_NUMBER, getAllowlistedDeviceNameHash(mAdapterService.getRemoteName(device))); getAllowlistedDeviceNameHash( mAdapterService.getRemoteName(device), includeMedicalDevices)); // write COD writeFieldIfNotNull( Loading Loading @@ -567,7 +637,7 @@ public class MetricsLogger { } } private String getMatchedString(List<String> wordBreakdownList) { private String getMatchedString(List<String> wordBreakdownList, boolean includeMedicalDevices) { if (!mBloomFilterInitialized || wordBreakdownList.isEmpty()) { return ""; } Loading @@ -579,6 +649,21 @@ public class MetricsLogger { matchedString = word; } } return (matchedString.equals("") && includeMedicalDevices) ? getMatchedStringForMedicalDevice(wordBreakdownList) : matchedString; } private String getMatchedStringForMedicalDevice(List<String> wordBreakdownList) { String matchedString = ""; for (String word : wordBreakdownList) { byte[] sha256 = getSha256(word); if (mMedicalDeviceBloomFilter.mightContain(sha256) && word.length() > matchedString.length()) { matchedString = word; } } return matchedString; } Loading Loading @@ -664,16 +749,18 @@ public class MetricsLogger { advDurationMs); } protected String getAllowlistedDeviceNameHash(String deviceName) { protected String getAllowlistedDeviceNameHash( String deviceName, boolean includeMedicalDevices) { List<String> wordBreakdownList = getWordBreakdownList(deviceName); String matchedString = getMatchedString(wordBreakdownList); String matchedString = getMatchedString(wordBreakdownList, includeMedicalDevices); return getSha256String(matchedString); } protected String logAllowlistedDeviceNameHash( int metricId, String deviceName, boolean logRestrictedNames) { List<String> wordBreakdownList = getWordBreakdownList(deviceName); String matchedString = getMatchedString(wordBreakdownList); boolean includeMedicalDevices = false; String matchedString = getMatchedString(wordBreakdownList, includeMedicalDevices); if (logRestrictedNames) { // Log the restricted bluetooth device name if (SdkLevel.isAtLeastU()) { Loading Loading @@ -705,7 +792,7 @@ public class MetricsLogger { state, uid, mAdapterService.getMetricId(device), getRemoteDeviceInfoProto(device)); getRemoteDeviceInfoProto(device, false)); } protected static String getSha256String(String name) { Loading Loading @@ -836,6 +923,6 @@ public class MetricsLogger { latencyPaSyncMs, latencyBisSyncMs, syncStatus, getRemoteDeviceInfoProto(device)); getRemoteDeviceInfoProto(device, false)); } } android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java +22 −0 Original line number Diff line number Diff line Loading @@ -269,6 +269,28 @@ public class MetricsLoggerTest { } } @Test public void testGetAllowlistedDeviceNameHashForMedicalDevice() { String deviceName = "Sam's rphonak hearing aid"; String expectMedicalDeviceSha256 = MetricsLogger.getSha256String("rphonakhearingaid"); String actualMedicalDeviceSha256 = mTestableMetricsLogger.getAllowlistedDeviceNameHash(deviceName, true); Assert.assertEquals(expectMedicalDeviceSha256, actualMedicalDeviceSha256); } @Test public void testGetAllowlistedDeviceNameHashForMedicalDeviceIdentifiedLogging() { String deviceName = "Sam's rphonak hearing aid"; String expectMedicalDeviceSha256 = ""; String actualMedicalDeviceSha256 = mTestableMetricsLogger.getAllowlistedDeviceNameHash(deviceName, false); Assert.assertEquals(expectMedicalDeviceSha256, actualMedicalDeviceSha256); } @Test public void uploadEmptyDeviceName() { initTestingBloomfilter(); Loading Loading
android/app/src/com/android/bluetooth/btservice/MedicalDeviceBloomfilterGenerator.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.btservice; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** Class to generate a medical device bloomfilter */ public class MedicalDeviceBloomfilterGenerator { public static final String BLOOM_FILTER_DEFAULT = "01070000003C01002106584044800850055" + "002308488410020D9A00001138410510000" + "000042004200000000000C2000000040064" + "0120080020110412A500090520210040C40" + "4002601040005004400148414006198A041" + "00890000600400000800210041810600800" + "0142208000721A030000028102448201110" + "0002007120020101448C211490A2B000084" + "C010004004C00C080808200026210608110" + "200011200000015000000212C4400040802" + "00111114840000001002080040186000404" + "81C064400052381109017039900000200C9" + "C0002E6480000101C40000601064001A018" + "40440280A810001000040455A0404617034" + "50000140040D020020C6204100804041600" + "80840002000800804280028000440000122" + "00808409905022000590000110448080400" + "561004210020430092602000040C0090C00" + "C18480020000519C1482100111011120390" + "02C0000228208104800C050440000004040" + "00871400882400140080000005308124900" + "104000040002410508CA349000200000202" + "90200920181890100800110220A20874820" + "0428080054A0005101C0820060090080040" + "6820C480F40081014010201800000018101" + "208914100321008006520002030010800C4" + "1022C000048206002010041220000008021" + "002080314040000100030002008"; /** Provide byte[] version of a given string */ public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } /** Generate bloom filter file given filePath */ public static void generateDefaultBloomfilter(String filePath) throws IOException { File outputFile = new File(filePath); outputFile.createNewFile(); // if file already exists will do nothing FileOutputStream fos = new FileOutputStream(filePath); fos.write(hexStringToByteArray(BLOOM_FILTER_DEFAULT)); fos.close(); } }
android/app/src/com/android/bluetooth/btservice/MetricsLogger.java +99 −12 Original line number Diff line number Diff line Loading @@ -96,7 +96,10 @@ public class MetricsLogger { private static final String TAG = "BluetoothMetricsLogger"; private static final String BLOOMFILTER_PATH = "/data/misc/bluetooth"; private static final String BLOOMFILTER_FILE = "/devices_for_metrics_v3"; private static final String MEDICAL_DEVICE_BLOOMFILTER_FILE = "/medical_devices_for_metrics_v1"; public static final String BLOOMFILTER_FULL_PATH = BLOOMFILTER_PATH + BLOOMFILTER_FILE; public static final String MEDICAL_DEVICE_BLOOMFILTER_FULL_PATH = BLOOMFILTER_PATH + MEDICAL_DEVICE_BLOOMFILTER_FILE; // 6 hours timeout for counter metrics private static final long BLUETOOTH_COUNTER_METRICS_ACTION_DURATION_MILLIS = 6L * 3600L * 1000L; Loading @@ -114,6 +117,10 @@ public class MetricsLogger { private BloomFilter<byte[]> mBloomFilter = null; protected boolean mBloomFilterInitialized = false; private BloomFilter<byte[]> mMedicalDeviceBloomFilter = null; protected boolean mMedicalDeviceBloomFilterInitialized = false; private AlarmManager.OnAlarmListener mOnAlarmListener = new AlarmManager.OnAlarmListener() { @Override Loading Loading @@ -185,10 +192,48 @@ public class MetricsLogger { return true; } /** Initialize medical device bloom filter */ public boolean initMedicalDeviceBloomFilter(String path) { try { File medicalDeviceFile = new File(path); if (!medicalDeviceFile.exists()) { Log.w(TAG, "MetricsLogger is creating a new medical device Bloomfilter file"); MedicalDeviceBloomfilterGenerator.generateDefaultBloomfilter(path); } FileInputStream inputStream = new FileInputStream(new File(path)); mMedicalDeviceBloomFilter = BloomFilter.readFrom(inputStream, Funnels.byteArrayFunnel()); mMedicalDeviceBloomFilterInitialized = true; } catch (IOException e1) { Log.w(TAG, "MetricsLogger can't read the medical device BloomFilter file."); byte[] bloomfilterData = MedicalDeviceBloomfilterGenerator.hexStringToByteArray( MedicalDeviceBloomfilterGenerator.BLOOM_FILTER_DEFAULT); try { mMedicalDeviceBloomFilter = BloomFilter.readFrom( new ByteArrayInputStream(bloomfilterData), Funnels.byteArrayFunnel()); mMedicalDeviceBloomFilterInitialized = true; Log.i(TAG, "The medical device bloomfilter is used"); return true; } catch (IOException e2) { Log.w(TAG, "The medical device bloomfilter can't be used."); } return false; } return true; } protected void setBloomfilter(BloomFilter bloomfilter) { mBloomFilter = bloomfilter; } protected void setMedicalDeviceBloomfilter(BloomFilter bloomfilter) { mMedicalDeviceBloomFilter = bloomfilter; } void init(AdapterService adapterService, RemoteDevices remoteDevices) { if (mInitialized) { return; Loading @@ -203,6 +248,12 @@ public class MetricsLogger { // We still want to use this class even if the bloomfilter isn't initialized // so still return true here. } if (!initMedicalDeviceBloomFilter(MEDICAL_DEVICE_BLOOMFILTER_FULL_PATH)) { Log.w(TAG, "MetricsLogger can't initialize the medical device bloomfilter"); // The class is for multiple metrics tasks. // We still want to use this class even if the bloomfilter isn't initialized // so still return true here. } IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); Loading Loading @@ -289,7 +340,8 @@ public class MetricsLogger { BluetoothDevice device = connIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state = connIntent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); int metricId = mAdapterService.getMetricId(device); byte[] remoteDeviceInfoBytes = getRemoteDeviceInfoProto(device); boolean includeMedicalDevices = false; byte[] remoteDeviceInfoBytes = getRemoteDeviceInfoProto(device, includeMedicalDevices); if (state == BluetoothProfile.STATE_CONNECTING) { String deviceName = mRemoteDevices.getName(device); BluetoothStatsLog.write( Loading Loading @@ -417,6 +469,7 @@ public class MetricsLogger { mAdapterService = null; mInitialized = false; mBloomFilterInitialized = false; mMedicalDeviceBloomFilterInitialized = false; } protected void cancelPendingDrain() { Loading Loading @@ -446,15 +499,31 @@ public class MetricsLogger { /** * Retrieves a byte array containing serialized remote device information for the specified * BluetoothDevice. This data can be used for remote device identification and logging. * BluetoothDevice. This data can be used for remote device identification and logging. Does not * include medical remote devices. * * @param device The BluetoothDevice for which to retrieve device information. * @return A byte array containing the serialized remote device information. */ public byte[] getRemoteDeviceInfoProto(BluetoothDevice device) { if (!mInitialized) { return null; return mInitialized ? buildRemoteDeviceInfoProto(device, false) : null; } /** * Retrieves a byte array containing serialized remote device information for the specified * BluetoothDevice. This data can be used for remote device identification and logging. * * @param device The BluetoothDevice for which to retrieve device information. * @param includeMedicalDevices Should be true only if logging as de-identified metric, * otherwise false. * @return A byte array containing the serialized remote device information. */ public byte[] getRemoteDeviceInfoProto(BluetoothDevice device, boolean includeMedicalDevices) { return mInitialized ? buildRemoteDeviceInfoProto(device, includeMedicalDevices) : null; } private byte[] buildRemoteDeviceInfoProto( BluetoothDevice device, boolean includeMedicalDevices) { ProtoOutputStream proto = new ProtoOutputStream(); // write Allowlisted Device Name Hash Loading @@ -463,7 +532,8 @@ public class MetricsLogger { ProtoOutputStream.FIELD_TYPE_STRING, ProtoOutputStream.FIELD_COUNT_SINGLE, BluetoothRemoteDeviceInformation.ALLOWLISTED_DEVICE_NAME_HASH_FIELD_NUMBER, getAllowlistedDeviceNameHash(mAdapterService.getRemoteName(device))); getAllowlistedDeviceNameHash( mAdapterService.getRemoteName(device), includeMedicalDevices)); // write COD writeFieldIfNotNull( Loading Loading @@ -567,7 +637,7 @@ public class MetricsLogger { } } private String getMatchedString(List<String> wordBreakdownList) { private String getMatchedString(List<String> wordBreakdownList, boolean includeMedicalDevices) { if (!mBloomFilterInitialized || wordBreakdownList.isEmpty()) { return ""; } Loading @@ -579,6 +649,21 @@ public class MetricsLogger { matchedString = word; } } return (matchedString.equals("") && includeMedicalDevices) ? getMatchedStringForMedicalDevice(wordBreakdownList) : matchedString; } private String getMatchedStringForMedicalDevice(List<String> wordBreakdownList) { String matchedString = ""; for (String word : wordBreakdownList) { byte[] sha256 = getSha256(word); if (mMedicalDeviceBloomFilter.mightContain(sha256) && word.length() > matchedString.length()) { matchedString = word; } } return matchedString; } Loading Loading @@ -664,16 +749,18 @@ public class MetricsLogger { advDurationMs); } protected String getAllowlistedDeviceNameHash(String deviceName) { protected String getAllowlistedDeviceNameHash( String deviceName, boolean includeMedicalDevices) { List<String> wordBreakdownList = getWordBreakdownList(deviceName); String matchedString = getMatchedString(wordBreakdownList); String matchedString = getMatchedString(wordBreakdownList, includeMedicalDevices); return getSha256String(matchedString); } protected String logAllowlistedDeviceNameHash( int metricId, String deviceName, boolean logRestrictedNames) { List<String> wordBreakdownList = getWordBreakdownList(deviceName); String matchedString = getMatchedString(wordBreakdownList); boolean includeMedicalDevices = false; String matchedString = getMatchedString(wordBreakdownList, includeMedicalDevices); if (logRestrictedNames) { // Log the restricted bluetooth device name if (SdkLevel.isAtLeastU()) { Loading Loading @@ -705,7 +792,7 @@ public class MetricsLogger { state, uid, mAdapterService.getMetricId(device), getRemoteDeviceInfoProto(device)); getRemoteDeviceInfoProto(device, false)); } protected static String getSha256String(String name) { Loading Loading @@ -836,6 +923,6 @@ public class MetricsLogger { latencyPaSyncMs, latencyBisSyncMs, syncStatus, getRemoteDeviceInfoProto(device)); getRemoteDeviceInfoProto(device, false)); } }
android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java +22 −0 Original line number Diff line number Diff line Loading @@ -269,6 +269,28 @@ public class MetricsLoggerTest { } } @Test public void testGetAllowlistedDeviceNameHashForMedicalDevice() { String deviceName = "Sam's rphonak hearing aid"; String expectMedicalDeviceSha256 = MetricsLogger.getSha256String("rphonakhearingaid"); String actualMedicalDeviceSha256 = mTestableMetricsLogger.getAllowlistedDeviceNameHash(deviceName, true); Assert.assertEquals(expectMedicalDeviceSha256, actualMedicalDeviceSha256); } @Test public void testGetAllowlistedDeviceNameHashForMedicalDeviceIdentifiedLogging() { String deviceName = "Sam's rphonak hearing aid"; String expectMedicalDeviceSha256 = ""; String actualMedicalDeviceSha256 = mTestableMetricsLogger.getAllowlistedDeviceNameHash(deviceName, false); Assert.assertEquals(expectMedicalDeviceSha256, actualMedicalDeviceSha256); } @Test public void uploadEmptyDeviceName() { initTestingBloomfilter(); Loading