1 /* 2 * Copyright (C) 2022 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 17 package com.android.server.wifi.util; 18 19 import android.os.Message; 20 21 import androidx.annotation.NonNull; 22 23 import com.android.internal.util.State; 24 import com.android.internal.util.StateMachine; 25 26 /** 27 * A generic WaitingState which will defer all messages. 28 * 29 * Note: if the state machine implements local transitions as opposed to external transitions 30 * (see https://en.wikipedia.org/wiki/UML_state_machine#Local_versus_external_transitions for a 31 * description) then this state can be added under the state which it is supposed to be blocking - 32 * it will prevent triggering local exit() and enter() methods on that state. However, the Android 33 * state machine does a mix of local and external transitions. Therefore, if the class which is 34 * being blocked has an enter() and/or exit() methods then modify the state machine structure: 35 * - Create a new container state which will have the enter() and exit() methods 36 * - Add both the original state (now without the enter() and exit() methods) and the waiting state 37 * to the container. 38 */ 39 public class WaitingState extends State { 40 private final StateMachine mParentStateMachine; 41 42 // message.what and message.getData() entries to try to guarantee no overlap with any containing 43 // state 44 private static final int MESSAGE_TYPE_TRANSITION = 0xFFFFFF; 45 private static final String MESSAGE_BUNDLE_KEY_TRANSITION_STATE_COMMAND = 46 "__waiting_state_transition_state_command"; 47 private static final String MESSAGE_BUNDLE_KEY_MESSAGE_WAS_WAITING = 48 "__waiting_state_message_was_waiting"; 49 WaitingState(StateMachine parentStateMachine)50 public WaitingState(StateMachine parentStateMachine) { 51 mParentStateMachine = parentStateMachine; 52 } 53 54 @Override processMessage(Message message)55 public boolean processMessage(Message message) { 56 if (message.what == MESSAGE_TYPE_TRANSITION && message.getData().getBoolean( 57 MESSAGE_BUNDLE_KEY_TRANSITION_STATE_COMMAND, false)) { 58 mParentStateMachine.transitionTo((State) message.obj); 59 } else { 60 message.getData().putBoolean(MESSAGE_BUNDLE_KEY_MESSAGE_WAS_WAITING, true); 61 mParentStateMachine.deferMessage(message); 62 } 63 return HANDLED; 64 } 65 66 /** 67 * Trigger a transition to another state. 68 * 69 * Note: has to be done as a message processing operation for the StateMachine to accept it 70 * and trigger state transition, deferred message processing. 71 */ sendTransitionStateCommand(State destState)72 public void sendTransitionStateCommand(State destState) { 73 Message message = mParentStateMachine.obtainMessage(MESSAGE_TYPE_TRANSITION, destState); 74 message.getData().putBoolean(MESSAGE_BUNDLE_KEY_TRANSITION_STATE_COMMAND, true); 75 mParentStateMachine.sendMessage(message); 76 } 77 78 /** 79 * Returns whether the given message was ever deferred in the WaitingState. 80 */ wasMessageInWaitingState(@onNull Message message)81 public static boolean wasMessageInWaitingState(@NonNull Message message) { 82 return message.getData().getBoolean(MESSAGE_BUNDLE_KEY_MESSAGE_WAS_WAITING, false); 83 } 84 } 85