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 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() { 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 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 } 211