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

Commit 6b491bdf authored by Joseph Pirozzo's avatar Joseph Pirozzo
Browse files

Refactor A2dpSink and AvrcpController

Update Java side code to support command and response to addressed
device.  Simplify connection logic to create a new state machine per
addressed device rather than allowing connections to transfer from one
device to another, this eliminates the possibility of having AVCRP
connected to one device and A2dp connected to another one.

Bug: 120673113
Test: runtest bluetooth
Change-Id: Ia4feba10ea8ebc22834def42b57b7abf923bee04
(cherry picked from commit ae7bfdcd)
parent 98fa3a8e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -231,7 +231,7 @@ static JNINativeMethod sMethods[] = {

int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) {
  return jniRegisterNativeMethods(
      env, "com/android/bluetooth/a2dpsink/A2dpSinkStateMachine", sMethods,
      env, "com/android/bluetooth/a2dpsink/A2dpSinkService", sMethods,
      NELEM(sMethods));
}
}
+111 −79
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ static void btavrcp_passthrough_response_callback(const RawAddress& bd_addr,
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for passthrough response");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -109,7 +109,7 @@ static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect,
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for connection state");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -134,7 +134,7 @@ static void btavrcp_get_rcfeatures_callback(const RawAddress& bd_addr,
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -158,7 +158,7 @@ static void btavrcp_setplayerapplicationsetting_rsp_callback(
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -184,7 +184,7 @@ static void btavrcp_playerapplicationsetting_callback(
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
@@ -202,7 +202,7 @@ static void btavrcp_playerapplicationsetting_callback(
  ScopedLocalRef<jbyteArray> playerattribs(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen));
  if (!playerattribs.get()) {
    ALOGE("Fail to new jbyteArray playerattribs ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -236,7 +236,7 @@ static void btavrcp_playerapplicationsetting_changed_callback(
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
@@ -279,7 +279,7 @@ static void btavrcp_set_abs_vol_cmd_callback(const RawAddress& bd_addr,
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -303,7 +303,7 @@ static void btavrcp_register_notification_absvol_callback(
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -333,7 +333,7 @@ static void btavrcp_track_changed_callback(const RawAddress& bd_addr,
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

@@ -388,7 +388,7 @@ static void btavrcp_play_position_changed_callback(const RawAddress& bd_addr,
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
@@ -411,7 +411,7 @@ static void btavrcp_play_status_changed_callback(
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
@@ -436,6 +436,16 @@ static void btavrcp_get_folder_items_callback(
    return;
  }

  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  // Inspect if the first element is a folder/item or player listing. They are
  // always exclusive.
  bool isPlayerListing =
@@ -468,17 +478,7 @@ static void btavrcp_get_folder_items_callback(
          return;
        }
        // Parse UID
        ScopedLocalRef<jbyteArray> uidByteArray(
            sCallbackEnv.get(),
            sCallbackEnv->NewByteArray(sizeof(uint8_t) * BTRC_UID_SIZE));
        if (!uidByteArray.get()) {
          ALOGE("%s can't allocate uid array!", __func__);
          return;
        }
        sCallbackEnv->SetByteArrayRegion(uidByteArray.get(), 0,
                                         BTRC_UID_SIZE * sizeof(uint8_t),
                                         (jbyte*)item->media.uid);

        long long uid = *(long long*)item->media.uid;
        // Parse Attrs
        ScopedLocalRef<jintArray> attrIdArray(
            sCallbackEnv.get(),
@@ -504,10 +504,6 @@ static void btavrcp_get_folder_items_callback(
          ScopedLocalRef<jstring> attrValStr(
              sCallbackEnv.get(),
              sCallbackEnv->NewStringUTF((char*)(item->media.p_attrs[j].text)));
          if (!uidByteArray.get()) {
            ALOGE("%s can't allocate uid array!", __func__);
            return;
          }
          sCallbackEnv->SetObjectArrayElement(attrValArray.get(), j,
                                              attrValStr.get());
        }
@@ -515,9 +511,9 @@ static void btavrcp_get_folder_items_callback(
        ScopedLocalRef<jobject> mediaObj(
            sCallbackEnv.get(),
            (jobject)sCallbackEnv->CallObjectMethod(
                sCallbacksObj, method_createFromNativeMediaItem,
                uidByteArray.get(), (jint)item->media.type, mediaName.get(),
                attrIdArray.get(), attrValArray.get()));
                sCallbacksObj, method_createFromNativeMediaItem, uid,
                (jint)item->media.type, mediaName.get(), attrIdArray.get(),
                attrValArray.get()));
        if (!mediaObj.get()) {
          ALOGE("%s failed to creae MediaItem for type ITEM_MEDIA", __func__);
          return;
@@ -536,22 +532,12 @@ static void btavrcp_get_folder_items_callback(
          return;
        }
        // Parse UID
        ScopedLocalRef<jbyteArray> uidByteArray(
            sCallbackEnv.get(),
            sCallbackEnv->NewByteArray(sizeof(uint8_t) * BTRC_UID_SIZE));
        if (!uidByteArray.get()) {
          ALOGE("%s can't allocate uid array!", __func__);
          return;
        }
        sCallbackEnv->SetByteArrayRegion(uidByteArray.get(), 0,
                                         BTRC_UID_SIZE * sizeof(uint8_t),
                                         (jbyte*)item->folder.uid);

        long long uid = *(long long*)item->folder.uid;
        ScopedLocalRef<jobject> folderObj(
            sCallbackEnv.get(),
            (jobject)sCallbackEnv->CallObjectMethod(
                sCallbacksObj, method_createFromNativeFolderItem,
                uidByteArray.get(), (jint)item->folder.type, folderName.get(),
                sCallbacksObj, method_createFromNativeFolderItem, uid,
                (jint)item->folder.type, folderName.get(),
                (jint)item->folder.playable));
        if (!folderObj.get()) {
          ALOGE("%s failed to create MediaItem for type ITEM_FOLDER", __func__);
@@ -609,10 +595,10 @@ static void btavrcp_get_folder_items_callback(

  if (isPlayerListing) {
    sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetPlayerItemsRsp,
                                 itemArray.get());
                                 addr.get(), itemArray.get());
  } else {
    sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetFolderItemsRsp,
                                 status, itemArray.get());
                                 addr.get(), status, itemArray.get());
  }
}

@@ -626,9 +612,18 @@ static void btavrcp_change_path_callback(const RawAddress& bd_addr,
    ALOGE("%s: sCallbacksObj is null", __func__);
    return;
  }
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleChangeFolderRsp,
                               (jint)count);
                               addr.get(), (jint)count);
}

static void btavrcp_set_browsed_player_callback(const RawAddress& bd_addr,
@@ -642,9 +637,18 @@ static void btavrcp_set_browsed_player_callback(const RawAddress& bd_addr,
    ALOGE("%s: sCallbacksObj is null", __func__);
    return;
  }
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetBrowsedPlayerRsp,
                               (jint)num_items, (jint)depth);
                               addr.get(), (jint)num_items, (jint)depth);
}

static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr,
@@ -657,9 +661,19 @@ static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr,
    ALOGE("%s: sCallbacksObj is null", __func__);
    return;
  }
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

  sCallbackEnv->CallVoidMethod(
      sCallbacksObj, method_handleSetAddressedPlayerRsp, (jint)status);
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  sCallbackEnv->CallVoidMethod(sCallbacksObj,
                               method_handleSetAddressedPlayerRsp, addr.get(),
                               (jint)status);
}

static void btavrcp_addressed_player_changed_callback(const RawAddress& bd_addr,
@@ -672,9 +686,18 @@ static void btavrcp_addressed_player_changed_callback(const RawAddress& bd_addr,
    ALOGE("%s: sCallbacksObj is null", __func__);
    return;
  }
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

  sCallbackEnv->CallVoidMethod(sCallbacksObj,
                               method_handleAddressedPlayerChanged, (jint)id);
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  sCallbackEnv->CallVoidMethod(
      sCallbacksObj, method_handleAddressedPlayerChanged, addr.get(), (jint)id);
}

static void btavrcp_now_playing_content_changed_callback(
@@ -683,9 +706,18 @@ static void btavrcp_now_playing_content_changed_callback(

  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("%s: Failed to allocate a new byte array", __func__);
    return;
  }

  sCallbackEnv->CallVoidMethod(sCallbacksObj,
                               method_handleNowPlayingContentChanged);
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  sCallbackEnv->CallVoidMethod(
      sCallbacksObj, method_handleNowPlayingContentChanged, addr.get());
}

static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
@@ -747,32 +779,32 @@ static void classInitNative(JNIEnv* env, jclass clazz) {

  method_handleGetFolderItemsRsp =
      env->GetMethodID(clazz, "handleGetFolderItemsRsp",
                       "(I[Landroid/media/browse/MediaBrowser$MediaItem;)V");
                       "([BI[Landroid/media/browse/MediaBrowser$MediaItem;)V");
  method_handleGetPlayerItemsRsp = env->GetMethodID(
      clazz, "handleGetPlayerItemsRsp",
      "([Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V");
      "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V");

  method_createFromNativeMediaItem =
      env->GetMethodID(clazz, "createFromNativeMediaItem",
                       "([BILjava/lang/String;[I[Ljava/lang/String;)Landroid/"
                       "(JILjava/lang/String;[I[Ljava/lang/String;)Landroid/"
                       "media/browse/MediaBrowser$MediaItem;");
  method_createFromNativeFolderItem = env->GetMethodID(
      clazz, "createFromNativeFolderItem",
      "([BILjava/lang/String;I)Landroid/media/browse/MediaBrowser$MediaItem;");
      "(JILjava/lang/String;I)Landroid/media/browse/MediaBrowser$MediaItem;");
  method_createFromNativePlayerItem =
      env->GetMethodID(clazz, "createFromNativePlayerItem",
                       "(ILjava/lang/String;[BII)Lcom/android/bluetooth/"
                       "avrcpcontroller/AvrcpPlayer;");
  method_handleChangeFolderRsp =
      env->GetMethodID(clazz, "handleChangeFolderRsp", "(I)V");
      env->GetMethodID(clazz, "handleChangeFolderRsp", "([BI)V");
  method_handleSetBrowsedPlayerRsp =
      env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "(II)V");
      env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "([BII)V");
  method_handleSetAddressedPlayerRsp =
      env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "(I)V");
      env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "([BI)V");
  method_handleAddressedPlayerChanged =
      env->GetMethodID(clazz, "handleAddressedPlayerChanged", "(I)V");
      env->GetMethodID(clazz, "handleAddressedPlayerChanged", "([BI)V");
  method_handleNowPlayingContentChanged =
      env->GetMethodID(clazz, "handleNowPlayingContentChanged", "()V");
      env->GetMethodID(clazz, "handleNowPlayingContentChanged", "([B)V");

  ALOGI("%s: succeeds", __func__);
}
@@ -1081,7 +1113,7 @@ static void getPlayerListNative(JNIEnv* env, jobject object, jbyteArray address,

static void changeFolderPathNative(JNIEnv* env, jobject object,
                                   jbyteArray address, jbyte direction,
                                   jbyteArray uidarr) {
                                   jlong uid) {
  if (!sBluetoothAvrcpInterface) return;
  jbyte* addr = env->GetByteArrayElements(address, NULL);
  if (!addr) {
@@ -1089,22 +1121,22 @@ static void changeFolderPathNative(JNIEnv* env, jobject object,
    return;
  }

  jbyte* uid = env->GetByteArrayElements(uidarr, NULL);
  if (!uid) {
    jniThrowIOException(env, EINVAL);
    return;
  }
  // jbyte* uid = env->GetByteArrayElements(uidarr, NULL);
  // if (!uid) {
  //  jniThrowIOException(env, EINVAL);
  //  return;
  //}

  ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
  RawAddress rawAddress;
  rawAddress.FromOctets((uint8_t*)addr);

  bt_status_t status = sBluetoothAvrcpInterface->change_folder_path_cmd(
      rawAddress, (uint8_t)direction, (uint8_t*)uid);
      rawAddress, (uint8_t)direction, (uint8_t*)&uid);
  if (status != BT_STATUS_SUCCESS) {
    ALOGE("Failed sending changeFolderPathNative command, status: %d", status);
  }
  env->ReleaseByteArrayElements(address, addr, 0);
  // env->ReleaseByteArrayElements(address, addr, 0);
}

static void setBrowsedPlayerNative(JNIEnv* env, jobject object,
@@ -1149,7 +1181,7 @@ static void setAddressedPlayerNative(JNIEnv* env, jobject object,
}

static void playItemNative(JNIEnv* env, jobject object, jbyteArray address,
                           jbyte scope, jbyteArray uidArr, jint uidCounter) {
                           jbyte scope, jlong uid, jint uidCounter) {
  if (!sBluetoothAvrcpInterface) return;
  jbyte* addr = env->GetByteArrayElements(address, NULL);
  if (!addr) {
@@ -1157,17 +1189,17 @@ static void playItemNative(JNIEnv* env, jobject object, jbyteArray address,
    return;
  }

  jbyte* uid = env->GetByteArrayElements(uidArr, NULL);
  if (!uid) {
    jniThrowIOException(env, EINVAL);
    return;
  }
  //  jbyte* uid = env->GetByteArrayElements(uidArr, NULL);
  //  if (!uid) {
  //    jniThrowIOException(env, EINVAL);
  //    return;
  //  }
  RawAddress rawAddress;
  rawAddress.FromOctets((uint8_t*)addr);

  ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
  bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd(
      rawAddress, (uint8_t)scope, (uint8_t*)uid, (uint16_t)uidCounter);
      rawAddress, (uint8_t)scope, (uint8_t*)&uid, (uint16_t)uidCounter);
  if (status != BT_STATUS_SUCCESS) {
    ALOGE("Failed sending playItemNative command, status: %d", status);
  }
@@ -1191,8 +1223,8 @@ static JNINativeMethod sMethods[] = {
    {"getNowPlayingListNative", "([BII)V", (void*)getNowPlayingListNative},
    {"getFolderListNative", "([BII)V", (void*)getFolderListNative},
    {"getPlayerListNative", "([BII)V", (void*)getPlayerListNative},
    {"changeFolderPathNative", "([BB[B)V", (void*)changeFolderPathNative},
    {"playItemNative", "([BB[BI)V", (void*)playItemNative},
    {"changeFolderPathNative", "([BBJ)V", (void*)changeFolderPathNative},
    {"playItemNative", "([BBJI)V", (void*)playItemNative},
    {"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative},
    {"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative},
};
+255 −177

File changed.

Preview size limit exceeded, changes collapsed.

+177 −777

File changed.

Preview size limit exceeded, changes collapsed.

+33 −71
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ import android.os.Message;
import android.util.Log;

import com.android.bluetooth.R;
import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService;
import com.android.bluetooth.hfpclient.HeadsetClientService;

import java.util.List;
@@ -53,8 +53,8 @@ import java.util.List;
 * restored.
 */
public class A2dpSinkStreamHandler extends Handler {
    private static final boolean DBG = false;
    private static final String TAG = "A2dpSinkStreamHandler";
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);

    // Configuration Variables
    private static final int DEFAULT_DUCK_PERCENT = 25;
@@ -78,7 +78,7 @@ public class A2dpSinkStreamHandler extends Handler {
    private static final int STATE_FOCUS_GRANTED = 1;

    // Private variables.
    private A2dpSinkStateMachine mA2dpSinkSm;
    private A2dpSinkService mA2dpSinkService;
    private Context mContext;
    private AudioManager mAudioManager;
    // Keep track if the remote device is providing audio
@@ -109,12 +109,26 @@ public class A2dpSinkStreamHandler extends Handler {
        }
    };

    public A2dpSinkStreamHandler(A2dpSinkStateMachine a2dpSinkSm, Context context) {
        mA2dpSinkSm = a2dpSinkSm;
    public A2dpSinkStreamHandler(A2dpSinkService a2dpSinkService, Context context) {
        mA2dpSinkService = a2dpSinkService;
        mContext = context;
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

    void requestAudioFocus(boolean request) {
        obtainMessage(REQUEST_FOCUS, request).sendToTarget();
    }

    int getFocusState() {
        return mAudioFocus;
    }

    boolean isPlaying() {
        return (mStreamAvailable
                && (mAudioFocus == AudioManager.AUDIOFOCUS_GAIN
                || mAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK));
    }

    @Override
    public void handleMessage(Message message) {
        if (DBG) {
@@ -123,13 +137,10 @@ public class A2dpSinkStreamHandler extends Handler {
        }
        switch (message.what) {
            case SRC_STR_START:
                mStreamAvailable = true;
                if (isTvDevice() || shouldRequestFocus()) {
                    requestAudioFocusIfNone();
                }
                // Audio stream has started, stop it if we don't have focus.
                if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
                    sendAvrcpPause();
                }
                break;

            case SRC_STR_STOP:
@@ -153,10 +164,6 @@ public class A2dpSinkStreamHandler extends Handler {
                    requestAudioFocusIfNone();
                    break;
                }
                // Otherwise, pause if we don't have focus
                if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
                    sendAvrcpPause();
                }
                break;

            case SRC_PAUSE:
@@ -299,6 +306,7 @@ public class A2dpSinkStreamHandler extends Handler {
        });

        mMediaPlayer.start();
        BluetoothMediaBrowserService.setActive(true);
    }

    private synchronized void abandonAudioFocus() {
@@ -318,85 +326,39 @@ public class A2dpSinkStreamHandler extends Handler {
        if (mMediaPlayer == null) {
            return;
        }
        BluetoothMediaBrowserService.setActive(false);
        mMediaPlayer.stop();
        mMediaPlayer.release();
        mMediaPlayer = null;
    }

    private void startFluorideStreaming() {
        mA2dpSinkSm.informAudioFocusStateNative(STATE_FOCUS_GRANTED);
        mA2dpSinkSm.informAudioTrackGainNative(1.0f);
        mA2dpSinkService.informAudioFocusStateNative(STATE_FOCUS_GRANTED);
        mA2dpSinkService.informAudioTrackGainNative(1.0f);
    }

    private void stopFluorideStreaming() {
        mA2dpSinkSm.informAudioFocusStateNative(STATE_FOCUS_LOST);
        mA2dpSinkService.informAudioFocusStateNative(STATE_FOCUS_LOST);
    }

    private void setFluorideAudioTrackGain(float gain) {
        mA2dpSinkSm.informAudioTrackGainNative(gain);
        mA2dpSinkService.informAudioTrackGainNative(gain);
    }

    private void sendAvrcpPause() {
        // Since AVRCP gets started after A2DP we may need to request it later in cycle.
        AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();

        if (DBG) {
            Log.d(TAG, "sendAvrcpPause");
        }
        if (avrcpService != null) {
            List<BluetoothDevice> connectedDevices = avrcpService.getConnectedDevices();
            if (!connectedDevices.isEmpty()) {
                BluetoothDevice targetDevice = connectedDevices.get(0);
                if (DBG) {
                    Log.d(TAG, "Pausing AVRCP.");
                }
                avrcpService.sendPassThroughCmd(targetDevice,
                        AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE,
                        AvrcpControllerService.KEY_STATE_PRESSED);
                avrcpService.sendPassThroughCmd(targetDevice,
                        AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE,
                        AvrcpControllerService.KEY_STATE_RELEASED);
            }
        } else {
            Log.e(TAG, "Passthrough not sent, connection un-available.");
        }
        BluetoothMediaBrowserService.pause();
    }

    private void sendAvrcpPlay() {
        // Since AVRCP gets started after A2DP we may need to request it later in cycle.
        AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();

        if (DBG) {
            Log.d(TAG, "sendAvrcpPlay");
        }
        if (avrcpService != null) {
            List<BluetoothDevice> connectedDevices = avrcpService.getConnectedDevices();
            if (!connectedDevices.isEmpty()) {
                BluetoothDevice targetDevice = connectedDevices.get(0);
                if (DBG) {
                    Log.d(TAG, "Playing AVRCP.");
                }
                avrcpService.sendPassThroughCmd(targetDevice,
                        AvrcpControllerService.PASS_THRU_CMD_ID_PLAY,
                        AvrcpControllerService.KEY_STATE_PRESSED);
                avrcpService.sendPassThroughCmd(targetDevice,
                        AvrcpControllerService.PASS_THRU_CMD_ID_PLAY,
                        AvrcpControllerService.KEY_STATE_RELEASED);
            }
        } else {
            Log.e(TAG, "Passthrough not sent, connection un-available.");
        }
        BluetoothMediaBrowserService.play();
    }

    private boolean inCallFromStreamingDevice() {
        AvrcpControllerService avrcpService = AvrcpControllerService.getAvrcpControllerService();
        BluetoothDevice targetDevice = null;
        if (avrcpService != null) {
            List<BluetoothDevice> connectedDevices = avrcpService.getConnectedDevices();
        List<BluetoothDevice> connectedDevices = mA2dpSinkService.getConnectedDevices();
        if (!connectedDevices.isEmpty()) {
            targetDevice = connectedDevices.get(0);
        }
        }
        HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
        if (targetDevice != null && headsetClientService != null) {
            return headsetClientService.getCurrentCalls(targetDevice).size() > 0;
Loading