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

Commit 6ceb0dd5 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Multi-HFP device support for audio selector."

parents e24966c5 ef6d3791
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import com.android.dialer.spam.SpamComponent;
import com.android.dialer.speeddial.loader.UiItemLoaderComponent;
import com.android.dialer.storage.StorageComponent;
import com.android.dialer.strictmode.StrictModeComponent;
import com.android.incallui.audiomode.BluetoothDeviceProviderComponent;
import com.android.incallui.calllocation.CallLocationComponent;
import com.android.incallui.maps.MapsComponent;
import com.android.incallui.speakeasy.SpeakEasyComponent;
@@ -49,7 +50,8 @@ import com.android.voicemail.VoicemailComponent;
 * from this component.
 */
public interface BaseDialerRootComponent
    extends BubbleComponent.HasComponent,
    extends BluetoothDeviceProviderComponent.HasComponent,
        BubbleComponent.HasComponent,
        CallLocationComponent.HasComponent,
        CallLogComponent.HasComponent,
        CallLogConfigComponent.HasComponent,
+3 −0
Original line number Diff line number Diff line
@@ -40,6 +40,9 @@
  <!-- Testing location -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

  <!-- Set Bluetooth device -->
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

  <!-- Set android:taskAffinity="com.android.incallui" for all activities to ensure proper
  navigation. Otherwise system could bring up DialtactsActivity instead, e.g. when user unmerge a
  call.
+3 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.telecom.InCallService;
import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
import com.android.dialer.feedback.FeedbackComponent;
import com.android.incallui.audiomode.AudioModeProvider;
import com.android.incallui.audiomode.BluetoothDeviceProviderComponent;
import com.android.incallui.call.CallList;
import com.android.incallui.call.ExternalCallList;
import com.android.incallui.call.TelecomAdapter;
@@ -97,6 +98,7 @@ public class InCallServiceImpl extends InCallService {
    final Context context = getApplicationContext();
    final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);
    AudioModeProvider.getInstance().initializeAudioState(this);
    BluetoothDeviceProviderComponent.get(context).bluetoothDeviceProvider().setUp();
    InCallPresenter.getInstance()
        .setUp(
            context,
@@ -141,6 +143,7 @@ public class InCallServiceImpl extends InCallService {
    // Tear down the InCall system
    InCallPresenter.getInstance().tearDown();
    TelecomAdapter.getInstance().clearInCallService();
    BluetoothDeviceProviderComponent.get(this).bluetoothDeviceProvider().tearDown();
    if (returnToCallController != null) {
      returnToCallController.tearDown();
      returnToCallController = null;
+203 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.incallui.audiomode;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.ArraySet;
import com.android.dialer.common.LogUtil;
import com.android.dialer.inject.ApplicationContext;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;

/** Proxy class for getting and setting connected/active Bluetooth devices. */
@Singleton
public final class BluetoothDeviceProvider extends BroadcastReceiver {

  // TODO(yueg): use BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED when possible
  private static final String ACTION_ACTIVE_DEVICE_CHANGED =
      "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";

  private final Context appContext;
  private final BluetoothProfileServiceListener bluetoothProfileServiceListener =
      new BluetoothProfileServiceListener();

  private final Set<BluetoothDevice> connectedBluetoothDeviceSet = new ArraySet<>();

  private BluetoothDevice activeBluetoothDevice;
  private BluetoothHeadset bluetoothHeadset;
  private boolean isSetUp;

  @Inject
  public BluetoothDeviceProvider(@ApplicationContext Context appContext) {
    this.appContext = appContext;
  }

  public void setUp() {
    if (BluetoothAdapter.getDefaultAdapter() == null) {
      // Bluetooth is not supported on this hardware platform
      return;
    }
    // Get Bluetooth service including the initial connected device list (should only contain one
    // device)
    BluetoothAdapter.getDefaultAdapter()
        .getProfileProxy(appContext, bluetoothProfileServiceListener, BluetoothProfile.HEADSET);
    // Get notified of Bluetooth device update
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
    filter.addAction(ACTION_ACTIVE_DEVICE_CHANGED);
    appContext.registerReceiver(this, filter);

    isSetUp = true;
  }

  public void tearDown() {
    if (!isSetUp) {
      return;
    }
    appContext.unregisterReceiver(this);
    if (bluetoothHeadset != null) {
      BluetoothAdapter.getDefaultAdapter()
          .closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset);
    }
  }

  public Set<BluetoothDevice> getConnectedBluetoothDeviceSet() {
    return connectedBluetoothDeviceSet;
  }

  public BluetoothDevice getActiveBluetoothDevice() {
    return activeBluetoothDevice;
  }

  @SuppressLint("PrivateApi")
  public void setActiveBluetoothDevice(BluetoothDevice bluetoothDevice) {
    if (!connectedBluetoothDeviceSet.contains(bluetoothDevice)) {
      LogUtil.e("BluetoothProfileServiceListener.setActiveBluetoothDevice", "device is not in set");
      return;
    }
    // TODO(yueg): use BluetoothHeadset.setActiveDevice() when possible
    try {
      Method getActiveDeviceMethod =
          bluetoothHeadset.getClass().getDeclaredMethod("setActiveDevice", BluetoothDevice.class);
      getActiveDeviceMethod.setAccessible(true);
      getActiveDeviceMethod.invoke(bluetoothHeadset, bluetoothDevice);
    } catch (Exception e) {
      LogUtil.e(
          "BluetoothProfileServiceListener.setActiveBluetoothDevice",
          "failed to call setActiveDevice",
          e);
    }
  }

  @Override
  public void onReceive(Context context, Intent intent) {
    if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
      handleActionConnectionStateChanged(intent);
    } else if (ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) {
      handleActionActiveDeviceChanged(intent);
    }
  }

  private void handleActionConnectionStateChanged(Intent intent) {
    if (!intent.hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
      LogUtil.i(
          "BluetoothDeviceProvider.handleActionConnectionStateChanged",
          "extra BluetoothDevice.EXTRA_DEVICE not found");
      return;
    }
    BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    if (bluetoothDevice == null) {
      return;
    }

    int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
    if (state == BluetoothProfile.STATE_DISCONNECTED) {
      connectedBluetoothDeviceSet.remove(bluetoothDevice);
      LogUtil.i("BluetoothDeviceProvider.handleActionConnectionStateChanged", "device removed");
    } else if (state == BluetoothProfile.STATE_CONNECTED) {
      connectedBluetoothDeviceSet.add(bluetoothDevice);
      LogUtil.i("BluetoothDeviceProvider.handleActionConnectionStateChanged", "device added");
    }
  }

  private void handleActionActiveDeviceChanged(Intent intent) {
    if (!intent.hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
      LogUtil.i(
          "BluetoothDeviceProvider.handleActionActiveDeviceChanged",
          "extra BluetoothDevice.EXTRA_DEVICE not found");
      return;
    }
    activeBluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    LogUtil.i(
        "BluetoothDeviceProvider.handleActionActiveDeviceChanged",
        (activeBluetoothDevice == null ? "null" : ""));
  }

  private final class BluetoothProfileServiceListener implements BluetoothProfile.ServiceListener {
    @Override
    @SuppressLint("PrivateApi")
    public void onServiceConnected(int profile, BluetoothProfile bluetoothProfile) {
      if (profile != BluetoothProfile.HEADSET) {
        return;
      }
      // Get initial connected device list
      bluetoothHeadset = (BluetoothHeadset) bluetoothProfile;
      List<BluetoothDevice> devices = bluetoothProfile.getConnectedDevices();
      for (BluetoothDevice device : devices) {
        connectedBluetoothDeviceSet.add(device);
        LogUtil.i(
            "BluetoothProfileServiceListener.onServiceConnected", "get initial connected device");
      }

      // Get initial active device
      // TODO(yueg): use BluetoothHeadset.getActiveDevice() when possible
      try {
        Method getActiveDeviceMethod =
            bluetoothHeadset.getClass().getDeclaredMethod("getActiveDevice");
        getActiveDeviceMethod.setAccessible(true);
        activeBluetoothDevice = (BluetoothDevice) getActiveDeviceMethod.invoke(bluetoothHeadset);
        LogUtil.i(
            "BluetoothProfileServiceListener.onServiceConnected",
            "get initial active device" + ((activeBluetoothDevice == null) ? " null" : ""));
      } catch (Exception e) {
        LogUtil.e(
            "BluetoothProfileServiceListener.onServiceConnected",
            "failed to call getAcitveDevice",
            e);
      }
    }

    @Override
    public void onServiceDisconnected(int profile) {
      LogUtil.enterBlock("BluetoothProfileServiceListener.onServiceDisconnected");
      if (profile == BluetoothProfile.HEADSET) {
        bluetoothHeadset = null;
      }
    }
  }
}
+39 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.incallui.audiomode;

import android.content.Context;
import com.android.dialer.inject.HasRootComponent;
import dagger.Subcomponent;

/** Dagger component for the Bluetooth device provider. */
@Subcomponent
public abstract class BluetoothDeviceProviderComponent {

  public abstract BluetoothDeviceProvider bluetoothDeviceProvider();

  public static BluetoothDeviceProviderComponent get(Context context) {
    return ((BluetoothDeviceProviderComponent.HasComponent)
            ((HasRootComponent) context.getApplicationContext()).component())
        .bluetoothDeviceProviderComponent();
  }

  /** Used to refer to the root application component. */
  public interface HasComponent {
    BluetoothDeviceProviderComponent bluetoothDeviceProviderComponent();
  }
}
Loading