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