Home | History | Annotate | Download | only in dataconnection
      1 /*
      2  * Copyright (C) 2013 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.internal.telephony.dataconnection;
     18 
     19 import android.net.LinkAddress;
     20 import android.net.NetworkUtils;
     21 import android.net.LinkProperties.CompareResult;
     22 import android.os.AsyncResult;
     23 import android.os.Build;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.os.SystemClock;
     27 import android.telephony.DataConnectionRealTimeInfo;
     28 import android.telephony.Rlog;
     29 
     30 import com.android.internal.telephony.DctConstants;
     31 import com.android.internal.telephony.PhoneBase;
     32 import com.android.internal.telephony.PhoneConstants;
     33 import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
     34 import com.android.internal.util.State;
     35 import com.android.internal.util.StateMachine;
     36 
     37 import java.io.FileDescriptor;
     38 import java.io.PrintWriter;
     39 import java.util.ArrayList;
     40 import java.util.HashMap;
     41 
     42 /**
     43  * Data Connection Controller which is a package visible class and controls
     44  * multiple data connections. For instance listening for unsolicited messages
     45  * and then demultiplexing them to the appropriate DC.
     46  */
     47 class DcController extends StateMachine {
     48     private static final boolean DBG = true;
     49     private static final boolean VDBG = false;
     50 
     51     private PhoneBase mPhone;
     52     private DcTrackerBase mDct;
     53     private DcTesterDeactivateAll mDcTesterDeactivateAll;
     54 
     55     // package as its used by Testing code
     56     ArrayList<DataConnection> mDcListAll = new ArrayList<DataConnection>();
     57     private HashMap<Integer, DataConnection> mDcListActiveByCid =
     58             new HashMap<Integer, DataConnection>();
     59 
     60     /**
     61      * Constants for the data connection activity:
     62      * physical link down/up
     63      *
     64      * TODO: Move to RILConstants.java
     65      */
     66     static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0;
     67     static final int DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT = 1;
     68     static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2;
     69     static final int DATA_CONNECTION_ACTIVE_UNKNOWN = Integer.MAX_VALUE;
     70 
     71     // One of the DATA_CONNECTION_ACTIVE_XXX values
     72     int mOverallDataConnectionActiveState = DATA_CONNECTION_ACTIVE_UNKNOWN;
     73 
     74     private DccDefaultState mDccDefaultState = new DccDefaultState();
     75 
     76     /**
     77      * Constructor.
     78      *
     79      * @param name to be used for the Controller
     80      * @param phone the phone associated with Dcc and Dct
     81      * @param dct the DataConnectionTracker associated with Dcc
     82      * @param handler defines the thread/looper to be used with Dcc
     83      */
     84     private DcController(String name, PhoneBase phone, DcTrackerBase dct,
     85             Handler handler) {
     86         super(name, handler);
     87         setLogRecSize(300);
     88         log("E ctor");
     89         mPhone = phone;
     90         mDct = dct;
     91         addState(mDccDefaultState);
     92         setInitialState(mDccDefaultState);
     93         log("X ctor");
     94     }
     95 
     96     static DcController makeDcc(PhoneBase phone, DcTrackerBase dct, Handler handler) {
     97         DcController dcc = new DcController("Dcc", phone, dct, handler);
     98         dcc.start();
     99         return dcc;
    100     }
    101 
    102     void dispose() {
    103         log("dispose: call quiteNow()");
    104         quitNow();
    105     }
    106 
    107     void addDc(DataConnection dc) {
    108         mDcListAll.add(dc);
    109     }
    110 
    111     void removeDc(DataConnection dc) {
    112         mDcListActiveByCid.remove(dc.mCid);
    113         mDcListAll.remove(dc);
    114     }
    115 
    116     void addActiveDcByCid(DataConnection dc) {
    117         if (DBG && dc.mCid < 0) {
    118             log("addActiveDcByCid dc.mCid < 0 dc=" + dc);
    119         }
    120         mDcListActiveByCid.put(dc.mCid, dc);
    121     }
    122 
    123     void removeActiveDcByCid(DataConnection dc) {
    124         DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
    125         if (DBG && removedDc == null) {
    126             log("removeActiveDcByCid removedDc=null dc=" + dc);
    127         }
    128     }
    129 
    130     private class DccDefaultState extends State {
    131         @Override
    132         public void enter() {
    133             mPhone.mCi.registerForRilConnected(getHandler(),
    134                     DataConnection.EVENT_RIL_CONNECTED, null);
    135             mPhone.mCi.registerForDataNetworkStateChanged(getHandler(),
    136                     DataConnection.EVENT_DATA_STATE_CHANGED, null);
    137             if (Build.IS_DEBUGGABLE) {
    138                 mDcTesterDeactivateAll =
    139                         new DcTesterDeactivateAll(mPhone, DcController.this, getHandler());
    140             }
    141         }
    142 
    143         @Override
    144         public void exit() {
    145             if (mPhone != null) {
    146                 mPhone.mCi.unregisterForRilConnected(getHandler());
    147                 mPhone.mCi.unregisterForDataNetworkStateChanged(getHandler());
    148             }
    149             if (mDcTesterDeactivateAll != null) {
    150                 mDcTesterDeactivateAll.dispose();
    151             }
    152         }
    153 
    154         @Override
    155         public boolean processMessage(Message msg) {
    156             AsyncResult ar;
    157 
    158             switch (msg.what) {
    159                 case DataConnection.EVENT_RIL_CONNECTED:
    160                     ar = (AsyncResult)msg.obj;
    161                     if (ar.exception == null) {
    162                         if (DBG) {
    163                             log("DccDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
    164                                 ar.result);
    165                         }
    166                     } else {
    167                         log("DccDefaultState: Unexpected exception on EVENT_RIL_CONNECTED");
    168                     }
    169                     break;
    170 
    171                 case DataConnection.EVENT_DATA_STATE_CHANGED:
    172                     ar = (AsyncResult)msg.obj;
    173                     if (ar.exception == null) {
    174                         onDataStateChanged((ArrayList<DataCallResponse>)ar.result);
    175                     } else {
    176                         log("DccDefaultState: EVENT_DATA_STATE_CHANGED:" +
    177                                     " exception; likely radio not available, ignore");
    178                     }
    179                     break;
    180             }
    181             return HANDLED;
    182         }
    183 
    184         /**
    185          * Process the new list of "known" Data Calls
    186          * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
    187          */
    188         private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
    189             if (DBG) {
    190                 lr("onDataStateChanged: dcsList=" + dcsList
    191                         + " mDcListActiveByCid=" + mDcListActiveByCid);
    192             }
    193             if (VDBG) {
    194                 log("onDataStateChanged: mDcListAll=" + mDcListAll);
    195             }
    196 
    197             // Create hashmap of cid to DataCallResponse
    198             HashMap<Integer, DataCallResponse> dataCallResponseListByCid =
    199                     new HashMap<Integer, DataCallResponse>();
    200             for (DataCallResponse dcs : dcsList) {
    201                 dataCallResponseListByCid.put(dcs.cid, dcs);
    202             }
    203 
    204             // Add a DC that is active but not in the
    205             // dcsList to the list of DC's to retry
    206             ArrayList<DataConnection> dcsToRetry = new ArrayList<DataConnection>();
    207             for (DataConnection dc : mDcListActiveByCid.values()) {
    208                 if (dataCallResponseListByCid.get(dc.mCid) == null) {
    209                     if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
    210                     dcsToRetry.add(dc);
    211                 }
    212             }
    213             if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
    214 
    215             // Find which connections have changed state and send a notification or cleanup
    216             // and any that are in active need to be retried.
    217             ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();
    218 
    219             boolean isAnyDataCallDormant = false;
    220             boolean isAnyDataCallActive = false;
    221 
    222             for (DataCallResponse newState : dcsList) {
    223 
    224                 DataConnection dc = mDcListActiveByCid.get(newState.cid);
    225                 if (dc == null) {
    226                     // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
    227                     loge("onDataStateChanged: no associated DC yet, ignore");
    228                     continue;
    229                 }
    230 
    231                 if (dc.mApnContexts.size() == 0) {
    232                     if (DBG) loge("onDataStateChanged: no connected apns, ignore");
    233                 } else {
    234                     // Determine if the connection/apnContext should be cleaned up
    235                     // or just a notification should be sent out.
    236                     if (DBG) log("onDataStateChanged: Found ConnId=" + newState.cid
    237                             + " newState=" + newState.toString());
    238                     if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
    239                         if (mDct.mIsCleanupRequired) {
    240                             apnsToCleanup.addAll(dc.mApnContexts);
    241                             mDct.mIsCleanupRequired = false;
    242                         } else {
    243                             DcFailCause failCause = DcFailCause.fromInt(newState.status);
    244                             if (DBG) log("onDataStateChanged: inactive failCause=" + failCause);
    245                             if (failCause.isRestartRadioFail()) {
    246                                 if (DBG) log("onDataStateChanged: X restart radio");
    247                                 mDct.sendRestartRadio();
    248                             } else if (mDct.isPermanentFail(failCause)) {
    249                                 if (DBG) log("onDataStateChanged: inactive, add to cleanup list");
    250                                 apnsToCleanup.addAll(dc.mApnContexts);
    251                             } else {
    252                                 if (DBG) log("onDataStateChanged: inactive, add to retry list");
    253                                 dcsToRetry.add(dc);
    254                             }
    255                         }
    256                     } else {
    257                         // Its active so update the DataConnections link properties
    258                         UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
    259                         if (result.oldLp.equals(result.newLp)) {
    260                             if (DBG) log("onDataStateChanged: no change");
    261                         } else {
    262                             if (result.oldLp.isIdenticalInterfaceName(result.newLp)) {
    263                                 if (! result.oldLp.isIdenticalDnses(result.newLp) ||
    264                                         ! result.oldLp.isIdenticalRoutes(result.newLp) ||
    265                                         ! result.oldLp.isIdenticalHttpProxy(result.newLp) ||
    266                                         ! result.oldLp.isIdenticalAddresses(result.newLp)) {
    267                                     // If the same address type was removed and
    268                                     // added we need to cleanup
    269                                     CompareResult<LinkAddress> car =
    270                                         result.oldLp.compareAddresses(result.newLp);
    271                                     if (DBG) {
    272                                         log("onDataStateChanged: oldLp=" + result.oldLp +
    273                                                 " newLp=" + result.newLp + " car=" + car);
    274                                     }
    275                                     boolean needToClean = false;
    276                                     for (LinkAddress added : car.added) {
    277                                         for (LinkAddress removed : car.removed) {
    278                                             if (NetworkUtils.addressTypeMatches(
    279                                                     removed.getAddress(),
    280                                                     added.getAddress())) {
    281                                                 needToClean = true;
    282                                                 break;
    283                                             }
    284                                         }
    285                                     }
    286                                     if (needToClean) {
    287                                         if (DBG) {
    288                                             log("onDataStateChanged: addr change," +
    289                                                     " cleanup apns=" + dc.mApnContexts +
    290                                                     " oldLp=" + result.oldLp +
    291                                                     " newLp=" + result.newLp);
    292                                         }
    293                                         apnsToCleanup.addAll(dc.mApnContexts);
    294                                     } else {
    295                                         if (DBG) log("onDataStateChanged: simple change");
    296 
    297                                         for (ApnContext apnContext : dc.mApnContexts) {
    298                                              mPhone.notifyDataConnection(
    299                                                  PhoneConstants.REASON_LINK_PROPERTIES_CHANGED,
    300                                                  apnContext.getApnType());
    301                                         }
    302                                     }
    303                                 } else {
    304                                     if (DBG) {
    305                                         log("onDataStateChanged: no changes");
    306                                     }
    307                                 }
    308                             } else {
    309                                 apnsToCleanup.addAll(dc.mApnContexts);
    310                                 if (DBG) {
    311                                     log("onDataStateChanged: interface change, cleanup apns="
    312                                             + dc.mApnContexts);
    313                                 }
    314                             }
    315                         }
    316                     }
    317                 }
    318 
    319                 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) {
    320                     isAnyDataCallActive = true;
    321                 }
    322                 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) {
    323                     isAnyDataCallDormant = true;
    324                 }
    325             }
    326 
    327             int newOverallDataConnectionActiveState = mOverallDataConnectionActiveState;
    328 
    329             if (isAnyDataCallDormant && !isAnyDataCallActive) {
    330                 // There is no way to indicate link activity per APN right now. So
    331                 // Link Activity will be considered dormant only when all data calls
    332                 // are dormant.
    333                 // If a single data call is in dormant state and none of the data
    334                 // calls are active broadcast overall link state as dormant.
    335                 if (DBG) {
    336                     log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
    337                 }
    338                 mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
    339                 newOverallDataConnectionActiveState = DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT;
    340             } else {
    341                 if (DBG) {
    342                     log("onDataStateChanged: Data Activity updated to NONE. " +
    343                             "isAnyDataCallActive = " + isAnyDataCallActive +
    344                             " isAnyDataCallDormant = " + isAnyDataCallDormant);
    345                 }
    346                 if (isAnyDataCallActive) {
    347                     newOverallDataConnectionActiveState = DATA_CONNECTION_ACTIVE_PH_LINK_UP;
    348                     mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
    349                 } else {
    350                     newOverallDataConnectionActiveState = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE;
    351                 }
    352             }
    353 
    354             // Temporary notification until RIL implementation is complete.
    355             if (mOverallDataConnectionActiveState != newOverallDataConnectionActiveState) {
    356                 mOverallDataConnectionActiveState = newOverallDataConnectionActiveState;
    357                 long time = SystemClock.elapsedRealtimeNanos();
    358                 int dcPowerState;
    359                 switch (mOverallDataConnectionActiveState) {
    360                     case DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE:
    361                     case DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT:
    362                         dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
    363                         break;
    364                     case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
    365                         dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
    366                         break;
    367                     default:
    368                         dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_UNKNOWN;
    369                         break;
    370                 }
    371                 DataConnectionRealTimeInfo dcRtInfo =
    372                         new DataConnectionRealTimeInfo(time , dcPowerState);
    373                 log("onDataStateChanged: notify DcRtInfo changed dcRtInfo=" + dcRtInfo);
    374                 mPhone.notifyDataConnectionRealTimeInfo(dcRtInfo);
    375             }
    376 
    377             if (DBG) {
    378                 lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
    379                         + " apnsToCleanup=" + apnsToCleanup);
    380             }
    381 
    382             // Cleanup connections that have changed
    383             for (ApnContext apnContext : apnsToCleanup) {
    384                mDct.sendCleanUpConnection(true, apnContext);
    385             }
    386 
    387             // Retry connections that have disappeared
    388             for (DataConnection dc : dcsToRetry) {
    389                 if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
    390                 dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
    391             }
    392 
    393             if (DBG) log("onDataStateChanged: X");
    394         }
    395     }
    396 
    397     /**
    398      * lr is short name for logAndAddLogRec
    399      * @param s
    400      */
    401     private void lr(String s) {
    402         logAndAddLogRec(s);
    403     }
    404 
    405     @Override
    406     protected void log(String s) {
    407         Rlog.d(getName(), s);
    408     }
    409 
    410     @Override
    411     protected void loge(String s) {
    412         Rlog.e(getName(), s);
    413     }
    414 
    415     /**
    416      * @return the string for msg.what as our info.
    417      */
    418     @Override
    419     protected String getWhatToString(int what) {
    420         String info = null;
    421         info = DataConnection.cmdToString(what);
    422         if (info == null) {
    423             info = DcAsyncChannel.cmdToString(what);
    424         }
    425         return info;
    426     }
    427 
    428     @Override
    429     public String toString() {
    430         return "mDcListAll=" + mDcListAll + " mDcListActiveByCid=" + mDcListActiveByCid;
    431     }
    432 
    433     @Override
    434     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    435         super.dump(fd, pw, args);
    436         pw.println(" mPhone=" + mPhone);
    437         pw.println(" mDcListAll=" + mDcListAll);
    438         pw.println(" mDcListActiveByCid=" + mDcListActiveByCid);
    439     }
    440 }
    441