blob: 01caee2c5a86636f759c95e53346be148331cb3b [file] [log] [blame]
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001/*
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 */
16package com.android.car;
17
18import android.os.Message;
19import android.util.Log;
20
21import com.android.internal.util.State;
22import com.android.internal.util.StateMachine;
23
24import 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 */
37public 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;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080049 public static final int CONNECTION_TIMEOUT_MS = 8000;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080050
51
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
67 public static BluetoothAutoConnectStateMachine make(BluetoothDeviceConnectionPolicy policy) {
68 BluetoothAutoConnectStateMachine mStateMachine = new BluetoothAutoConnectStateMachine(
69 policy);
70 mStateMachine.start();
71 return mStateMachine;
72 }
73
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
86 public void enter() {
87 if (DBG) {
88 Log.d(TAG, "Enter Idle");
89 }
90 connectToBluetoothDevice();
91 }
92
93 @Override
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 */
130 private void connectToBluetoothDevice() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800131 boolean deviceToConnectFound = mPolicy.findDeviceToConnect();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800132 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
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
157 public void enter() {
158 if (DBG) {
159 Log.d(TAG, "Enter Processing");
160 }
161
162 }
163
164 @Override
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
199 public void exit() {
200 if (DBG) {
201 Log.d(TAG, "Exit Processing");
202 }
203 }
204 }
205
206 public void dump(PrintWriter writer) {
207 writer.println("StateMachine: " + this.toString());
208 }
209
210}