Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 275f7938 authored by Angela Wang's avatar Angela Wang Committed by Android (Google) Code Review
Browse files

Merge "Add new groups for LE hearing and hearable devices" into main

parents b56b9cbe ce69c93c
Loading
Loading
Loading
Loading
+71 −53
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ public final class HearingAidStatsLogUtils {
            "bt_hearing_aids_paired_history";
    private static final String BT_HEARING_DEVICES_CONNECTED_HISTORY =
            "bt_hearing_aids_connected_history";
    private static final String BT_LE_HEARING_DEVICES_CONNECTED_HISTORY =
            "bt_le_hearing_aids_connected_history";
    // The values here actually represent Bluetooth hearable devices, but were mistyped
    // as hearing devices in the string value previously. Keep the string values to ensure record
    // persistence.
@@ -57,19 +59,25 @@ public final class HearingAidStatsLogUtils {
            "bt_hearing_devices_paired_history";
    private static final String BT_HEARABLE_DEVICES_CONNECTED_HISTORY =
            "bt_hearing_devices_connected_history";
    private static final String BT_LE_HEARABLE_DEVICES_CONNECTED_HISTORY =
            "bt_le_hearing_devices_connected_history";

    private static final String HISTORY_RECORD_DELIMITER = ",";

    static final String CATEGORY_HEARING_DEVICES = "A11yHearingAidsUser";
    static final String CATEGORY_NEW_HEARING_DEVICES = "A11yNewHearingAidsUser";
    // Previously, 'hearable device' was incorrectly entered as 'hearing device'. These string
    // values are maintained to ensure records persistence.
    static final String CATEGORY_LE_HEARING_DEVICES = "A11yLeHearingAidsUser";
    static final String CATEGORY_NEW_LE_HEARING_DEVICES = "A11yNewLeHearingAidsUser";
    // The values here actually represent Bluetooth hearable devices, but were mistyped
    // as hearing devices in the string value previously. Keep the string values to ensure record
    // persistence.
    static final String CATEGORY_HEARABLE_DEVICES = "A11yHearingDevicesUser";
    static final String CATEGORY_NEW_HEARABLE_DEVICES = "A11yNewHearingDevicesUser";
    static final String CATEGORY_LE_HEARABLE_DEVICES = "A11yLeHearingDevicesUser";
    static final String CATEGORY_NEW_LE_HEARABLE_DEVICES = "A11yNewLeHearingDevicesUser";

    static final int PAIRED_HISTORY_EXPIRED_DAY = 30;
    static final int CONNECTED_HISTORY_EXPIRED_DAY = 7;
    private static final int VALID_PAIRED_EVENT_COUNT = 1;
    private static final int VALID_CONNECTED_EVENT_COUNT = 7;

    /**
@@ -81,13 +89,18 @@ public final class HearingAidStatsLogUtils {
            HistoryType.TYPE_HEARING_DEVICES_PAIRED,
            HistoryType.TYPE_HEARING_DEVICES_CONNECTED,
            HistoryType.TYPE_HEARABLE_DEVICES_PAIRED,
            HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED})
            HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED,
            HistoryType.TYPE_LE_HEARING_CONNECTED,
            HistoryType.TYPE_LE_HEARABLE_CONNECTED,
    })
    public @interface HistoryType {
        int TYPE_UNKNOWN = -1;
        int TYPE_HEARING_DEVICES_PAIRED = 0;
        int TYPE_HEARING_DEVICES_CONNECTED = 1;
        int TYPE_HEARABLE_DEVICES_PAIRED = 2;
        int TYPE_HEARABLE_DEVICES_CONNECTED = 3;
        int TYPE_LE_HEARING_CONNECTED = 4;
        int TYPE_LE_HEARABLE_CONNECTED = 5;
    }

    private static final HashMap<String, Integer> sDeviceAddressToBondEntryMap = new HashMap<>();
@@ -148,71 +161,56 @@ public final class HearingAidStatsLogUtils {
        if (isJustBonded(cachedDevice.getAddress())) {
            // Saves bonded timestamp as the source for judging whether to display the survey
            if (isHearingDevice(cachedDevice)) {
                HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
                        HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED);
                addCurrentTimeToHistory(context, HistoryType.TYPE_HEARING_DEVICES_PAIRED);
            } else if (isHearableDevice(cachedDevice)) {
                HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
                        HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_PAIRED);
                addCurrentTimeToHistory(context, HistoryType.TYPE_HEARABLE_DEVICES_PAIRED);
            }
            removeFromJustBonded(cachedDevice.getAddress());
        }

        if (profileState == BluetoothProfile.STATE_CONNECTED) {
            // Saves connected timestamp as the source for judging whether to display the survey
            if (isHearingProfile(profile)) {
                HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
                        HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
            } else if (isHearableProfile(profile) && !isHearingDevice(cachedDevice)) {
                // Hearing devices may contain some hearable profiles as well, make sure to exclude
                // these cases when update hearable connected history.
                HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
                        HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED);
            if (profile instanceof LeAudioProfile) {
                if (isHearingDevice(cachedDevice)) {
                    addCurrentTimeToHistory(context, HistoryType.TYPE_LE_HEARING_CONNECTED);
                } else {
                    addCurrentTimeToHistory(context, HistoryType.TYPE_LE_HEARABLE_CONNECTED);
                }
            } else if (isHearingProfile(profile)) {
                addCurrentTimeToHistory(context, HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
            } else if (isHearableProfile(profile)) {
                addCurrentTimeToHistory(context, HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED);
            }
        }
    }

    /**
     * Returns the user category if the user is already categorized. Otherwise, checks the
     * history and sees if the user is categorized as one of {@link #CATEGORY_HEARING_DEVICES},
     * {@link #CATEGORY_NEW_HEARING_DEVICES}, {@link #CATEGORY_HEARABLE_DEVICES}, and
     * {@link #CATEGORY_NEW_HEARABLE_DEVICES}.
     * Returns the user category based on different histories.
     *
     * @param context the request context
     * @return the category which user belongs to
     */
    public static synchronized String getUserCategory(Context context) {
        LinkedList<Long> hearingAidsConnectedHistory = getHistory(context,
                HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
        if (hearingAidsConnectedHistory != null
                && hearingAidsConnectedHistory.size() >= VALID_CONNECTED_EVENT_COUNT) {
            LinkedList<Long> hearingAidsPairedHistory = getHistory(context,
        boolean isNewPairedHearingUser = hasSufficientData(context,
                HistoryType.TYPE_HEARING_DEVICES_PAIRED);
            // Since paired history will be cleared after 30 days. If there's any record within 30
            // days, the user will be categorized as CATEGORY_NEW_HEARING_AIDS. Otherwise, the user
            // will be categorized as CATEGORY_HEARING_AIDS.
            if (hearingAidsPairedHistory != null
                    && hearingAidsPairedHistory.size() >= VALID_PAIRED_EVENT_COUNT) {
                return CATEGORY_NEW_HEARING_DEVICES;
            } else {
                return CATEGORY_HEARING_DEVICES;
        boolean isNewPairedHearableUser = hasSufficientData(context,
                HistoryType.TYPE_HEARABLE_DEVICES_PAIRED);

        if (hasSufficientData(context, HistoryType.TYPE_LE_HEARING_CONNECTED)) {
            return isNewPairedHearingUser ? CATEGORY_NEW_LE_HEARING_DEVICES
                    : CATEGORY_LE_HEARING_DEVICES;
        }
        if (hasSufficientData(context, HistoryType.TYPE_HEARING_DEVICES_CONNECTED)) {
            return isNewPairedHearingUser ? CATEGORY_NEW_HEARING_DEVICES
                    : CATEGORY_HEARING_DEVICES;
        }

        LinkedList<Long> hearableDevicesConnectedHistory = getHistory(context,
                HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED);
        if (hearableDevicesConnectedHistory != null
                && hearableDevicesConnectedHistory.size() >= VALID_CONNECTED_EVENT_COUNT) {
            LinkedList<Long> hearableDevicesPairedHistory = getHistory(context,
                    HistoryType.TYPE_HEARABLE_DEVICES_PAIRED);
            // Since paired history will be cleared after 30 days. If there's any record within 30
            // days, the user will be categorized as CATEGORY_NEW_HEARABLE_DEVICES. Otherwise, the
            // user will be categorized as CATEGORY_HEARABLE_DEVICES.
            if (hearableDevicesPairedHistory != null
                    && hearableDevicesPairedHistory.size() >= VALID_PAIRED_EVENT_COUNT) {
                return CATEGORY_NEW_HEARABLE_DEVICES;
            } else {
                return CATEGORY_HEARABLE_DEVICES;
        if (hasSufficientData(context, HistoryType.TYPE_LE_HEARABLE_CONNECTED)) {
            return isNewPairedHearableUser ? CATEGORY_NEW_LE_HEARABLE_DEVICES
                    : CATEGORY_LE_HEARABLE_DEVICES;
        }
        if (hasSufficientData(context, HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED)) {
            return isNewPairedHearableUser ? CATEGORY_NEW_HEARABLE_DEVICES
                    : CATEGORY_HEARABLE_DEVICES;
        }
        return "";
    }
@@ -265,7 +263,7 @@ public final class HearingAidStatsLogUtils {
        }
        if (history.peekLast() != null && isSameDay(timestamp, history.peekLast())) {
            if (DEBUG) {
                Log.w(TAG, "Skip this record, it's same day record");
                Log.w(TAG, "Skip record of history type=" + type + ", it's same day record");
            }
            return;
        }
@@ -285,7 +283,9 @@ public final class HearingAidStatsLogUtils {
            removeRecordsBeforeDay(history, PAIRED_HISTORY_EXPIRED_DAY);
            return history;
        } else if (BT_HEARING_DEVICES_CONNECTED_HISTORY.equals(spName)
                || BT_HEARABLE_DEVICES_CONNECTED_HISTORY.equals(spName)) {
                || BT_HEARABLE_DEVICES_CONNECTED_HISTORY.equals(spName)
                || BT_LE_HEARING_DEVICES_CONNECTED_HISTORY.equals(spName)
                || BT_LE_HEARABLE_DEVICES_CONNECTED_HISTORY.equals(spName)) {
            LinkedList<Long> history = convertToHistoryList(
                    getSharedPreferences(context).getString(spName, ""));
            removeRecordsBeforeDay(history, CONNECTED_HISTORY_EXPIRED_DAY);
@@ -357,6 +357,20 @@ public final class HearingAidStatsLogUtils {
                || profile instanceof LeAudioProfile;
    }

    private static boolean hasSufficientData(Context context, @HistoryType int historyType) {
        LinkedList<Long> history = getHistory(context, historyType);
        if (history == null) {
            return false;
        }

        if (historyType == HistoryType.TYPE_HEARING_DEVICES_PAIRED
                || historyType == HistoryType.TYPE_HEARABLE_DEVICES_PAIRED) {
            return !history.isEmpty();
        } else {
            return history.size() >= VALID_CONNECTED_EVENT_COUNT;
        }
    }

    private static SharedPreferences getSharedPreferences(Context context) {
        return context.getSharedPreferences(ACCESSIBILITY_PREFERENCE, Context.MODE_PRIVATE);
    }
@@ -372,6 +386,10 @@ public final class HearingAidStatsLogUtils {
                HistoryType.TYPE_HEARABLE_DEVICES_PAIRED, BT_HEARABLE_DEVICES_PAIRED_HISTORY);
        HISTORY_TYPE_TO_SP_NAME_MAPPING.put(
                HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED, BT_HEARABLE_DEVICES_CONNECTED_HISTORY);
        HISTORY_TYPE_TO_SP_NAME_MAPPING.put(
                HistoryType.TYPE_LE_HEARING_CONNECTED, BT_LE_HEARING_DEVICES_CONNECTED_HISTORY);
        HISTORY_TYPE_TO_SP_NAME_MAPPING.put(
                HistoryType.TYPE_LE_HEARABLE_CONNECTED, BT_LE_HEARABLE_DEVICES_CONNECTED_HISTORY);
    }

    private HearingAidStatsLogUtils() {}
+64 −41
Original line number Diff line number Diff line
@@ -17,6 +17,12 @@
package com.android.settingslib.bluetooth;

import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.CONNECTED_HISTORY_EXPIRED_DAY;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_PAIRED;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.HistoryType.TYPE_LE_HEARABLE_CONNECTED;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.HistoryType.TYPE_LE_HEARING_CONNECTED;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.PAIRED_HISTORY_EXPIRED_DAY;

import static com.google.common.truth.Truth.assertThat;
@@ -52,8 +58,7 @@ import java.util.concurrent.TimeUnit;
public class HearingAidStatsLogUtilsTest {

    private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
    private static final int TEST_HISTORY_TYPE =
            HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED;
    private static final int TEST_HISTORY_TYPE = TYPE_HEARING_DEVICES_CONNECTED;

    @Rule
    public final MockitoRule mockito = MockitoJUnit.rule();
@@ -132,43 +137,63 @@ public class HearingAidStatsLogUtilsTest {
    }

    @Test
    public void getUserCategory_hearingDevicesUser() {
        prepareHearingDevicesUserHistory();
    public void getUserCategory_hearingDevices() {
        prepareConnectedHistory(TYPE_HEARING_DEVICES_CONNECTED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_HEARING_DEVICES);
    }

    @Test
    public void getUserCategory_newHearingDevicesUser() {
        prepareHearingDevicesUserHistory();
        prepareNewUserHistory();
        preparePairedHistory(TYPE_HEARING_DEVICES_PAIRED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_DEVICES);
    }

    @Test
    public void getUserCategory_hearableDevicesUser() {
        prepareHearableDevicesUserHistory();
    public void getUserCategory_hearableDevices() {
        prepareConnectedHistory(TYPE_HEARABLE_DEVICES_CONNECTED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_HEARABLE_DEVICES);

        preparePairedHistory(TYPE_HEARABLE_DEVICES_PAIRED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_NEW_HEARABLE_DEVICES);
    }

    @Test
    public void getUserCategory_newHearableDevicesUser() {
        prepareHearableDevicesUserHistory();
        prepareNewUserHistory();
    public void getUserCategory_leHearingDevices() {
        prepareConnectedHistory(TYPE_HEARING_DEVICES_CONNECTED);
        prepareConnectedHistory(TYPE_LE_HEARING_CONNECTED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_NEW_HEARABLE_DEVICES);
                HearingAidStatsLogUtils.CATEGORY_LE_HEARING_DEVICES);

        preparePairedHistory(TYPE_HEARING_DEVICES_PAIRED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_NEW_LE_HEARING_DEVICES);
    }

    @Test
    public void getUserCategory_leHearableDevices() {
        prepareConnectedHistory(TYPE_HEARABLE_DEVICES_CONNECTED);
        prepareConnectedHistory(TYPE_LE_HEARABLE_CONNECTED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_LE_HEARABLE_DEVICES);

        preparePairedHistory(TYPE_HEARABLE_DEVICES_PAIRED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_NEW_LE_HEARABLE_DEVICES);
    }

    @Test
    public void getUserCategory_bothHearingAndHearableDevicesUser_returnHearingDevicesUser() {
        prepareHearingDevicesUserHistory();
        prepareHearableDevicesUserHistory();
    public void getUserCategory_bothHearingAndHearableDevices_returnHearingDevicesUser() {
        prepareConnectedHistory(TYPE_HEARING_DEVICES_CONNECTED);
        prepareConnectedHistory(TYPE_HEARABLE_DEVICES_CONNECTED);

        assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
                HearingAidStatsLogUtils.CATEGORY_HEARING_DEVICES);
@@ -179,10 +204,10 @@ public class HearingAidStatsLogUtilsTest {
        prepareAshaHearingDevice();

        HearingAidProfile ashaProfile = mock(HearingAidProfile.class);
        HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, mCachedBluetoothDevice, ashaProfile,
                BluetoothProfile.STATE_CONNECTED);
        HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, mCachedBluetoothDevice,
                ashaProfile, BluetoothProfile.STATE_CONNECTED);

        assertHistorySize(HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED, 1);
        assertHistorySize(TYPE_HEARING_DEVICES_CONNECTED, 1);
    }

    @Test
@@ -193,7 +218,7 @@ public class HearingAidStatsLogUtilsTest {
        HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, mCachedBluetoothDevice,
                hapClientProfile, BluetoothProfile.STATE_CONNECTED);

        assertHistorySize(HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED, 1);
        assertHistorySize(TYPE_HEARING_DEVICES_CONNECTED, 1);
    }

    @Test
@@ -204,7 +229,18 @@ public class HearingAidStatsLogUtilsTest {
        HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, mCachedBluetoothDevice,
                leAudioProfile, BluetoothProfile.STATE_CONNECTED);

        assertHistorySize(HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED, 0);
        assertHistorySize(TYPE_LE_HEARING_CONNECTED, 1);
    }

    @Test
    public void updateHistoryIfNeeded_hearableDevice_a2dpConnected_historyCorrect() {
        prepareHearableDevice();

        A2dpProfile a2dpProfile = mock(A2dpProfile.class);
        HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, mCachedBluetoothDevice,
                a2dpProfile, BluetoothProfile.STATE_CONNECTED);

        assertHistorySize(TYPE_HEARABLE_DEVICES_CONNECTED, 1);
    }

    @Test
@@ -215,7 +251,7 @@ public class HearingAidStatsLogUtilsTest {
        HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, mCachedBluetoothDevice,
                leAudioProfile, BluetoothProfile.STATE_CONNECTED);

        assertHistorySize(HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED, 1);
        assertHistorySize(TYPE_LE_HEARABLE_CONNECTED, 1);
    }

    private long convertToStartOfDayTime(long timestamp) {
@@ -224,31 +260,18 @@ public class HearingAidStatsLogUtilsTest {
        return date.atStartOfDay(zoneId).toInstant().toEpochMilli();
    }

    private void prepareHearingDevicesUserHistory() {
        final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
        for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
            final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
            HearingAidStatsLogUtils.addToHistory(mContext,
                    HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED, data);
        }
    }

    private void prepareHearableDevicesUserHistory() {
    private void prepareConnectedHistory(int historyType) {
        final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
        for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
            final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
            HearingAidStatsLogUtils.addToHistory(mContext,
                    HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_CONNECTED, data);
            HearingAidStatsLogUtils.addToHistory(mContext, historyType, data);
        }
    }

    private void prepareNewUserHistory() {
    private void preparePairedHistory(int historyType) {
        final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
        final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(PAIRED_HISTORY_EXPIRED_DAY - 1);
        HearingAidStatsLogUtils.addToHistory(mContext,
                HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED, data);
        HearingAidStatsLogUtils.addToHistory(mContext,
                HearingAidStatsLogUtils.HistoryType.TYPE_HEARABLE_DEVICES_PAIRED, data);
        HearingAidStatsLogUtils.addToHistory(mContext, historyType, data);
    }

    private void prepareAshaHearingDevice() {