• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.car;
17 
18 import android.bluetooth.BluetoothDevice;
19 import android.bluetooth.BluetoothProfile;
20 import android.os.Message;
21 import android.util.Log;
22 
23 import com.android.internal.util.State;
24 import com.android.internal.util.StateMachine;
25 
26 import java.io.PrintWriter;
27 
28 /**
29  * BluetoothAutoConnectStateMachine is a simple state machine to manage automatic bluetooth
30  * connection attempts.  It has 2 states Idle & Processing.
31  * Idle is the starting state. Incoming 'CONNECT' message is honored and connection attempts are
32  * triggered.  A Connection Timeout is also set before transitioning to Processing State
33  * Processing state ignores any incoming 'CONNECT' requests from any of the vehicle signals,
34  * since it is already in the middle of a connection attempt.  Processing moves back to Idle, when
35  * either
36  * 1. All the connections are made.
37  * 2. All connection attempts failed and there is nothing else to try.
38  */
39 public class BluetoothAutoConnectStateMachine extends StateMachine {
40     private static final String TAG = "BTAutoConnStateMachine";
41     private static final boolean DBG = Utils.DBG;
42     private final BluetoothDeviceConnectionPolicy mPolicy;
43     private final Idle mIdle;
44     private final Processing mProcessing;
45     // The messages handled by the States in the State Machine
46     public static final int CONNECT = 101;
47     public static final int DISCONNECT = 102;
48     public static final int CONNECT_TIMEOUT = 103;
49     public static final int DEVICE_CONNECTED = 104;
50     public static final int DEVICE_DISCONNECTED = 105;
51     // The following is used when PBAP and MAP should be connected to,
52     // after device connects on HFP.
53     public static final int CHECK_CLIENT_PROFILES = 1006;
54 
55     public static final int CONNECTION_TIMEOUT_MS = 8000;
56     static final int CONNECT_MORE_PROFILES_TIMEOUT_MS = 2000;
57 
58 
BluetoothAutoConnectStateMachine(BluetoothDeviceConnectionPolicy policy)59     BluetoothAutoConnectStateMachine(BluetoothDeviceConnectionPolicy policy) {
60         super(TAG);
61         mPolicy = policy;
62 
63         // Two supported states -
64         // Idle when ready to accept connections
65         // Processing when in the middle of a connection attempt.
66         mIdle = new Idle();
67         mProcessing = new Processing();
68 
69         addState(mIdle);
70         addState(mProcessing);
71         setInitialState(mIdle);
72     }
73 
make(BluetoothDeviceConnectionPolicy policy)74     public static BluetoothAutoConnectStateMachine make(BluetoothDeviceConnectionPolicy policy) {
75         BluetoothAutoConnectStateMachine mStateMachine = new BluetoothAutoConnectStateMachine(
76                 policy);
77         mStateMachine.start();
78         return mStateMachine;
79     }
80 
doQuit()81     public void doQuit() {
82         quitNow();
83     }
84 
85     /**
86      * Idle State is the Initial State, when the system is accepting incoming 'CONNECT' requests.
87      * Attempts a connection whenever the state transitions into Idle.
88      * If the policy finds a device to connect on a profile, transitions to Processing.
89      * If there is nothing to connect to, wait for the next 'CONNECT' message to try next.
90      */
91     private class Idle extends State {
92         @Override
enter()93         public void enter() {
94             if (DBG) {
95                 Log.d(TAG, "Enter Idle");
96             }
97             connectToBluetoothDevice();
98         }
99 
100         @Override
processMessage(Message msg)101         public boolean processMessage(Message msg) {
102             if (DBG) {
103                 Log.d(TAG, "Idle processMessage " + msg.what);
104             }
105             switch (msg.what) {
106                 case CONNECT: {
107                     if (DBG) {
108                         Log.d(TAG, "Idle->Connect:");
109                     }
110                     connectToBluetoothDevice();
111                     break;
112                 }
113 
114                 case DEVICE_CONNECTED: {
115                     if (DBG) {
116                         Log.d(TAG, "Idle->DeviceConnected: Ignored");
117                     }
118                     break;
119                 }
120 
121                 case CHECK_CLIENT_PROFILES: {
122                     removeMessages(CHECK_CLIENT_PROFILES);
123                     BluetoothDeviceConnectionPolicy.ConnectionParams params =
124                             (BluetoothDeviceConnectionPolicy.ConnectionParams) msg.obj;
125                     BluetoothDevice device = params.getBluetoothDevice();
126                     // After pairing/disconnect, always try to connect to both PBAP and MAP
127                     if (DBG) {
128                         Log.d(TAG, "try to connect to PBAP/MAP after pairing or disconnect: "
129                                 + Utils.getDeviceDebugInfo(device));
130                     }
131                     mPolicy.connectToDeviceOnProfile(BluetoothProfile.PBAP_CLIENT, device);
132                     mPolicy.connectToDeviceOnProfile(BluetoothProfile.MAP_CLIENT, device);
133                     break;
134                 }
135 
136                 default: {
137                     if (DBG) {
138                         Log.d(TAG, "Idle->Unhandled Msg; " + msg.what);
139                     }
140                     return false;
141                 }
142             }
143             return true;
144         }
145 
146         /**
147          * Instruct the policy to find and connect to a device on a connectable profile.
148          * If the policy reports that there is nothing to connect to, stay in the Idle state.
149          * If it found a {device, profile} combination to attempt a connection, move to
150          * Processing state
151          */
connectToBluetoothDevice()152         private void connectToBluetoothDevice() {
153             boolean deviceToConnectFound = mPolicy.findDeviceToConnect();
154             if (deviceToConnectFound) {
155                 transitionTo(mProcessing);
156             } else {
157                 // Stay in Idle State and wait for the next 'CONNECT' message.
158                 if (DBG) {
159                     Log.d(TAG, "Idle->No device to connect");
160                 }
161             }
162         }
163 
164         @Override
exit()165         public void exit() {
166             if (DBG) {
167                 Log.d(TAG, "Exit Idle");
168             }
169         }
170 
171     }
172 
173     /**
174      * Processing state indicates the system is processing a auto connect trigger and will ignore
175      * connection requests.
176      */
177     private class Processing extends State {
178         @Override
enter()179         public void enter() {
180             if (DBG) {
181                 Log.d(TAG, "Enter Processing");
182             }
183 
184         }
185 
186         @Override
processMessage(Message msg)187         public boolean processMessage(Message msg) {
188             if (DBG) {
189                 Log.d(TAG, "Processing processMessage " + msg.what);
190             }
191             BluetoothDeviceConnectionPolicy.ConnectionParams params;
192             switch (msg.what) {
193                 case CONNECT_TIMEOUT: {
194                     if (DBG) {
195                         Log.d(TAG, "Connection Timeout");
196                     }
197                     params = (BluetoothDeviceConnectionPolicy.ConnectionParams) msg.obj;
198                     mPolicy.updateDeviceConnectionStatus(params, false);
199                     transitionTo(mIdle);
200                     break;
201                 }
202 
203                 case DEVICE_CONNECTED:
204                     // fall through
205                 case DEVICE_DISCONNECTED: {
206                     removeMessages(CONNECT_TIMEOUT);
207                     transitionTo(mIdle);
208                     break;
209                 }
210 
211                 default:
212                     if (DBG) {
213                         Log.d(TAG, "Processing->Unhandled Msg: " + msg.what);
214                     }
215                     return false;
216             }
217             return true;
218         }
219 
220         @Override
exit()221         public void exit() {
222             if (DBG) {
223                 Log.d(TAG, "Exit Processing");
224             }
225         }
226     }
227 
dump(PrintWriter writer)228     public void dump(PrintWriter writer) {
229         writer.println("StateMachine: " + this.toString());
230     }
231 
232 }
233