Home | History | Annotate | Download | only in bandwidthtest
      1 /*
      2  * Copyright (C) 2011 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.bandwidthtest;
     18 
     19 import android.net.NetworkInfo.State;
     20 import android.util.Log;
     21 
     22 import java.util.List;
     23 import java.util.ArrayList;
     24 
     25 /**
     26  * Data structure to keep track of the network state transitions.
     27  */
     28 public class NetworkState {
     29     /**
     30      * Desired direction of state transition.
     31      */
     32     public enum StateTransitionDirection {
     33         TO_DISCONNECTION, TO_CONNECTION, DO_NOTHING
     34     }
     35     private final String LOG_TAG = "NetworkState";
     36     private List<State> mStateDepository;
     37     private State mTransitionTarget;
     38     private StateTransitionDirection mTransitionDirection;
     39     private String mReason = null;         // record mReason of state transition failure
     40 
     41     public NetworkState() {
     42         mStateDepository = new ArrayList<State>();
     43         mTransitionDirection = StateTransitionDirection.DO_NOTHING;
     44         mTransitionTarget = State.UNKNOWN;
     45     }
     46 
     47     public NetworkState(State currentState) {
     48         mStateDepository = new ArrayList<State>();
     49         mStateDepository.add(currentState);
     50         mTransitionDirection = StateTransitionDirection.DO_NOTHING;
     51         mTransitionTarget = State.UNKNOWN;
     52     }
     53 
     54     /**
     55      * Reinitialize the network state
     56      */
     57     public void resetNetworkState() {
     58         mStateDepository.clear();
     59         mTransitionDirection = StateTransitionDirection.DO_NOTHING;
     60         mTransitionTarget = State.UNKNOWN;
     61     }
     62 
     63     /**
     64      * Set the transition criteria
     65      * @param initState initial {@link State}
     66      * @param transitionDir explicit {@link StateTransitionDirection}
     67      * @param targetState desired {@link State}
     68      */
     69     public void setStateTransitionCriteria(State initState, StateTransitionDirection transitionDir,
     70             State targetState) {
     71         if (!mStateDepository.isEmpty()) {
     72             mStateDepository.clear();
     73         }
     74         mStateDepository.add(initState);
     75         mTransitionDirection = transitionDir;
     76         mTransitionTarget = targetState;
     77         Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
     78     }
     79 
     80     /**
     81      * Record the current state of the network
     82      * @param currentState  the current {@link State}
     83      */
     84     public void recordState(State currentState) {
     85         mStateDepository.add(currentState);
     86     }
     87 
     88     /**
     89      * Verify the state transition
     90      * @return true if the requested transition completed successfully.
     91      */
     92     public boolean validateStateTransition() {
     93         Log.v(LOG_TAG, String.format("Print state depository: %s", printStates()));
     94         switch (mTransitionDirection) {
     95             case DO_NOTHING:
     96                 Log.v(LOG_TAG, "No direction requested, verifying network states");
     97                 return validateNetworkStates();
     98             case TO_CONNECTION:
     99                 Log.v(LOG_TAG, "Transition to CONNECTED");
    100                 return validateNetworkConnection();
    101             case TO_DISCONNECTION:
    102                 Log.v(LOG_TAG, "Transition to DISCONNECTED");
    103                 return validateNetworkDisconnection();
    104             default:
    105                 Log.e(LOG_TAG, "Invalid transition direction.");
    106                 return false;
    107         }
    108     }
    109 
    110     /**
    111      * Verify that network states are valid
    112      * @return false if any of the states are invalid
    113      */
    114     private boolean validateNetworkStates() {
    115         if (mStateDepository.isEmpty()) {
    116             Log.v(LOG_TAG, "no state is recorded");
    117             mReason = "no state is recorded.";
    118             return false;
    119         } else if (mStateDepository.size() > 1) {
    120             Log.v(LOG_TAG, "no broadcast is expected, instead broadcast is probably received");
    121             mReason = "no broadcast is expected, instead broadcast is probably received";
    122             return false;
    123         } else if (mStateDepository.get(0) != mTransitionTarget) {
    124             Log.v(LOG_TAG, String.format("%s is expected, but it is %s",
    125                     mTransitionTarget.toString(),
    126                     mStateDepository.get(0).toString()));
    127             mReason = String.format("%s is expected, but it is %s",
    128                     mTransitionTarget.toString(),
    129                     mStateDepository.get(0).toString());
    130             return false;
    131         }
    132         return true;
    133     }
    134 
    135     /**
    136      * Verify the network state to disconnection
    137      * @return false if any of the state transitions were not valid
    138      */
    139     private boolean validateNetworkDisconnection() {
    140         // Transition from CONNECTED -> DISCONNECTED: CONNECTED->DISCONNECTING->DISCONNECTED
    141         StringBuffer str = new StringBuffer ("States: ");
    142         str.append(printStates());
    143         if (mStateDepository.get(0) != State.CONNECTED) {
    144             str.append(String.format(" Initial state should be CONNECTED, but it is %s.",
    145                     mStateDepository.get(0)));
    146             mReason = str.toString();
    147             return false;
    148         }
    149         State lastState = mStateDepository.get(mStateDepository.size() - 1);
    150         if ( lastState != mTransitionTarget) {
    151             str.append(String.format(" Last state should be DISCONNECTED, but it is %s",
    152                     lastState));
    153             mReason = str.toString();
    154             return false;
    155         }
    156         for (int i = 1; i < mStateDepository.size() - 1; i++) {
    157             State preState = mStateDepository.get(i-1);
    158             State curState = mStateDepository.get(i);
    159             if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
    160                     (curState == State.DISCONNECTED))) {
    161                 continue;
    162             } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
    163                 continue;
    164             } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
    165                 continue;
    166             } else {
    167                 str.append(String.format(" Transition state from %s to %s is not valid",
    168                         preState.toString(), curState.toString()));
    169                 mReason = str.toString();
    170                 return false;
    171             }
    172         }
    173         mReason = str.toString();
    174         return true;
    175     }
    176 
    177     /**
    178      * Verify the network state to connection
    179      * @return false if any of the state transitions were not valid
    180      */
    181     private boolean validateNetworkConnection() {
    182         StringBuffer str = new StringBuffer("States ");
    183         str.append(printStates());
    184         if (mStateDepository.get(0) != State.DISCONNECTED) {
    185             str.append(String.format(" Initial state should be DISCONNECTED, but it is %s.",
    186                     mStateDepository.get(0)));
    187             mReason = str.toString();
    188             return false;
    189         }
    190         State lastState = mStateDepository.get(mStateDepository.size() - 1);
    191         if ( lastState != mTransitionTarget) {
    192             str.append(String.format(" Last state should be %s, but it is %s", mTransitionTarget,
    193                     lastState));
    194             mReason = str.toString();
    195             return false;
    196         }
    197         for (int i = 1; i < mStateDepository.size(); i++) {
    198             State preState = mStateDepository.get(i-1);
    199             State curState = mStateDepository.get(i);
    200             if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
    201                     (curState == State.CONNECTED) || (curState == State.DISCONNECTED))) {
    202                 continue;
    203             } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
    204                 continue;
    205             } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
    206                 continue;
    207             } else {
    208                 str.append(String.format(" Transition state from %s to %s is not valid.",
    209                         preState.toString(), curState.toString()));
    210                 mReason = str.toString();
    211                 return false;
    212             }
    213         }
    214         mReason = str.toString();
    215         return true;
    216     }
    217 
    218     /**
    219      * Fetch the different network state transitions
    220      * @return {@link List} of {@link State}
    221      */
    222     public List<State> getTransitionStates() {
    223         return mStateDepository;
    224     }
    225 
    226     /**
    227      * Fetch the reason for network state transition failure
    228      * @return the {@link String} for the failure
    229      */
    230     public String getFailureReason() {
    231         return mReason;
    232     }
    233 
    234     /**
    235      * Print the network state
    236      * @return {@link String} representation of the network state
    237      */
    238     public String printStates() {
    239         StringBuilder stateBuilder = new StringBuilder();
    240         for (int i = 0; i < mStateDepository.size(); i++) {
    241             stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
    242         }
    243         return stateBuilder.toString();
    244     }
    245 
    246     /**
    247      * {@inheritDoc}
    248      */
    249     @Override
    250     public String toString() {
    251         StringBuilder builder = new StringBuilder();
    252         builder.append("mTransitionDirection: ").append(mTransitionDirection.toString()).
    253         append("; ").append("states:").
    254         append(printStates()).append("; ");
    255         return builder.toString();
    256     }
    257 }
    258