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

Commit 891ca8c8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topics "hearing-aid-white-list", "hearing-aid" into pi-dev

* changes:
  DO NOT MERGE Hearing Aid: Use separate time for L/R in connect() and use whitelist
  Hearing Aid: Remove device from HiSyncIdMap when unbonded
  Add Feature Flag for Hearing Aid Profile
parents e0838a0f 7734956e
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -194,6 +194,40 @@ static jboolean disconnectHearingAidNative(JNIEnv* env, jobject object,
  return JNI_TRUE;
}

static jboolean addToWhiteListNative(JNIEnv* env, jobject object,
                                     jbyteArray address) {
  std::shared_lock<std::shared_timed_mutex> 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->AddToWhiteList(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
  return JNI_TRUE;
}

static jboolean removeFromWhiteListNative(JNIEnv* env, jobject object,
                                          jbyteArray address) {
  std::shared_lock<std::shared_timed_mutex> 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->RemoveFromWhiteList(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
  return JNI_TRUE;
}

static void setVolumeNative(JNIEnv* env, jclass clazz, jint volume) {
  if (!sHearingAidInterface) {
    LOG(ERROR) << __func__
@@ -209,6 +243,8 @@ static JNINativeMethod sMethods[] = {
    {"cleanupNative", "()V", (void*)cleanupNative},
    {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
    {"disconnectHearingAidNative", "([B)Z", (void*)disconnectHearingAidNative},
    {"addToWhiteListNative", "([B)Z", (void*)addToWhiteListNative},
    {"removeFromWhiteListNative", "([B)Z", (void*)removeFromWhiteListNative},
    {"setVolumeNative", "(I)V", (void*)setVolumeNative},
};

+8 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
import android.util.FeatureFlagUtils;
import android.util.Log;

import com.android.bluetooth.R;
@@ -117,6 +118,13 @@ public class Config {
        ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);
        for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
            boolean supported = resources.getBoolean(config.mSupported);

            if (supported && (config.mClass == HearingAidService.class) && !FeatureFlagUtils
                                .isEnabled(ctx, FeatureFlagUtils.HEARING_AID_SETTINGS)) {
                Log.v(TAG, "Feature Flag disables support for HearingAidService");
                supported = false;
            }

            if (supported && !isProfileDisabled(ctx, config.mMask)) {
                Log.v(TAG, "Adding " + config.mClass.getSimpleName());
                profiles.add(config.mClass);
+24 −0
Original line number Diff line number Diff line
@@ -104,6 +104,28 @@ public class HearingAidNativeInterface {
        return disconnectHearingAidNative(getByteAddress(device));
    }

    /**
     * Add a hearing aid device to white list.
     *
     * @param device the remote device
     * @return true on success, otherwise false.
     */
    @VisibleForTesting (otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    public boolean addToWhiteList(BluetoothDevice device) {
        return addToWhiteListNative(getByteAddress(device));
    }

    /**
     * Remove a hearing aid device from white list.
     *
     * @param device the remote device
     * @return true on success, otherwise false.
     */
    @VisibleForTesting (otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    public boolean removeFromWhiteList(BluetoothDevice device) {
        return removeFromWhiteListNative(getByteAddress(device));
    }

    /**
     * Sets the HearingAid volume
     * @param volume
@@ -168,5 +190,7 @@ public class HearingAidNativeInterface {
    private native void cleanupNative();
    private native boolean connectHearingAidNative(byte[] address);
    private native boolean disconnectHearingAidNative(byte[] address);
    private native boolean addToWhiteListNative(byte[] address);
    private native boolean removeFromWhiteListNative(byte[] address);
    private native void setVolumeNative(int volume);
}
+33 −13
Original line number Diff line number Diff line
@@ -56,6 +56,10 @@ public class HearingAidService extends ProfileService {
    // Upper limit of all HearingAid devices: Bonded or Connected
    private static final int MAX_HEARING_AID_STATE_MACHINES = 10;
    private static HearingAidService sHearingAidService;
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    static int sConnectTimeoutForEachSideMs = 8000;
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    static int sCheckWhitelistTimeoutMs = 16000;

    private AdapterService mAdapterService;
    private HandlerThread mStateMachinesThread;
@@ -243,14 +247,6 @@ public class HearingAidService extends ProfileService {
            }
        }

        synchronized (mStateMachines) {
            HearingAidStateMachine smConnect = getOrCreateStateMachine(device);
            if (smConnect == null) {
                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
            }
            smConnect.sendMessage(HearingAidStateMachine.CONNECT);
        }

        for (BluetoothDevice storedDevice : mDeviceHiSyncIdMap.keySet()) {
            if (device.equals(storedDevice)) {
                continue;
@@ -263,14 +259,27 @@ public class HearingAidService extends ProfileService {
                        Log.e(TAG, "Ignored connect request for " + device + " : no state machine");
                        continue;
                    }
                    sm.sendMessage(HearingAidStateMachine.CONNECT);
                    sm.sendMessage(HearingAidStateMachine.CONNECT,
                            sConnectTimeoutForEachSideMs);
                    sm.sendMessageDelayed(HearingAidStateMachine.CHECK_WHITELIST_CONNECTION,
                            sCheckWhitelistTimeoutMs);
                }
                if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID
                        && !device.equals(storedDevice)) {
                break;
            }
        }

        synchronized (mStateMachines) {
            HearingAidStateMachine smConnect = getOrCreateStateMachine(device);
            if (smConnect == null) {
                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
            } else {
                smConnect.sendMessage(HearingAidStateMachine.CONNECT,
                        sConnectTimeoutForEachSideMs * 2);
                smConnect.sendMessageDelayed(HearingAidStateMachine.CHECK_WHITELIST_CONNECTION,
                        sCheckWhitelistTimeoutMs);
            }
        }

        return true;
    }

@@ -405,6 +414,16 @@ public class HearingAidService extends ProfileService {
        }
    }

    /**
     * Get the HiSyncIdMap for testing
     *
     * @return mDeviceHiSyncIdMap
     */
    @VisibleForTesting
    Map<BluetoothDevice, Long> getHiSyncIdMap() {
        return mDeviceHiSyncIdMap;
    }

    int getConnectionState(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        synchronized (mStateMachines) {
@@ -658,6 +677,7 @@ public class HearingAidService extends ProfileService {
        if (bondState != BluetoothDevice.BOND_NONE) {
            return;
        }
        mDeviceHiSyncIdMap.remove(device);
        synchronized (mStateMachines) {
            HearingAidStateMachine sm = mStateMachines.get(device);
            if (sm == null) {
+21 −12
Original line number Diff line number Diff line
@@ -69,13 +69,15 @@ final class HearingAidStateMachine extends StateMachine {

    static final int CONNECT = 1;
    static final int DISCONNECT = 2;
    static final int CHECK_WHITELIST_CONNECTION = 3;
    @VisibleForTesting
    static final int STACK_EVENT = 101;
    private static final int CONNECT_TIMEOUT = 201;

    // NOTE: the value is not "final" - it is modified in the unit tests
    @VisibleForTesting
    static int sConnectTimeoutMs = 30000;        // 30s
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    static int sConnectTimeoutMs = 16000;
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    static int sDisconnectTimeoutMs = 16000;

    private Disconnected mDisconnected;
    private Connecting mConnecting;
@@ -172,6 +174,12 @@ final class HearingAidStateMachine extends StateMachine {
                case DISCONNECT:
                    Log.w(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
                    break;
                case CHECK_WHITELIST_CONNECTION:
                    if (mService.getConnectedDevices().isEmpty()) {
                        log("No device connected, remove this device from white list");
                        mNativeInterface.removeFromWhiteList(mDevice);
                    }
                    break;
                case STACK_EVENT:
                    HearingAidStackEvent event = (HearingAidStackEvent) message.obj;
                    if (DBG) {
@@ -238,7 +246,9 @@ final class HearingAidStateMachine extends StateMachine {
        public void enter() {
            Log.i(TAG, "Enter Connecting(" + mDevice + "): "
                    + messageWhatToString(getCurrentMessage().what));
            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
            int timeout = getCurrentMessage().arg1 != 0
                    ? getCurrentMessage().arg1 : sConnectTimeoutMs;
            sendMessageDelayed(CONNECT_TIMEOUT, timeout);
            mConnectionState = BluetoothProfile.STATE_CONNECTING;
            broadcastConnectionState(mConnectionState, mLastConnectionState);
        }
@@ -261,14 +271,13 @@ final class HearingAidStateMachine extends StateMachine {
                    deferMessage(message);
                    break;
                case CONNECT_TIMEOUT:
                    Log.w(TAG, "Connecting connection timeout: " + mDevice);
                    Log.w(TAG, "Connecting connection timeout: " + mDevice + ". Try whitelist");
                    mNativeInterface.disconnectHearingAid(mDevice);
                    HearingAidStackEvent disconnectEvent =
                            new HearingAidStackEvent(
                                    HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
                    disconnectEvent.device = mDevice;
                    disconnectEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_DISCONNECTED;
                    sendMessage(STACK_EVENT, disconnectEvent);
                    mNativeInterface.addToWhiteList(mDevice);
                    transitionTo(mDisconnected);
                    break;
                case CHECK_WHITELIST_CONNECTION:
                    deferMessage(message);
                    break;
                case DISCONNECT:
                    log("Connecting: connection canceled to " + mDevice);
@@ -325,7 +334,7 @@ final class HearingAidStateMachine extends StateMachine {
        public void enter() {
            Log.i(TAG, "Enter Disconnecting(" + mDevice + "): "
                    + messageWhatToString(getCurrentMessage().what));
            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
            sendMessageDelayed(CONNECT_TIMEOUT, sDisconnectTimeoutMs);
            mConnectionState = BluetoothProfile.STATE_DISCONNECTING;
            broadcastConnectionState(mConnectionState, mLastConnectionState);
        }
Loading