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

Commit 4ef430e7 authored by yueg's avatar yueg Committed by Copybara-Service
Browse files

Use Telecom Bluetooth API instead of system Bluetooth API.

Bug: 74238896
Test: manual
PiperOrigin-RevId: 195437669
Change-Id: I1cb26187b8b90664b72de2a4451283a9fbdc0f10
parent fb215410
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ 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;
@@ -53,7 +52,6 @@ import com.android.voicemail.VoicemailComponent;
 */
public interface BaseDialerRootComponent
    extends ActiveCallsComponent.HasComponent,
        BluetoothDeviceProviderComponent.HasComponent,
        BubbleComponent.HasComponent,
        CallLocationComponent.HasComponent,
        CallLogComponent.HasComponent,
+0 −3
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ 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;
@@ -98,7 +97,6 @@ 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,
@@ -142,7 +140,6 @@ 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;
+0 −203
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;
      }
    }
  }
}
+0 −39
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();
  }
}
+25 −18
Original line number Diff line number Diff line
@@ -39,12 +39,12 @@ import com.android.dialer.common.FragmentUtils;
import com.android.dialer.common.LogUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.android.incallui.audiomode.BluetoothDeviceProviderComponent;
import com.android.incallui.call.CallList;
import com.android.incallui.call.DialerCall;
import com.android.incallui.call.TelecomAdapter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.Collection;

/** Shows picker for audio routes */
public class AudioRouteSelectorDialogFragment extends BottomSheetDialogFragment {
@@ -91,26 +91,35 @@ public class AudioRouteSelectorDialogFragment extends BottomSheetDialogFragment

  @Nullable
  @Override
  @SuppressLint("NewApi")
  public View onCreateView(
      LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
    View view = layoutInflater.inflate(R.layout.audioroute_selector, viewGroup, false);
    CallAudioState audioState = getArguments().getParcelable(ARG_AUDIO_STATE);

    Set<BluetoothDevice> bluetoothDeviceSet =
        BluetoothDeviceProviderComponent.get(getContext())
            .bluetoothDeviceProvider()
            .getConnectedBluetoothDeviceSet();
    if (BuildCompat.isAtLeastP()) {
      // Create items for all connected Bluetooth devices
      Collection<BluetoothDevice> bluetoothDeviceSet = audioState.getSupportedBluetoothDevices();
      for (BluetoothDevice device : bluetoothDeviceSet) {
        boolean selected =
            (audioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH)
                && (bluetoothDeviceSet.size() == 1
                  || device.equals(
                      BluetoothDeviceProviderComponent.get(getContext())
                          .bluetoothDeviceProvider()
                          .getActiveBluetoothDevice()));
                    || device.equals(audioState.getActiveBluetoothDevice()));
        TextView textView = createBluetoothItem(device, selected);
        ((LinearLayout) view).addView(textView, 0);
      }
    } else {
      // Only create Bluetooth audio route
      TextView textView =
          (TextView) getLayoutInflater().inflate(R.layout.audioroute_item, null, false);
      textView.setText(getString(R.string.audioroute_bluetooth));
      initItem(
          textView,
          CallAudioState.ROUTE_BLUETOOTH,
          audioState,
          DialerImpression.Type.IN_CALL_SWITCH_AUDIO_ROUTE_BLUETOOTH);
      ((LinearLayout) view).addView(textView, 0);
    }

    initItem(
        (TextView) view.findViewById(R.id.audioroute_speaker),
@@ -183,9 +192,7 @@ public class AudioRouteSelectorDialogFragment extends BottomSheetDialogFragment
                  AudioRouteSelectorDialogFragment.this, AudioRouteSelectorPresenter.class)
              .onAudioRouteSelected(CallAudioState.ROUTE_BLUETOOTH);
          // Set active Bluetooth device
          BluetoothDeviceProviderComponent.get(getContext())
              .bluetoothDeviceProvider()
              .setActiveBluetoothDevice(bluetoothDevice);
          TelecomAdapter.getInstance().requestBluetoothAudio(bluetoothDevice);
          dismiss();
        });

Loading