1/*
2 * Copyright (C) 2010 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
17package com.android.connectivitymanagertest;
18
19import android.net.NetworkInfo.State;
20import android.util.Log;
21
22import java.util.List;
23import java.util.ArrayList;
24
25public class NetworkState {
26    public static final int TO_DISCONNECTION = 0; // transition to disconnection
27    public static final int TO_CONNECTION = 1; // transition to connection
28    public static final int DO_NOTHING = -1;   // no state change
29    private final String LOG_TAG = "NetworkState";
30    private List<State> mStateDepository;
31    private State mTransitionTarget;
32    private int mTransitionDirection;
33    private String mReason = null;         // record mReason of state transition failure
34
35    public NetworkState() {
36        mStateDepository = new ArrayList<State>();
37        mTransitionDirection = DO_NOTHING;
38        mTransitionTarget = State.UNKNOWN;
39    }
40
41    public NetworkState(State currentState) {
42        mStateDepository = new ArrayList<State>();
43        mStateDepository.add(currentState);
44        mTransitionDirection = DO_NOTHING;
45        mTransitionTarget = State.UNKNOWN;
46    }
47
48    // Reinitialize the network state
49    public void resetNetworkState() {
50        mStateDepository.clear();
51        mTransitionDirection = DO_NOTHING;
52        mTransitionTarget = State.UNKNOWN;
53    }
54
55    // set the transition criteria, transitionDir could be:
56    // DO_NOTHING, TO_CONNECTION, TO_DISCONNECTION
57    public void setStateTransitionCriteria(State initState, int transitionDir, State targetState) {
58        if (!mStateDepository.isEmpty()) {
59            mStateDepository.clear();
60        }
61        mStateDepository.add(initState);
62        mTransitionDirection = transitionDir;
63        mTransitionTarget = targetState;
64        Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
65    }
66
67    public void recordState(State currentState) {
68        mStateDepository.add(currentState);
69    }
70
71    // Verify state transition
72    public boolean validateStateTransition() {
73        Log.v(LOG_TAG, "print state depository: " + printStates());
74        if (mTransitionDirection == DO_NOTHING) {
75            if (mStateDepository.isEmpty()) {
76                Log.v(LOG_TAG, "no state is recorded");
77                mReason = "no state is recorded.";
78                return false;
79            } else if (mStateDepository.size() > 1) {
80                for (int i = 0; i < mStateDepository.size(); i++) {
81                    if (mStateDepository.get(i) != mTransitionTarget) {
82                        Log.v(LOG_TAG, "state changed.");
83                        mReason = "Unexpected state change";
84                        return false;
85                    }
86                }
87            } else if (mStateDepository.get(0) != mTransitionTarget) {
88                Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
89                        mStateDepository.get(0));
90                mReason = mTransitionTarget + " is expected, but it is " + mStateDepository.get(0);
91                return false;
92            }
93            return true;
94        } else if (mTransitionDirection == TO_CONNECTION) {
95            Log.v(LOG_TAG, "transition to CONNECTED");
96            return transitToConnection();
97        } else {
98            Log.v(LOG_TAG, "transition to DISCONNECTED");
99            return transitToDisconnection();
100        }
101    }
102
103    /*
104     * Transition from CONNECTED -> DISCONNECTED:
105     *    CONNECTED->DISCONNECTING->DISCONNECTED
106     * return false if any state transition is not valid and save a message in mReason
107     */
108    public boolean transitToDisconnection () {
109        mReason = "states: " + printStates();
110        if (mStateDepository.get(0) != State.CONNECTED) {
111            mReason += " initial state should be CONNECTED, but it is " +
112                    mStateDepository.get(0) + ".";
113            return false;
114        }
115        State lastState = mStateDepository.get(mStateDepository.size() - 1);
116        if ( lastState != mTransitionTarget) {
117            mReason += " the last state should be DISCONNECTED, but it is " + lastState;
118            return false;
119        }
120        for (int i = 1; i < mStateDepository.size() - 1; i++) {
121            State preState = mStateDepository.get(i-1);
122            State curState = mStateDepository.get(i);
123            if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
124                    (curState == State.DISCONNECTED))) {
125                continue;
126            } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
127                continue;
128            } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
129                continue;
130            } else {
131                mReason += " Transition state from " + preState.toString() + " to " +
132                        curState.toString() + " is not valid.";
133                return false;
134            }
135        }
136        return true;
137    }
138
139    // DISCONNECTED->CONNECTING->CONNECTED
140    public boolean transitToConnection() {
141        mReason = "states: " + printStates();
142        if (mStateDepository.get(0) != State.DISCONNECTED) {
143            mReason += " initial state should be DISCONNECTED, but it is " +
144                    mStateDepository.get(0) + ".";
145            return false;
146        }
147        State lastState = mStateDepository.get(mStateDepository.size() - 1);
148        if ( lastState != mTransitionTarget) {
149            mReason += "The last state should be " + mTransitionTarget + ", but it is " + lastState;
150            return false;
151        }
152        for (int i = 1; i < mStateDepository.size(); i++) {
153            State preState = mStateDepository.get(i-1);
154            State curState = mStateDepository.get(i);
155            if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
156                    (curState == State.CONNECTED) || (curState == State.DISCONNECTED))) {
157                continue;
158            } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
159                continue;
160            } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
161                continue;
162            } else {
163                mReason += " Transition state from " + preState.toString() + " to " +
164                        curState.toString() + " is not valid.";
165                return false;
166            }
167        }
168        return true;
169    }
170
171    public List<State> getTransitionStates() {
172        return mStateDepository;
173    }
174
175    // return state failure mReason
176    public String getReason() {
177        return mReason;
178    }
179
180    public String printStates() {
181        StringBuilder stateBuilder = new StringBuilder("");
182        for (int i = 0; i < mStateDepository.size(); i++) {
183            stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
184        }
185        return stateBuilder.toString();
186    }
187
188    @Override
189    public String toString() {
190        StringBuilder builder = new StringBuilder(" ");
191        builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
192                append("; ").append("states:").
193                append(printStates()).append("; ");
194        return builder.toString();
195    }
196}
197