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     * Verifies state transition from CONNECTED->...-> DISCONNECTED.
105     *
106     * returns false if initial state or target state is not correct, or if there is
107     * any transition from DISCONNECTING/DISCONNECTED -> CONNECTED.
108     */
109    public boolean transitToDisconnection () {
110        mReason = "states: " + printStates();
111        if (mStateDepository.get(0) != State.CONNECTED) {
112            mReason += " initial state should be CONNECTED, but it is " +
113                    mStateDepository.get(0) + ".";
114            return false;
115        }
116        State lastState = mStateDepository.get(mStateDepository.size() - 1);
117        if ( lastState != mTransitionTarget) {
118            mReason += " the last state should be DISCONNECTED, but it is " + lastState;
119            return false;
120        }
121        for (int i = 1; i < mStateDepository.size() - 1; i++) {
122            State preState = mStateDepository.get(i-1);
123            State curState = mStateDepository.get(i);
124            if (preState == curState) {
125                continue;
126            } else if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
127                    (curState == State.DISCONNECTED))) {
128                continue;
129            } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
130                continue;
131            } else {
132                mReason += " Transition state from " + preState.toString() + " to " +
133                        curState.toString() + " is not valid.";
134                return false;
135            }
136        }
137        return true;
138    }
139
140    /*
141     * Verifies state transition from DISCONNECTED->...-> CONNECTED.
142     *
143     * returns false if initial state or target state is not correct, or if there is
144     * any transition from CONNECED -> DISCONNECTED.
145     */
146    public boolean transitToConnection() {
147        mReason = "states: " + printStates();
148        if (mStateDepository.get(0) != State.DISCONNECTED) {
149            mReason += " initial state should be DISCONNECTED, but it is " +
150                    mStateDepository.get(0) + ".";
151            return false;
152        }
153        State lastState = mStateDepository.get(mStateDepository.size() - 1);
154        if ( lastState != mTransitionTarget) {
155            mReason += "The last state should be " + mTransitionTarget + ", but it is " + lastState;
156            return false;
157        }
158        for (int i = 1; i < mStateDepository.size(); i++) {
159            State preState = mStateDepository.get(i-1);
160            State curState = mStateDepository.get(i);
161            if (preState == curState) {
162                continue;
163            }
164            if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
165                    (curState == State.CONNECTED))) {
166                continue;
167             } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
168                 continue;
169             } else {
170                mReason += " Transition state from " + preState.toString() + " to " +
171                        curState.toString() + " is not valid.";
172                return false;
173            }
174        }
175        return true;
176    }
177
178    public List<State> getTransitionStates() {
179        return mStateDepository;
180    }
181
182    // return state failure mReason
183    public String getReason() {
184        return mReason;
185    }
186
187    public String printStates() {
188        StringBuilder stateBuilder = new StringBuilder("");
189        for (int i = 0; i < mStateDepository.size(); i++) {
190            stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
191        }
192        return stateBuilder.toString();
193    }
194
195    @Override
196    public String toString() {
197        StringBuilder builder = new StringBuilder(" ");
198        builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
199                append("; ").append("states:").
200                append(printStates()).append("; ");
201        return builder.toString();
202    }
203}
204