/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bandwidthtest; import android.net.NetworkInfo.State; import android.util.Log; import java.util.List; import java.util.ArrayList; /** * Data structure to keep track of the network state transitions. */ public class NetworkState { /** * Desired direction of state transition. */ public enum StateTransitionDirection { TO_DISCONNECTION, TO_CONNECTION, DO_NOTHING } private final String LOG_TAG = "NetworkState"; private List mStateDepository; private State mTransitionTarget; private StateTransitionDirection mTransitionDirection; private String mReason = null; // record mReason of state transition failure public NetworkState() { mStateDepository = new ArrayList(); mTransitionDirection = StateTransitionDirection.DO_NOTHING; mTransitionTarget = State.UNKNOWN; } public NetworkState(State currentState) { mStateDepository = new ArrayList(); mStateDepository.add(currentState); mTransitionDirection = StateTransitionDirection.DO_NOTHING; mTransitionTarget = State.UNKNOWN; } /** * Reinitialize the network state */ public void resetNetworkState() { mStateDepository.clear(); mTransitionDirection = StateTransitionDirection.DO_NOTHING; mTransitionTarget = State.UNKNOWN; } /** * Set the transition criteria * @param initState initial {@link State} * @param transitionDir explicit {@link StateTransitionDirection} * @param targetState desired {@link State} */ public void setStateTransitionCriteria(State initState, StateTransitionDirection transitionDir, State targetState) { if (!mStateDepository.isEmpty()) { mStateDepository.clear(); } mStateDepository.add(initState); mTransitionDirection = transitionDir; mTransitionTarget = targetState; Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates()); } /** * Record the current state of the network * @param currentState the current {@link State} */ public void recordState(State currentState) { mStateDepository.add(currentState); } /** * Verify the state transition * @return true if the requested transition completed successfully. */ public boolean validateStateTransition() { Log.v(LOG_TAG, String.format("Print state depository: %s", printStates())); switch (mTransitionDirection) { case DO_NOTHING: Log.v(LOG_TAG, "No direction requested, verifying network states"); return validateNetworkStates(); case TO_CONNECTION: Log.v(LOG_TAG, "Transition to CONNECTED"); return validateNetworkConnection(); case TO_DISCONNECTION: Log.v(LOG_TAG, "Transition to DISCONNECTED"); return validateNetworkDisconnection(); default: Log.e(LOG_TAG, "Invalid transition direction."); return false; } } /** * Verify that network states are valid * @return false if any of the states are invalid */ private boolean validateNetworkStates() { if (mStateDepository.isEmpty()) { Log.v(LOG_TAG, "no state is recorded"); mReason = "no state is recorded."; return false; } else if (mStateDepository.size() > 1) { Log.v(LOG_TAG, "no broadcast is expected, instead broadcast is probably received"); mReason = "no broadcast is expected, instead broadcast is probably received"; return false; } else if (mStateDepository.get(0) != mTransitionTarget) { Log.v(LOG_TAG, String.format("%s is expected, but it is %s", mTransitionTarget.toString(), mStateDepository.get(0).toString())); mReason = String.format("%s is expected, but it is %s", mTransitionTarget.toString(), mStateDepository.get(0).toString()); return false; } return true; } /** * Verify the network state to disconnection * @return false if any of the state transitions were not valid */ private boolean validateNetworkDisconnection() { // Transition from CONNECTED -> DISCONNECTED: CONNECTED->DISCONNECTING->DISCONNECTED StringBuffer str = new StringBuffer ("States: "); str.append(printStates()); if (mStateDepository.get(0) != State.CONNECTED) { str.append(String.format(" Initial state should be CONNECTED, but it is %s.", mStateDepository.get(0))); mReason = str.toString(); return false; } State lastState = mStateDepository.get(mStateDepository.size() - 1); if ( lastState != mTransitionTarget) { str.append(String.format(" Last state should be DISCONNECTED, but it is %s", lastState)); mReason = str.toString(); return false; } for (int i = 1; i < mStateDepository.size() - 1; i++) { State preState = mStateDepository.get(i-1); State curState = mStateDepository.get(i); if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) || (curState == State.DISCONNECTED))) { continue; } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) { continue; } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) { continue; } else { str.append(String.format(" Transition state from %s to %s is not valid", preState.toString(), curState.toString())); mReason = str.toString(); return false; } } mReason = str.toString(); return true; } /** * Verify the network state to connection * @return false if any of the state transitions were not valid */ private boolean validateNetworkConnection() { StringBuffer str = new StringBuffer("States "); str.append(printStates()); if (mStateDepository.get(0) != State.DISCONNECTED) { str.append(String.format(" Initial state should be DISCONNECTED, but it is %s.", mStateDepository.get(0))); mReason = str.toString(); return false; } State lastState = mStateDepository.get(mStateDepository.size() - 1); if ( lastState != mTransitionTarget) { str.append(String.format(" Last state should be %s, but it is %s", mTransitionTarget, lastState)); mReason = str.toString(); return false; } for (int i = 1; i < mStateDepository.size(); i++) { State preState = mStateDepository.get(i-1); State curState = mStateDepository.get(i); if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) || (curState == State.CONNECTED) || (curState == State.DISCONNECTED))) { continue; } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) { continue; } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) { continue; } else { str.append(String.format(" Transition state from %s to %s is not valid.", preState.toString(), curState.toString())); mReason = str.toString(); return false; } } mReason = str.toString(); return true; } /** * Fetch the different network state transitions * @return {@link List} of {@link State} */ public List getTransitionStates() { return mStateDepository; } /** * Fetch the reason for network state transition failure * @return the {@link String} for the failure */ public String getFailureReason() { return mReason; } /** * Print the network state * @return {@link String} representation of the network state */ public String printStates() { StringBuilder stateBuilder = new StringBuilder(); for (int i = 0; i < mStateDepository.size(); i++) { stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->"); } return stateBuilder.toString(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("mTransitionDirection: ").append(mTransitionDirection.toString()). append("; ").append("states:"). append(printStates()).append("; "); return builder.toString(); } }