1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.tv.settings.accessories; 18 19import android.bluetooth.BluetoothA2dp; 20import android.bluetooth.BluetoothAdapter; 21import android.bluetooth.BluetoothDevice; 22import android.bluetooth.BluetoothProfile; 23import android.content.BroadcastReceiver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.util.Log; 28 29 30public class BluetoothA2dpConnector implements BluetoothDevicePairer.BluetoothConnector { 31 32 public static final String TAG = "BluetoothA2dpConnector"; 33 34 private static final boolean DEBUG = false; 35 36 private Context mContext; 37 private BluetoothDevice mTarget; 38 private BluetoothDevicePairer.OpenConnectionCallback mOpenConnectionCallback; 39 private BluetoothA2dp mA2dpProfile; 40 private boolean mConnectionStateReceiverRegistered = false; 41 42 private BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() { 43 @Override 44 public void onReceive(Context context, Intent intent) { 45 BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra( 46 BluetoothDevice.EXTRA_DEVICE); 47 if (DEBUG) { 48 Log.d(TAG, "There was a connection status change for: " + device.getAddress()); 49 } 50 51 if (device.equals(mTarget)) { 52 int previousState = intent.getIntExtra( 53 BluetoothA2dp.EXTRA_PREVIOUS_STATE, 54 BluetoothA2dp.STATE_CONNECTING); 55 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, 56 BluetoothA2dp.STATE_CONNECTING); 57 58 if (DEBUG) { 59 Log.d(TAG, "Connection states: old = " + previousState + ", new = " + state); 60 } 61 62 if (previousState == BluetoothA2dp.STATE_CONNECTING) { 63 if (state == BluetoothA2dp.STATE_CONNECTED) { 64 mOpenConnectionCallback.succeeded(); 65 } else if (state == BluetoothA2dp.STATE_DISCONNECTED) { 66 Log.d(TAG, "Failed to connect"); 67 mOpenConnectionCallback.failed(); 68 } 69 70 unregisterConnectionStateReceiver(); 71 closeA2dpProfileProxy(); 72 } 73 } 74 } 75 }; 76 77 private BluetoothProfile.ServiceListener mServiceConnection = 78 new BluetoothProfile.ServiceListener() { 79 80 @Override 81 public void onServiceDisconnected(int profile) { 82 Log.w(TAG, "Service disconnected, perhaps unexpectedly"); 83 unregisterConnectionStateReceiver(); 84 closeA2dpProfileProxy(); 85 mOpenConnectionCallback.failed(); 86 } 87 88 @Override 89 public void onServiceConnected(int profile, BluetoothProfile proxy) { 90 if (DEBUG) { 91 Log.d(TAG, "Connection made to bluetooth proxy." ); 92 } 93 BluetoothA2dp mA2dpProfile = (BluetoothA2dp) proxy; 94 if (DEBUG) { 95 Log.d(TAG, "Connecting to target: " + mTarget.getAddress()); 96 } 97 98 registerConnectionStateReceiver(); 99 100 // TODO need to start a timer, otherwise if the connection fails we might be 101 // stuck here forever 102 mA2dpProfile.connect(mTarget); 103 104 // must set PRIORITY_AUTO_CONNECT or auto-connection will not 105 // occur, however this setting does not appear to be sticky 106 // across a reboot 107 mA2dpProfile.setPriority(mTarget, BluetoothProfile.PRIORITY_AUTO_CONNECT); 108 } 109 }; 110 111 private BluetoothA2dpConnector() { 112 } 113 114 public BluetoothA2dpConnector(Context context, BluetoothDevice target, 115 BluetoothDevicePairer.OpenConnectionCallback callback) { 116 mContext = context; 117 mTarget = target; 118 mOpenConnectionCallback = callback; 119 } 120 121 @Override 122 public void openConnection(BluetoothAdapter adapter) { 123 if (DEBUG) { 124 Log.d(TAG, "opening connection"); 125 } 126 if (!adapter.getProfileProxy(mContext, mServiceConnection, BluetoothProfile.A2DP)) { 127 mOpenConnectionCallback.failed(); 128 } 129 } 130 131 private void closeA2dpProfileProxy() { 132 if (mA2dpProfile != null) { 133 try { 134 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 135 adapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dpProfile); 136 mA2dpProfile = null; 137 } catch (Throwable t) { 138 Log.w(TAG, "Error cleaning up A2DP proxy", t); 139 } 140 } 141 } 142 143 private void registerConnectionStateReceiver() { 144 if (DEBUG) Log.d(TAG, "registerConnectionStateReceiver()"); 145 IntentFilter filter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 146 mContext.registerReceiver(mConnectionStateReceiver, filter); 147 mConnectionStateReceiverRegistered = true; 148 } 149 150 private void unregisterConnectionStateReceiver() { 151 if (mConnectionStateReceiverRegistered) { 152 if (DEBUG) Log.d(TAG, "unregisterConnectionStateReceiver()"); 153 mContext.unregisterReceiver(mConnectionStateReceiver); 154 mConnectionStateReceiverRegistered = false; 155 } 156 } 157 158} 159