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