Home | History | Annotate | Download | only in car
      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