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

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

Merge "HFP: Fix AG indicator update for Multi-HFP" into pi-dev

parents 05baf103 1ca8214c
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ static jmethodID method_onUnknownAt;
static jmethodID method_onKeyPressed;
static jmethodID method_onAtBind;
static jmethodID method_onAtBiev;
static jmethodID method_onAtBia;

static bluetooth::headset::Interface* sBluetoothHfpInterface = nullptr;
static std::shared_timed_mutex interface_mutex;
@@ -366,6 +367,19 @@ class JniHeadsetCallbacks : bluetooth::headset::Callbacks {
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBiev, ind_id,
                                 (jint)ind_value, addr.get());
  }

  void AtBiaCallback(bool service, bool roam, bool signal, bool battery,
                     RawAddress* bd_addr) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || !mCallbacksObj) return;

    ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
    if (addr.get() == nullptr) return;

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBia, service, roam,
                                 signal, battery, addr.get());
  }
};

static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -396,6 +410,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
  method_onAtBind =
      env->GetMethodID(clazz, "onATBind", "(Ljava/lang/String;[B)V");
  method_onAtBiev = env->GetMethodID(clazz, "onATBiev", "(II[B)V");
  method_onAtBia = env->GetMethodID(clazz, "onAtBia", "(ZZZZ[B)V");

  ALOGI("%s: succeeds", __func__);
}
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.bluetooth.hfp;

/**
 * An object that represents AG indicator enable states
 */
public class HeadsetAgIndicatorEnableState extends HeadsetMessageObject {
    public boolean service;
    public boolean roam;
    public boolean signal;
    public boolean battery;

    HeadsetAgIndicatorEnableState(boolean newService, boolean newRoam, boolean newSignal,
            boolean newBattery) {
        service = newService;
        roam = newRoam;
        signal = newSignal;
        battery = newBattery;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof HeadsetAgIndicatorEnableState)) {
            return false;
        }
        HeadsetAgIndicatorEnableState other = (HeadsetAgIndicatorEnableState) obj;
        return service == other.service && roam == other.roam && signal == other.signal
                && battery == other.battery;
    }

    @Override
    public int hashCode() {
        int result = 0;
        if (service) result += 1;
        if (roam) result += 2;
        if (signal) result += 4;
        if (battery) result += 8;
        return result;
    }

    @Override
    public void buildString(StringBuilder builder) {
        if (builder == null) {
            return;
        }
        builder.append(this.getClass().getSimpleName())
                .append("[service=")
                .append(service)
                .append(", roam=")
                .append(roam)
                .append(", signal=")
                .append(signal)
                .append(", battery=")
                .append(battery)
                .append("]");
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -197,6 +197,16 @@ public class HeadsetNativeInterface {
        sendMessageToService(event);
    }

    private void onAtBia(boolean service, boolean roam, boolean signal, boolean battery,
            byte[] address) {
        HeadsetAgIndicatorEnableState agIndicatorEnableState =
                new HeadsetAgIndicatorEnableState(service, roam, signal, battery);
        HeadsetStackEvent event =
                new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIA, agIndicatorEnableState,
                        getDevice(address));
        sendMessageToService(event);
    }

    // Native wrappers to help unit testing

    /**
+58 −25
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.bluetooth.hfp;

import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,6 +34,7 @@ import android.util.Log;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;

import java.util.HashMap;
import java.util.Objects;


@@ -71,7 +73,7 @@ public class HeadsetPhoneState {
    // HFP 1.6 CIND battchg value
    private int mCindBatteryCharge;

    private boolean mListening;
    private final HashMap<BluetoothDevice, Integer> mDeviceEventMap = new HashMap<>();
    private PhoneStateListener mPhoneStateListener;
    private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;

@@ -95,7 +97,10 @@ public class HeadsetPhoneState {
     * Cleanup this instance. Instance can no longer be used after calling this method.
     */
    public void cleanup() {
        listenForPhoneState(false);
        synchronized (mDeviceEventMap) {
            mDeviceEventMap.clear();
            stopListenForPhoneState();
        }
        mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
    }

@@ -104,45 +109,71 @@ public class HeadsetPhoneState {
        return "HeadsetPhoneState [mTelephonyServiceAvailability=" + mCindService + ", mNumActive="
                + mNumActive + ", mCallState=" + mCallState + ", mNumHeld=" + mNumHeld
                + ", mSignal=" + mCindSignal + ", mRoam=" + mCindRoam + ", mBatteryCharge="
                + mCindBatteryCharge + ", mListening=" + mListening + "]";
                + mCindBatteryCharge + ", TelephonyEvents=" + getTelephonyEventsToListen() + "]";
    }

    private int getTelephonyEventsToListen() {
        synchronized (mDeviceEventMap) {
            return mDeviceEventMap.values()
                    .stream()
                    .reduce(PhoneStateListener.LISTEN_NONE, (a, b) -> a | b);
        }
    }

    /**
     * Start or stop listening for phone state change
     * @param start True to start, False to stop
     *
     * @param device remote device that subscribes to this phone state update
     * @param events events in {@link PhoneStateListener} to listen to
     */
    @VisibleForTesting
    public void listenForPhoneState(boolean start) {
        synchronized (mTelephonyManager) {
            if (start) {
                startListenForPhoneState();
    public void listenForPhoneState(BluetoothDevice device, int events) {
        synchronized (mDeviceEventMap) {
            int prevEvents = getTelephonyEventsToListen();
            if (events == PhoneStateListener.LISTEN_NONE) {
                mDeviceEventMap.remove(device);
            } else {
                mDeviceEventMap.put(device, events);
            }
            int updatedEvents = getTelephonyEventsToListen();
            if (prevEvents != updatedEvents) {
                stopListenForPhoneState();
                startListenForPhoneState();
            }
        }
    }

    private void startListenForPhoneState() {
        if (!mListening) {
        if (mPhoneStateListener != null) {
            Log.w(TAG, "startListenForPhoneState, already listening");
            return;
        }
        int events = getTelephonyEventsToListen();
        if (events == PhoneStateListener.LISTEN_NONE) {
            Log.w(TAG, "startListenForPhoneState, no event to listen");
            return;
        }
        int subId = SubscriptionManager.getDefaultSubscriptionId();
            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                mPhoneStateListener = new HeadsetPhoneStateListener(subId,
                        mHeadsetService.getStateMachinesThreadLooper());
                mTelephonyManager.listen(mPhoneStateListener,
                        PhoneStateListener.LISTEN_SERVICE_STATE
                                | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
                mListening = true;
            } else {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            // Will retry listening for phone state in onSubscriptionsChanged() callback
            Log.w(TAG, "startListenForPhoneState, invalid subscription ID " + subId);
            return;
        }
        }
        Log.i(TAG, "startListenForPhoneState(), subId=" + subId + ", enabled_events=" + events);
        mPhoneStateListener = new HeadsetPhoneStateListener(subId,
                mHeadsetService.getStateMachinesThreadLooper());
        mTelephonyManager.listen(mPhoneStateListener, events);
    }

    private void stopListenForPhoneState() {
        if (mListening) {
            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
            mListening = false;
        if (mPhoneStateListener == null) {
            Log.i(TAG, "stopListenForPhoneState(), no listener indicates nothing is listening");
            return;
        }
        Log.i(TAG, "stopListenForPhoneState(), stopping listener, enabled_events="
                + getTelephonyEventsToListen());
        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
        mPhoneStateListener = null;
    }

    int getCindService() {
@@ -223,8 +254,10 @@ public class HeadsetPhoneState {

        @Override
        public void onSubscriptionsChanged() {
            listenForPhoneState(false);
            listenForPhoneState(true);
            synchronized (mDeviceEventMap) {
                stopListenForPhoneState();
                startListenForPhoneState();
            }
        }
    }

+2 −1
Original line number Diff line number Diff line
@@ -200,7 +200,8 @@ public class HeadsetService extends ProfileService {
     *
     * @return {@link Looper} for the state machine thread
     */
    Looper getStateMachinesThreadLooper() {
    @VisibleForTesting
    public Looper getStateMachinesThreadLooper() {
        return mStateMachinesThread.getLooper();
    }

Loading