blob: ed87a99683fa2ab2288aca2a913e21af062adc01 [file] [log] [blame]
/*
* Copyright (C) 2014 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.internal.telephony.dataconnection;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
import android.os.Message;
import android.telephony.Rlog;
public class DcSwitchStateMachine extends StateMachine {
private static final boolean DBG = true;
private static final boolean VDBG = false;
private static final String LOG_TAG = "DcSwitchSM";
// ***** Event codes for driving the state machine
private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00001000;
private static final int EVENT_CONNECTED = BASE + 0;
private int mId;
private Phone mPhone;
private AsyncChannel mAc;
private IdleState mIdleState = new IdleState();
private AttachingState mAttachingState = new AttachingState();
private AttachedState mAttachedState = new AttachedState();
private DetachingState mDetachingState = new DetachingState();
private DefaultState mDefaultState = new DefaultState();
protected DcSwitchStateMachine(Phone phone, String name, int id) {
super(name);
if (DBG) log("DcSwitchState constructor E");
mPhone = phone;
mId = id;
addState(mDefaultState);
addState(mIdleState, mDefaultState);
addState(mAttachingState, mDefaultState);
addState(mAttachedState, mDefaultState);
addState(mDetachingState, mDefaultState);
setInitialState(mIdleState);
if (DBG) log("DcSwitchState constructor X");
}
// public void notifyDataConnection(int phoneId, String state, String reason,
// String apnName, String apnType, boolean unavailable) {
// if (phoneId == mId &&
// TextUtils.equals(state, PhoneConstants.DataState.CONNECTED.toString())) {
// sendMessage(obtainMessage(EVENT_CONNECTED));
// }
// }
private class IdleState extends State {
@Override
public void enter() {
if (DBG) log("IdleState: enter");
try {
DctController.getInstance().processRequests();
} catch (RuntimeException e) {
if (DBG) loge("DctController is not ready");
}
}
@Override
public boolean processMessage(Message msg) {
boolean retVal;
switch (msg.what) {
case DcSwitchAsyncChannel.REQ_CONNECT: {
if (DBG) {
log("IdleState: REQ_CONNECT");
}
PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
pb.mCi.setDataAllowed(true, null);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
PhoneConstants.APN_REQUEST_STARTED);
transitionTo(mAttachingState);
retVal = HANDLED;
break;
}
case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
if (DBG) {
log("AttachingState: EVENT_DATA_ATTACHED");
}
transitionTo(mAttachedState);
retVal = HANDLED;
break;
case EVENT_CONNECTED: {
if (DBG) {
log("IdleState: Receive invalid event EVENT_CONNECTED!");
}
retVal = HANDLED;
break;
}
default:
if (VDBG) {
log("IdleState: nothandled msg.what=0x" +
Integer.toHexString(msg.what));
}
retVal = NOT_HANDLED;
break;
}
return retVal;
}
}
private class AttachingState extends State {
@Override
public void enter() {
log("AttachingState: enter");
}
@Override
public boolean processMessage(Message msg) {
boolean retVal;
switch (msg.what) {
case DcSwitchAsyncChannel.REQ_CONNECT: {
if (DBG) {
log("AttachingState: REQ_CONNECT");
}
PhoneBase pb = (PhoneBase) ((PhoneProxy) mPhone).getActivePhone();
pb.mCi.setDataAllowed(true, null);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
PhoneConstants.APN_REQUEST_STARTED);
retVal = HANDLED;
break;
}
case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
if (DBG) {
log("AttachingState: EVENT_DATA_ATTACHED");
}
transitionTo(mAttachedState);
retVal = HANDLED;
break;
case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
if (DBG) {
log("AttachingState: REQ_DISCONNECT_ALL" );
}
DctController.getInstance().releaseAllRequests(mId);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
PhoneConstants.APN_REQUEST_STARTED);
transitionTo(mDetachingState);
retVal = HANDLED;
break;
}
default:
if (VDBG) {
log("AttachingState: nothandled msg.what=0x" +
Integer.toHexString(msg.what));
}
retVal = NOT_HANDLED;
break;
}
return retVal;
}
}
private class AttachedState extends State {
@Override
public void enter() {
if (DBG) log("AttachedState: enter");
//When enter attached state, we need exeute all requests.
DctController.getInstance().executeAllRequests(mId);
}
@Override
public boolean processMessage(Message msg) {
boolean retVal;
switch (msg.what) {
case DcSwitchAsyncChannel.REQ_CONNECT: {
RequestInfo apnRequest = (RequestInfo)msg.obj;
if (DBG) {
log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
}
DctController.getInstance().executeRequest(apnRequest);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
PhoneConstants.APN_REQUEST_STARTED);
retVal = HANDLED;
break;
}
case DcSwitchAsyncChannel.REQ_DISCONNECT: {
RequestInfo apnRequest = (RequestInfo)msg.obj;
if (DBG) {
log("AttachedState: REQ_DISCONNECT apnRequest=" + apnRequest);
}
DctController.getInstance().releaseRequest(apnRequest);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
PhoneConstants.APN_REQUEST_STARTED);
retVal = HANDLED;
break;
}
case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
if (DBG) {
log("AttachedState: REQ_DISCONNECT_ALL" );
}
DctController.getInstance().releaseAllRequests(mId);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
PhoneConstants.APN_REQUEST_STARTED);
transitionTo(mDetachingState);
retVal = HANDLED;
break;
}
case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
if (DBG) {
log("AttachedState: EVENT_DATA_DETACHED");
}
transitionTo(mAttachingState);
retVal = HANDLED;
break;
}
default:
if (VDBG) {
log("AttachedState: nothandled msg.what=0x" +
Integer.toHexString(msg.what));
}
retVal = NOT_HANDLED;
break;
}
return retVal;
}
}
private class DetachingState extends State {
@Override
public void enter() {
if (DBG) log("DetachingState: enter");
PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
pb.mCi.setDataAllowed(false, obtainMessage(
DcSwitchAsyncChannel.EVENT_DATA_DETACHED));
}
@Override
public boolean processMessage(Message msg) {
boolean retVal;
switch (msg.what) {
case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
if (DBG) {
log("DetachingState: EVENT_DATA_DETACHED");
}
transitionTo(mIdleState);
retVal = HANDLED;
break;
}
case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
if (DBG) {
log("DetachingState: REQ_DISCONNECT_ALL, already detaching" );
}
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
PhoneConstants.APN_REQUEST_STARTED);
retVal = HANDLED;
break;
}
default:
if (VDBG) {
log("DetachingState: nothandled msg.what=0x" +
Integer.toHexString(msg.what));
}
retVal = NOT_HANDLED;
break;
}
return retVal;
}
}
private class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
if (mAc != null) {
if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
} else {
mAc = new AsyncChannel();
mAc.connected(null, getHandler(), msg.replyTo);
if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
}
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
if (VDBG) log("CMD_CHANNEL_DISCONNECT");
mAc.disconnect();
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
mAc = null;
break;
}
case DcSwitchAsyncChannel.REQ_IS_IDLE_STATE: {
boolean val = getCurrentState() == mIdleState;
if (VDBG) log("REQ_IS_IDLE_STATE isIdle=" + val);
mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_IS_IDLE_STATE, val ? 1 : 0);
break;
}
case DcSwitchAsyncChannel.REQ_IS_IDLE_OR_DETACHING_STATE: {
boolean val = (getCurrentState() == mIdleState ||
getCurrentState() == mDetachingState);
if (VDBG) log("REQ_IS_IDLE_OR_DETACHING_STATE isIdleDetaching=" + val);
mAc.replyToMessage(msg,
DcSwitchAsyncChannel.RSP_IS_IDLE_OR_DETACHING_STATE, val ? 1 : 0);
break;
}
default:
if (DBG) {
log("DefaultState: shouldn't happen but ignore msg.what=0x" +
Integer.toHexString(msg.what));
}
break;
}
return HANDLED;
}
}
@Override
protected void log(String s) {
Rlog.d(LOG_TAG, "[" + getName() + "] " + s);
}
}