186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal/*
286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * Copyright (C) 2017 The Android Open Source Project
386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal *
486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * Licensed under the Apache License, Version 2.0 (the "License");
586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * you may not use this file except in compliance with the License.
686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * You may obtain a copy of the License at
786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal *
886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal *      http://www.apache.org/licenses/LICENSE-2.0
986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal *
1086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * Unless required by applicable law or agreed to in writing, software
1186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * distributed under the License is distributed on an "AS IS" BASIS,
1286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * See the License for the specific language governing permissions and
1486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal * limitations under the License.
1586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal */
1686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
1786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalpackage com.android.bluetooth.btservice;
1886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
1986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.bluetooth.BluetoothA2dp;
2086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.bluetooth.BluetoothAdapter;
2186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.bluetooth.BluetoothDevice;
2286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.bluetooth.BluetoothHeadset;
2386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.bluetooth.BluetoothProfile;
2486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.bluetooth.BluetoothUuid;
2586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.content.BroadcastReceiver;
2686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.content.Context;
2786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.content.Intent;
2886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.content.IntentFilter;
2986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.os.Handler;
3086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.os.Looper;
3186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.os.Message;
3286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.os.ParcelUuid;
33c4fbd756e2645147470c486ae96f2253f5e13a52Jack Heimport android.os.Parcelable;
34928f14463ee51192039abef856495ecce418fe01Jack Heimport android.support.annotation.VisibleForTesting;
3586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport android.util.Log;
3686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
3786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport com.android.bluetooth.a2dp.A2dpService;
3809e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhangimport com.android.bluetooth.hearingaid.HearingAidService;
3986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport com.android.bluetooth.hfp.HeadsetService;
40f0429c92669aa388785a97f00985d63a83e509c2Hansong Zhangimport com.android.bluetooth.hid.HidHostService;
4186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport com.android.bluetooth.pan.PanService;
4286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport com.android.internal.R;
4386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
442cc754651ee7245e62fa247b1ca85acb6889e79eJack Heimport java.util.HashSet;
4586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalimport java.util.List;
4686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
4786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// Describes the phone policy
4886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal//
4986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// The policy should be as decoupled from the stack as possible. In an ideal world we should not
5086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// need to have this policy talk with any non-public APIs and one way to enforce that would be to
5186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// keep this file outside the Bluetooth process. Unfortunately, keeping a separate process alive is
5286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// an expensive and a tedious task.
5386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal//
5486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// Best practices:
5586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// a) PhonePolicy should be ALL private methods
5686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal//    -- Use broadcasts which can be listened in on the BroadcastReceiver
5786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// b) NEVER call from the PhonePolicy into the Java stack, unless public APIs. It is OK to call into
5886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// the non public versions as long as public versions exist (so that a 3rd party policy can mimick)
5986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// us.
6086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal//
6186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// Policy description:
6286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal//
6386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// Policies are usually governed by outside events that may warrant an action. We talk about various
6486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// events and the resulting outcome from this policy:
6586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal//
6686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// 1. Adapter turned ON: At this point we will try to auto-connect the (device, profile) pairs which
6786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// have PRIORITY_AUTO_CONNECT. The fact that we *only* auto-connect Headset and A2DP is something
6886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// that is hardcoded and specific to phone policy (see autoConnect() function)
6986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// 2. When the profile connection-state changes: At this point if a new profile gets CONNECTED we
7086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// will try to connect other profiles on the same device. This is to avoid collision if devices
7186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal// somehow end up trying to connect at same time or general connection issues.
7286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwalclass PhonePolicy {
73dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static final boolean DBG = true;
74dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static final String TAG = "BluetoothPhonePolicy";
7586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
7686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // Message types for the handler (internal messages generated by intents or timeouts)
77dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static final int MESSAGE_PROFILE_CONNECTION_STATE_CHANGED = 1;
78dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static final int MESSAGE_PROFILE_INIT_PRIORITIES = 2;
79dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static final int MESSAGE_CONNECT_OTHER_PROFILES = 3;
80dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static final int MESSAGE_ADAPTER_STATE_TURNED_ON = 4;
810c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5;
8286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
8386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // Timeouts
840c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s
8586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
86dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private final AdapterService mAdapterService;
87dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private final ServiceFactory mFactory;
88dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private final Handler mHandler;
89dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private final HashSet<BluetoothDevice> mHeadsetRetrySet = new HashSet<>();
90dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
91d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He    private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();
9286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
9386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // Broadcast receiver for all changes to states of various profiles
9486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
9586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        @Override
9686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        public void onReceive(Context context, Intent intent) {
97d94f6ddab1f088340d197086949409c144ec2b0aJack He            String action = intent.getAction();
98d94f6ddab1f088340d197086949409c144ec2b0aJack He            if (action == null) {
99d94f6ddab1f088340d197086949409c144ec2b0aJack He                errorLog("Received intent with null action");
100d94f6ddab1f088340d197086949409c144ec2b0aJack He                return;
101d94f6ddab1f088340d197086949409c144ec2b0aJack He            }
102d94f6ddab1f088340d197086949409c144ec2b0aJack He            switch (action) {
103d94f6ddab1f088340d197086949409c144ec2b0aJack He                case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
104d94f6ddab1f088340d197086949409c144ec2b0aJack He                    mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED,
105c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                            BluetoothProfile.HEADSET, -1, // No-op argument
106c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                            intent).sendToTarget();
107d94f6ddab1f088340d197086949409c144ec2b0aJack He                    break;
108d94f6ddab1f088340d197086949409c144ec2b0aJack He                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
109d94f6ddab1f088340d197086949409c144ec2b0aJack He                    mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED,
110c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                            BluetoothProfile.A2DP, -1, // No-op argument
111c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                            intent).sendToTarget();
112d94f6ddab1f088340d197086949409c144ec2b0aJack He                    break;
1130c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
1140c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    mHandler.obtainMessage(MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED,
1150c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                            BluetoothProfile.A2DP, -1, // No-op argument
1160c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                            intent).sendToTarget();
1170c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    break;
118d94f6ddab1f088340d197086949409c144ec2b0aJack He                case BluetoothAdapter.ACTION_STATE_CHANGED:
119d94f6ddab1f088340d197086949409c144ec2b0aJack He                    // Only pass the message on if the adapter has actually changed state from
120d94f6ddab1f088340d197086949409c144ec2b0aJack He                    // non-ON to ON. NOTE: ON is the state depicting BREDR ON and not just BLE ON.
121d94f6ddab1f088340d197086949409c144ec2b0aJack He                    int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
122d94f6ddab1f088340d197086949409c144ec2b0aJack He                    if (newState == BluetoothAdapter.STATE_ON) {
123d94f6ddab1f088340d197086949409c144ec2b0aJack He                        mHandler.obtainMessage(MESSAGE_ADAPTER_STATE_TURNED_ON).sendToTarget();
124d94f6ddab1f088340d197086949409c144ec2b0aJack He                    }
125d94f6ddab1f088340d197086949409c144ec2b0aJack He                    break;
126d94f6ddab1f088340d197086949409c144ec2b0aJack He                case BluetoothDevice.ACTION_UUID:
127d94f6ddab1f088340d197086949409c144ec2b0aJack He                    mHandler.obtainMessage(MESSAGE_PROFILE_INIT_PRIORITIES, intent).sendToTarget();
128d94f6ddab1f088340d197086949409c144ec2b0aJack He                    break;
129d94f6ddab1f088340d197086949409c144ec2b0aJack He                default:
130d94f6ddab1f088340d197086949409c144ec2b0aJack He                    Log.e(TAG, "Received unexpected intent, action=" + action);
131d94f6ddab1f088340d197086949409c144ec2b0aJack He                    break;
13286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            }
13386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
13486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    };
13586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
136928f14463ee51192039abef856495ecce418fe01Jack He    @VisibleForTesting
137928f14463ee51192039abef856495ecce418fe01Jack He    BroadcastReceiver getBroadcastReceiver() {
13886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        return mReceiver;
13986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
14086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
14186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // Handler to handoff intents to class thread
14286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    class PhonePolicyHandler extends Handler {
14386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        PhonePolicyHandler(Looper looper) {
14486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            super(looper);
14586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
14686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
14786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        @Override
14886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        public void handleMessage(Message msg) {
14986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            switch (msg.what) {
15086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                case MESSAGE_PROFILE_INIT_PRIORITIES: {
151d94f6ddab1f088340d197086949409c144ec2b0aJack He                    Intent intent = (Intent) msg.obj;
15286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    BluetoothDevice device =
153d94f6ddab1f088340d197086949409c144ec2b0aJack He                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
154d94f6ddab1f088340d197086949409c144ec2b0aJack He                    Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
155d94f6ddab1f088340d197086949409c144ec2b0aJack He                    debugLog("Received ACTION_UUID for device " + device);
1560068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                    if (uuids != null) {
1570068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                        ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
1580068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                        for (int i = 0; i < uuidsToSend.length; i++) {
1590068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                            uuidsToSend[i] = (ParcelUuid) uuids[i];
160d94f6ddab1f088340d197086949409c144ec2b0aJack He                            debugLog("index=" + i + "uuid=" + uuidsToSend[i]);
1610068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                        }
1620068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                        processInitProfilePriorities(device, uuidsToSend);
1630068bdad22ffd70c90de67b6e85c8a0dfa838d90Sanket Agarwal                    }
164c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
165c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                break;
16686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
16786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                case MESSAGE_PROFILE_CONNECTION_STATE_CHANGED: {
16886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    Intent intent = (Intent) msg.obj;
16986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    BluetoothDevice device =
17086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
17186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
17286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
17386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    processProfileStateChanged(device, msg.arg1, nextState, prevState);
174c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
175c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                break;
17686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
1770c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                case MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED: {
1780c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    Intent intent = (Intent) msg.obj;
1790c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    BluetoothDevice activeDevice =
1800c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1810c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    processProfileActiveDeviceChanged(activeDevice, msg.arg1);
1820c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                }
1830c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                break;
1840c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He
185d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He                case MESSAGE_CONNECT_OTHER_PROFILES: {
18686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    // Called when we try connect some profiles in processConnectOtherProfiles but
18786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    // we send a delayed message to try connecting the remaining profiles
188d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He                    BluetoothDevice device = (BluetoothDevice) msg.obj;
189d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He                    processConnectOtherProfiles(device);
190d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He                    mConnectOtherProfilesDeviceSet.remove(device);
19186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    break;
192d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He                }
19386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                case MESSAGE_ADAPTER_STATE_TURNED_ON:
19486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    // Call auto connect when adapter switches state to ON
1952cc754651ee7245e62fa247b1ca85acb6889e79eJack He                    resetStates();
19686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    autoConnect();
19786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                    break;
19886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            }
19986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
200c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    }
201c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
202c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    ;
20386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
20486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // Policy API functions for lifecycle management (protected)
20586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    protected void start() {
20686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        IntentFilter filter = new IntentFilter();
20786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
20886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
20986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        filter.addAction(BluetoothDevice.ACTION_UUID);
21086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
2110c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
21286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        mAdapterService.registerReceiver(mReceiver, filter);
21386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
214c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
21586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    protected void cleanup() {
21686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        mAdapterService.unregisterReceiver(mReceiver);
2172cc754651ee7245e62fa247b1ca85acb6889e79eJack He        resetStates();
21886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
21986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
22086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    PhonePolicy(AdapterService service, ServiceFactory factory) {
22186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        mAdapterService = service;
22286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        mFactory = factory;
22386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        mHandler = new PhonePolicyHandler(service.getMainLooper());
22486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
22586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
22686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // Policy implementation, all functions MUST be private
22786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    private void processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids) {
228d94f6ddab1f088340d197086949409c144ec2b0aJack He        debugLog("processInitProfilePriorities() - device " + device);
229f0429c92669aa388785a97f00985d63a83e509c2Hansong Zhang        HidHostService hidService = mFactory.getHidHostService();
23086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        A2dpService a2dpService = mFactory.getA2dpService();
23186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        HeadsetService headsetService = mFactory.getHeadsetService();
23286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        PanService panService = mFactory.getPanService();
23309e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhang        HearingAidService hearingAidService = mFactory.getHearingAidService();
23486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
23586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        // Set profile priorities only for the profiles discovered on the remote device.
23686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        // This avoids needless auto-connect attempts to profiles non-existent on the remote device
237c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if ((hidService != null) && (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid)
238c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                || BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) && (
239c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                hidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)) {
24086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            hidService.setPriority(device, BluetoothProfile.PRIORITY_ON);
24186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
24286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
24386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        // If we do not have a stored priority for HFP/A2DP (all roles) then default to on.
244c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if ((headsetService != null) && ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)
245c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                || BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) && (
246c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED))) {
24786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            headsetService.setPriority(device, BluetoothProfile.PRIORITY_ON);
24886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
24986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
250c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if ((a2dpService != null) && (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)
251c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                || BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AdvAudioDist)) && (
252c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                a2dpService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)) {
25386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            a2dpService.setPriority(device, BluetoothProfile.PRIORITY_ON);
25486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
25586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
256c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if ((panService != null) && (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU) && (
257c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                panService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)
258c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                && mAdapterService.getResources()
259c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) {
26086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            panService.setPriority(device, BluetoothProfile.PRIORITY_ON);
26186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
26209e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhang
2630c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if ((hearingAidService != null) && BluetoothUuid.isUuidPresent(uuids,
2640c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                BluetoothUuid.HearingAid) && (hearingAidService.getPriority(device)
2650c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                == BluetoothProfile.PRIORITY_UNDEFINED)) {
26609e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhang            debugLog("setting hearing aid profile priority for device " + device);
26709e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhang            hearingAidService.setPriority(device, BluetoothProfile.PRIORITY_ON);
26809e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhang        }
26986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
27086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
271c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState,
272c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            int prevState) {
273d94f6ddab1f088340d197086949409c144ec2b0aJack He        debugLog("processProfileStateChanged, device=" + device + ", profile=" + profileId + ", "
274d94f6ddab1f088340d197086949409c144ec2b0aJack He                + prevState + " -> " + nextState);
275d8ae37693406af4fa2633092ec9500a484933a9eJack He        if (((profileId == BluetoothProfile.A2DP) || (profileId == BluetoothProfile.HEADSET))) {
276d8ae37693406af4fa2633092ec9500a484933a9eJack He            if (nextState == BluetoothProfile.STATE_CONNECTED) {
277d8ae37693406af4fa2633092ec9500a484933a9eJack He                switch (profileId) {
278d8ae37693406af4fa2633092ec9500a484933a9eJack He                    case BluetoothProfile.A2DP:
279d8ae37693406af4fa2633092ec9500a484933a9eJack He                        mA2dpRetrySet.remove(device);
280d8ae37693406af4fa2633092ec9500a484933a9eJack He                        break;
281d8ae37693406af4fa2633092ec9500a484933a9eJack He                    case BluetoothProfile.HEADSET:
282d8ae37693406af4fa2633092ec9500a484933a9eJack He                        mHeadsetRetrySet.remove(device);
283d8ae37693406af4fa2633092ec9500a484933a9eJack He                        break;
284d8ae37693406af4fa2633092ec9500a484933a9eJack He                }
285d8ae37693406af4fa2633092ec9500a484933a9eJack He                connectOtherProfile(device);
286d8ae37693406af4fa2633092ec9500a484933a9eJack He            }
287d8ae37693406af4fa2633092ec9500a484933a9eJack He            if (prevState == BluetoothProfile.STATE_CONNECTING
288d8ae37693406af4fa2633092ec9500a484933a9eJack He                    && nextState == BluetoothProfile.STATE_DISCONNECTED) {
2890c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                HeadsetService hsService = mFactory.getHeadsetService();
2900c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                boolean hsDisconnected = hsService == null || hsService.getConnectionState(device)
2910c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                        == BluetoothProfile.STATE_DISCONNECTED;
2920c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                A2dpService a2dpService = mFactory.getA2dpService();
2930c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                boolean a2dpDisconnected = a2dpService == null
2940c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                        || a2dpService.getConnectionState(device)
2950c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                        == BluetoothProfile.STATE_DISCONNECTED;
2960c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                debugLog("processProfileStateChanged, device=" + device + ", a2dpDisconnected="
2970c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                        + a2dpDisconnected + ", hsDisconnected=" + hsDisconnected);
2980c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                if (hsDisconnected && a2dpDisconnected) {
2990c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    removeAutoConnectFromA2dpSink(device);
3000c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    removeAutoConnectFromHeadset(device);
3010c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                }
3022cc754651ee7245e62fa247b1ca85acb6889e79eJack He            }
3030c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        }
3040c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    }
305d8ae37693406af4fa2633092ec9500a484933a9eJack He
3060c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void processProfileActiveDeviceChanged(BluetoothDevice activeDevice, int profileId) {
3070c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        debugLog("processProfileActiveDeviceChanged, activeDevice=" + activeDevice + ", profile="
3080c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                + profileId);
3090c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        switch (profileId) {
3100c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            // Tracking active device changed intent only for A2DP so that we always connect to a
3110c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            // single device after toggling Bluetooth
3120c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            case BluetoothProfile.A2DP:
3130c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                // Ignore null active device since we don't know if the change is triggered by
3140c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                // normal device disconnection during Bluetooth shutdown or user action
3150c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                if (activeDevice == null) {
3160c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    warnLog("processProfileActiveDeviceChanged: ignore null A2DP active device");
3170c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    return;
3180c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                }
3190c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
3200c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    removeAutoConnectFromA2dpSink(device);
3210c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    removeAutoConnectFromHeadset(device);
3220c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                }
3230c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                setAutoConnectForA2dpSink(activeDevice);
3240c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                setAutoConnectForHeadset(activeDevice);
3250c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                break;
32686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
32786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
32886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
3292cc754651ee7245e62fa247b1ca85acb6889e79eJack He    private void resetStates() {
3302cc754651ee7245e62fa247b1ca85acb6889e79eJack He        mHeadsetRetrySet.clear();
3312cc754651ee7245e62fa247b1ca85acb6889e79eJack He        mA2dpRetrySet.clear();
3322cc754651ee7245e62fa247b1ca85acb6889e79eJack He    }
3332cc754651ee7245e62fa247b1ca85acb6889e79eJack He
33486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    private void autoConnect() {
33586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
3360c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            errorLog("autoConnect: BT is not ON. Exiting autoConnect");
33786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            return;
33886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
33986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
340d94f6ddab1f088340d197086949409c144ec2b0aJack He        if (!mAdapterService.isQuietModeEnabled()) {
3410c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("autoConnect: Initiate auto connection on BT on...");
3420c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
3430c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            if (bondedDevices == null) {
3440c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                errorLog("autoConnect: bondedDevices are null");
3450c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                return;
3460c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            }
3470c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            for (BluetoothDevice device : bondedDevices) {
3480c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                autoConnectHeadset(device);
3490c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                autoConnectA2dp(device);
3500c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            }
35186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        } else {
35286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            debugLog("autoConnect() - BT is in quiet mode. Not initiating auto connections");
35386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
35486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
35586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
3560c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void autoConnectA2dp(BluetoothDevice device) {
3570c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        final A2dpService a2dpService = mFactory.getA2dpService();
3580c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (a2dpService == null) {
3590c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            warnLog("autoConnectA2dp: service is null, failed to connect to " + device);
36086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            return;
36186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
3620c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        int a2dpPriority = a2dpService.getPriority(device);
3630c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (a2dpPriority == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
3640c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("autoConnectA2dp: connecting A2DP with " + device);
3650c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            a2dpService.connect(device);
3660c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        } else {
3670c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("autoConnectA2dp: skipped auto-connect A2DP with device " + device
3680c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    + " priority " + a2dpPriority);
36986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
37086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
37186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
3720c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void autoConnectHeadset(BluetoothDevice device) {
3730c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        final HeadsetService hsService = mFactory.getHeadsetService();
3740c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (hsService == null) {
3750c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            warnLog("autoConnectHeadset: service is null, failed to connect to " + device);
37686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            return;
37786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
3780c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        int headsetPriority = hsService.getPriority(device);
3790c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (headsetPriority == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
3800c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("autoConnectHeadset: Connecting HFP with " + device);
3810c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            hsService.connect(device);
3820c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        } else {
3830c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("autoConnectHeadset: skipped auto-connect HFP with device " + device
3840c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He                    + " priority " + headsetPriority);
38586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
38686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
38786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
388d94f6ddab1f088340d197086949409c144ec2b0aJack He    private void connectOtherProfile(BluetoothDevice device) {
389d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        if (mAdapterService.isQuietModeEnabled()) {
390d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He            debugLog("connectOtherProfile: in quiet mode, skip connect other profile " + device);
391d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He            return;
392d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        }
393d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        if (mConnectOtherProfilesDeviceSet.contains(device)) {
394d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He            debugLog("connectOtherProfile: already scheduled callback for " + device);
395d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He            return;
39686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
397d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        mConnectOtherProfilesDeviceSet.add(device);
398d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        Message m = mHandler.obtainMessage(MESSAGE_CONNECT_OTHER_PROFILES);
399d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        m.obj = device;
400d617142a6e7567c6ae46fbbbebb58e14900cb3a1Jack He        mHandler.sendMessageDelayed(m, sConnectOtherProfilesTimeoutMillis);
40186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
40286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
40386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // This function is called whenever a profile is connected.  This allows any other bluetooth
40486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // profiles which are not already connected or in the process of connecting to attempt to
40586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // connect to the device that initiated the connection.  In the event that this function is
40686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    // invoked and there are no current bluetooth connections no new profiles will be connected.
40786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    private void processConnectOtherProfiles(BluetoothDevice device) {
408d94f6ddab1f088340d197086949409c144ec2b0aJack He        debugLog("processConnectOtherProfiles, device=" + device);
40986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
410d94f6ddab1f088340d197086949409c144ec2b0aJack He            warnLog("processConnectOtherProfiles, adapter is not ON " + mAdapterService.getState());
41186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            return;
41286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
41386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        HeadsetService hsService = mFactory.getHeadsetService();
41486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        A2dpService a2dpService = mFactory.getA2dpService();
41586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        PanService panService = mFactory.getPanService();
41686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
4173cc3ca76069837340db03751e64b6df230e5d8ccJack He        boolean atLeastOneProfileConnectedForDevice = false;
41886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        boolean allProfilesEmpty = true;
41986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        List<BluetoothDevice> a2dpConnDevList = null;
42086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        List<BluetoothDevice> hsConnDevList = null;
42186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        List<BluetoothDevice> panConnDevList = null;
42286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
42386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (hsService != null) {
42486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            hsConnDevList = hsService.getConnectedDevices();
4253cc3ca76069837340db03751e64b6df230e5d8ccJack He            allProfilesEmpty &= hsConnDevList.isEmpty();
4263cc3ca76069837340db03751e64b6df230e5d8ccJack He            atLeastOneProfileConnectedForDevice |= hsConnDevList.contains(device);
42786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
42886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (a2dpService != null) {
42986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            a2dpConnDevList = a2dpService.getConnectedDevices();
4303cc3ca76069837340db03751e64b6df230e5d8ccJack He            allProfilesEmpty &= a2dpConnDevList.isEmpty();
4313cc3ca76069837340db03751e64b6df230e5d8ccJack He            atLeastOneProfileConnectedForDevice |= a2dpConnDevList.contains(device);
43286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
43386c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (panService != null) {
43486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            panConnDevList = panService.getConnectedDevices();
4353cc3ca76069837340db03751e64b6df230e5d8ccJack He            allProfilesEmpty &= panConnDevList.isEmpty();
4363cc3ca76069837340db03751e64b6df230e5d8ccJack He            atLeastOneProfileConnectedForDevice |= panConnDevList.contains(device);
43786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
43886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
4393cc3ca76069837340db03751e64b6df230e5d8ccJack He        if (!atLeastOneProfileConnectedForDevice) {
4403cc3ca76069837340db03751e64b6df230e5d8ccJack He            // Consider this device as fully disconnected, don't bother connecting others
441d94f6ddab1f088340d197086949409c144ec2b0aJack He            debugLog("processConnectOtherProfiles, all profiles disconnected for " + device);
4423cc3ca76069837340db03751e64b6df230e5d8ccJack He            mHeadsetRetrySet.remove(device);
4433cc3ca76069837340db03751e64b6df230e5d8ccJack He            mA2dpRetrySet.remove(device);
4443cc3ca76069837340db03751e64b6df230e5d8ccJack He            if (allProfilesEmpty) {
4453cc3ca76069837340db03751e64b6df230e5d8ccJack He                debugLog("processConnectOtherProfiles, all profiles disconnected for all devices");
4463cc3ca76069837340db03751e64b6df230e5d8ccJack He                // reset retry status so that in the next round we can start retrying connections
4473cc3ca76069837340db03751e64b6df230e5d8ccJack He                resetStates();
4483cc3ca76069837340db03751e64b6df230e5d8ccJack He            }
44986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            return;
45086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
45186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
45286c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (hsService != null) {
4533cc3ca76069837340db03751e64b6df230e5d8ccJack He            if (!mHeadsetRetrySet.contains(device) && (hsService.getPriority(device)
4543cc3ca76069837340db03751e64b6df230e5d8ccJack He                    >= BluetoothProfile.PRIORITY_ON) && (hsService.getConnectionState(device)
4553cc3ca76069837340db03751e64b6df230e5d8ccJack He                    == BluetoothProfile.STATE_DISCONNECTED)) {
456d94f6ddab1f088340d197086949409c144ec2b0aJack He                debugLog("Retrying connection to Headset with device " + device);
4572cc754651ee7245e62fa247b1ca85acb6889e79eJack He                mHeadsetRetrySet.add(device);
45886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                hsService.connect(device);
45986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            }
46086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
46186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (a2dpService != null) {
4623cc3ca76069837340db03751e64b6df230e5d8ccJack He            if (!mA2dpRetrySet.contains(device) && (a2dpService.getPriority(device)
4633cc3ca76069837340db03751e64b6df230e5d8ccJack He                    >= BluetoothProfile.PRIORITY_ON) && (a2dpService.getConnectionState(device)
4643cc3ca76069837340db03751e64b6df230e5d8ccJack He                    == BluetoothProfile.STATE_DISCONNECTED)) {
46586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                debugLog("Retrying connection to A2DP with device " + device);
4662cc754651ee7245e62fa247b1ca85acb6889e79eJack He                mA2dpRetrySet.add(device);
46786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                a2dpService.connect(device);
46886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            }
46986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
47086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        if (panService != null) {
47144ef48a20624b290ae614af344c802d6e749eba2Pavlin Radoslavov            // TODO: the panConnDevList.isEmpty() check below should be removed once
47244ef48a20624b290ae614af344c802d6e749eba2Pavlin Radoslavov            // Multi-PAN is supported.
473c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (panConnDevList.isEmpty() && (panService.getPriority(device)
474c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    >= BluetoothProfile.PRIORITY_ON) && (panService.getConnectionState(device)
475c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    == BluetoothProfile.STATE_DISCONNECTED)) {
476d94f6ddab1f088340d197086949409c144ec2b0aJack He                debugLog("Retrying connection to PAN with device " + device);
47786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal                panService.connect(device);
47886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal            }
47986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
48086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
48186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
4820c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    /**
4830c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * Set a device's headset profile priority to PRIORITY_AUTO_CONNECT if device support that
4840c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * profile
4850c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     *
4860c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * @param device device whose headset profile priority should be PRIORITY_AUTO_CONNECT
4870c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     */
4880c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void setAutoConnectForHeadset(BluetoothDevice device) {
4890c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        HeadsetService hsService = mFactory.getHeadsetService();
4900c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (hsService == null) {
4910c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            warnLog("setAutoConnectForHeadset: HEADSET service is null");
4920c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            return;
4930c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        }
4940c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
4950c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("setAutoConnectForHeadset: device " + device + " PRIORITY_AUTO_CONNECT");
4960c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            hsService.setPriority(device, BluetoothProfile.PRIORITY_AUTO_CONNECT);
49786c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
49886c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
49986c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
5000c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    /**
5010c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * Set a device's A2DP profile priority to PRIORITY_AUTO_CONNECT if device support that profile
5020c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     *
5030c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * @param device device whose headset profile priority should be PRIORITY_AUTO_CONNECT
5040c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     */
5050c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void setAutoConnectForA2dpSink(BluetoothDevice device) {
5060c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        A2dpService a2dpService = mFactory.getA2dpService();
5070c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (a2dpService == null) {
5080c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            warnLog("setAutoConnectForA2dpSink: A2DP service is null");
5090c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            return;
5100c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        }
5110c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
5120c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("setAutoConnectForA2dpSink: device " + device + " PRIORITY_AUTO_CONNECT");
5130c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            a2dpService.setPriority(device, BluetoothProfile.PRIORITY_AUTO_CONNECT);
51486c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
51586c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
51686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal
5170c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    /**
5180c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * Remove PRIORITY_AUTO_CONNECT from all headsets and set headset that used to have
5190c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * PRIORITY_AUTO_CONNECT to PRIORITY_ON
5200c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     *
5210c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * @param device device whose PRIORITY_AUTO_CONNECT priority should be removed
5220c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     */
5230c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void removeAutoConnectFromHeadset(BluetoothDevice device) {
5240c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        HeadsetService hsService = mFactory.getHeadsetService();
5250c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (hsService == null) {
5260c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            warnLog("removeAutoConnectFromHeadset: HEADSET service is null");
5270c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            return;
5280c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        }
5290c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT) {
5300c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("removeAutoConnectFromHeadset: device " + device + " PRIORITY_ON");
5310c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            hsService.setPriority(device, BluetoothProfile.PRIORITY_ON);
5320c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        }
5330c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    }
5340c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He
5350c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    /**
5360c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * Remove PRIORITY_AUTO_CONNECT from all A2DP sinks and set A2DP sink that used to have
5370c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * PRIORITY_AUTO_CONNECT to PRIORITY_ON
5380c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     *
5390c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     * @param device device whose PRIORITY_AUTO_CONNECT priority should be removed
5400c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He     */
5410c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He    private void removeAutoConnectFromA2dpSink(BluetoothDevice device) {
5420c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        A2dpService a2dpService = mFactory.getA2dpService();
5430c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (a2dpService == null) {
5440c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            warnLog("removeAutoConnectFromA2dpSink: A2DP service is null");
5450c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            return;
5460c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        }
5470c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He        if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT) {
5480c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            debugLog("removeAutoConnectFromA2dpSink: device " + device + " PRIORITY_ON");
5490c3e89e90ff670dd4356e9e31565e15aaea3abc9Jack He            a2dpService.setPriority(device, BluetoothProfile.PRIORITY_ON);
55086c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal        }
55186c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal    }
552d94f6ddab1f088340d197086949409c144ec2b0aJack He
553d94f6ddab1f088340d197086949409c144ec2b0aJack He    private static void debugLog(String msg) {
554c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (DBG) {
55509e97f4cfcca3564c8daa864ede0eee172e9d5aaHansong Zhang            Log.i(TAG, msg);
556c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
557d94f6ddab1f088340d197086949409c144ec2b0aJack He    }
558d94f6ddab1f088340d197086949409c144ec2b0aJack He
559d94f6ddab1f088340d197086949409c144ec2b0aJack He    private static void warnLog(String msg) {
560d94f6ddab1f088340d197086949409c144ec2b0aJack He        Log.w(TAG, msg);
561d94f6ddab1f088340d197086949409c144ec2b0aJack He    }
562d94f6ddab1f088340d197086949409c144ec2b0aJack He
563d94f6ddab1f088340d197086949409c144ec2b0aJack He    private static void errorLog(String msg) {
564d94f6ddab1f088340d197086949409c144ec2b0aJack He        Log.e(TAG, msg);
565d94f6ddab1f088340d197086949409c144ec2b0aJack He    }
56686c29fe88456bdcfbd4334647b04ef81ff384a06Sanket Agarwal}
567