| /* |
| * 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); |
| } |
| } |