1/*
2 * Copyright (C) 2011 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.settings.bluetooth;
18
19import android.bluetooth.BluetoothClass;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothHeadset;
22import android.bluetooth.BluetoothProfile;
23import android.bluetooth.BluetoothUuid;
24import android.content.Context;
25import android.os.ParcelUuid;
26import android.util.Log;
27
28import com.android.settings.R;
29
30import java.util.List;
31
32/**
33 * HeadsetProfile handles Bluetooth HFP and Headset profiles.
34 */
35final class HeadsetProfile implements LocalBluetoothProfile {
36    private static final String TAG = "HeadsetProfile";
37
38    private BluetoothHeadset mService;
39    private boolean mProfileReady;
40
41    private final LocalBluetoothAdapter mLocalAdapter;
42    private final CachedBluetoothDeviceManager mDeviceManager;
43    private final LocalBluetoothProfileManager mProfileManager;
44
45    static final ParcelUuid[] UUIDS = {
46        BluetoothUuid.HSP,
47        BluetoothUuid.Handsfree,
48    };
49
50    static final String NAME = "HEADSET";
51
52    // Order of this profile in device profiles list
53    private static final int ORDINAL = 0;
54
55    // These callbacks run on the main thread.
56    private final class HeadsetServiceListener
57            implements BluetoothProfile.ServiceListener {
58
59        public void onServiceConnected(int profile, BluetoothProfile proxy) {
60            mService = (BluetoothHeadset) proxy;
61            mProfileReady = true;
62            // We just bound to the service, so refresh the UI of the
63            // headset device.
64            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
65            if (deviceList.isEmpty()) {
66                return;
67            }
68            BluetoothDevice firstDevice = deviceList.get(0);
69            CachedBluetoothDevice device = mDeviceManager.findDevice(firstDevice);
70            // we may add a new device here, but generally this should not happen
71            if (device == null) {
72                Log.w(TAG, "HeadsetProfile found new device: " + firstDevice);
73                device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, firstDevice);
74            }
75            device.onProfileStateChanged(HeadsetProfile.this,
76                    BluetoothProfile.STATE_CONNECTED);
77
78            mProfileManager.callServiceConnectedListeners();
79        }
80
81        public void onServiceDisconnected(int profile) {
82            mProfileReady = false;
83            mService = null;
84            mProfileManager.callServiceDisconnectedListeners();
85        }
86    }
87
88    // TODO(): The calls must get queued if mService becomes null.
89    // It can happen when the phone app crashes for some reason.
90    // All callers should have service listeners. Dock Service is the only
91    // one right now.
92    HeadsetProfile(Context context, LocalBluetoothAdapter adapter,
93            CachedBluetoothDeviceManager deviceManager,
94            LocalBluetoothProfileManager profileManager) {
95        mLocalAdapter = adapter;
96        mDeviceManager = deviceManager;
97        mProfileManager = profileManager;
98        mLocalAdapter.getProfileProxy(context, new HeadsetServiceListener(),
99                BluetoothProfile.HEADSET);
100    }
101
102    public boolean isConnectable() {
103        return true;
104    }
105
106    public boolean isAutoConnectable() {
107        return true;
108    }
109
110    public boolean connect(BluetoothDevice device) {
111        List<BluetoothDevice> sinks = mService.getConnectedDevices();
112        if (sinks != null) {
113            for (BluetoothDevice sink : sinks) {
114                mService.disconnect(sink);
115            }
116        }
117        return mService.connect(device);
118    }
119
120    public boolean disconnect(BluetoothDevice device) {
121        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
122        if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
123            // Downgrade priority as user is disconnecting the headset.
124            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
125                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
126            }
127            return mService.disconnect(device);
128        } else {
129            return false;
130        }
131    }
132
133    public int getConnectionStatus(BluetoothDevice device) {
134        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
135
136        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
137
138        return !deviceList.isEmpty() && deviceList.get(0).equals(device)
139                ? mService.getConnectionState(device)
140                : BluetoothProfile.STATE_DISCONNECTED;
141    }
142
143    public boolean isPreferred(BluetoothDevice device) {
144        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
145    }
146
147    public int getPreferred(BluetoothDevice device) {
148        return mService.getPriority(device);
149    }
150
151    public void setPreferred(BluetoothDevice device, boolean preferred) {
152        if (preferred) {
153            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
154                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
155            }
156        } else {
157            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
158        }
159    }
160
161    public synchronized boolean isProfileReady() {
162        return mProfileReady;
163    }
164
165    public String toString() {
166        return NAME;
167    }
168
169    public int getOrdinal() {
170        return ORDINAL;
171    }
172
173    public int getNameResource(BluetoothDevice device) {
174        return R.string.bluetooth_profile_headset;
175    }
176
177    public int getSummaryResourceForDevice(BluetoothDevice device) {
178        int state = mService.getConnectionState(device);
179        switch (state) {
180            case BluetoothProfile.STATE_DISCONNECTED:
181                return R.string.bluetooth_headset_profile_summary_use_for;
182
183            case BluetoothProfile.STATE_CONNECTED:
184                return R.string.bluetooth_headset_profile_summary_connected;
185
186            default:
187                return Utils.getConnectionStateSummary(state);
188        }
189    }
190
191    public int getDrawableResource(BluetoothClass btClass) {
192        return R.drawable.ic_bt_headset_hfp;
193    }
194}
195