2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony.dataconnection;
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.telephony.Rlog;
27 import com.android.internal.telephony.DctConstants;
28 import com.android.internal.telephony.PhoneBase;
29 import com.android.internal.telephony.PhoneConstants;
30 import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
31 import com.android.internal.util.State;
32 import com.android.internal.util.StateMachine;
34 import java.io.FileDescriptor;
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.HashMap;
40 * Data Connection Controller which is a package visible class and controls
41 * multiple data connections. For instance listening for unsolicited messages
42 * and then demultiplexing them to the appropriate DC.
44 class DcController extends StateMachine {
45 private static final boolean DBG = true;
46 private static final boolean VDBG = false;
48 private PhoneBase mPhone;
49 private DcTrackerBase mDct;
50 private DcTesterDeactivateAll mDcTesterDeactivateAll;
52 // package as its used by Testing code
53 ArrayList<DataConnection> mDcListAll = new ArrayList<DataConnection>();
54 private HashMap<Integer, DataConnection> mDcListActiveByCid =
55 new HashMap<Integer, DataConnection>();
58 * Constants for the data connection activity:
59 * physical link down/up
61 * TODO: Move to RILConstants.java
63 static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0;
64 static final int DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT = 1;
65 static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2;
67 private DccDefaultState mDccDefaultState = new DccDefaultState();
72 * @param name to be used for the Controller
73 * @param phone the phone associated with Dcc and Dct
74 * @param dct the DataConnectionTracker associated with Dcc
75 * @param handler defines the thread/looper to be used with Dcc
77 private DcController(String name, PhoneBase phone, DcTrackerBase dct,
84 addState(mDccDefaultState);
85 setInitialState(mDccDefaultState);
89 static DcController makeDcc(PhoneBase phone, DcTrackerBase dct, Handler handler) {
90 DcController dcc = new DcController("Dcc", phone, dct, handler);
96 log("dispose: call quiteNow()");
100 void addDc(DataConnection dc) {
104 void removeDc(DataConnection dc) {
105 mDcListActiveByCid.remove(dc.mCid);
106 mDcListAll.remove(dc);
109 void addActiveDcByCid(DataConnection dc) {
110 if (DBG && dc.mCid < 0) {
111 log("addActiveDcByCid dc.mCid < 0 dc=" + dc);
113 mDcListActiveByCid.put(dc.mCid, dc);
116 void removeActiveDcByCid(DataConnection dc) {
117 DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
118 if (DBG && removedDc == null) {
119 log("removeActiveDcByCid removedDc=null dc=" + dc);
123 private class DccDefaultState extends State {
125 public void enter() {
126 mPhone.mCi.registerForRilConnected(getHandler(),
127 DataConnection.EVENT_RIL_CONNECTED, null);
128 mPhone.mCi.registerForDataNetworkStateChanged(getHandler(),
129 DataConnection.EVENT_DATA_STATE_CHANGED, null);
130 if (Build.IS_DEBUGGABLE) {
131 mDcTesterDeactivateAll =
132 new DcTesterDeactivateAll(mPhone, DcController.this, getHandler());
138 if (mPhone != null) {
139 mPhone.mCi.unregisterForRilConnected(getHandler());
140 mPhone.mCi.unregisterForDataNetworkStateChanged(getHandler());
142 if (mDcTesterDeactivateAll != null) {
143 mDcTesterDeactivateAll.dispose();
148 public boolean processMessage(Message msg) {
152 case DataConnection.EVENT_RIL_CONNECTED:
153 ar = (AsyncResult)msg.obj;
154 if (ar.exception == null) {
156 log("DccDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
160 log("DccDefaultState: Unexpected exception on EVENT_RIL_CONNECTED");
164 case DataConnection.EVENT_DATA_STATE_CHANGED:
165 ar = (AsyncResult)msg.obj;
166 if (ar.exception == null) {
167 onDataStateChanged((ArrayList<DataCallResponse>)ar.result);
169 log("DccDefaultState: EVENT_DATA_STATE_CHANGED:" +
170 " exception; likely radio not available, ignore");
178 * Process the new list of "known" Data Calls
179 * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
181 private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
183 lr("onDataStateChanged: dcsList=" + dcsList
184 + " mDcListActiveByCid=" + mDcListActiveByCid);
187 log("onDataStateChanged: mDcListAll=" + mDcListAll);
190 // Create hashmap of cid to DataCallResponse
191 HashMap<Integer, DataCallResponse> dataCallResponseListByCid =
192 new HashMap<Integer, DataCallResponse>();
193 for (DataCallResponse dcs : dcsList) {
194 dataCallResponseListByCid.put(dcs.cid, dcs);
197 // Add a DC that is active but not in the
198 // dcsList to the list of DC's to retry
199 ArrayList<DataConnection> dcsToRetry = new ArrayList<DataConnection>();
200 for (DataConnection dc : mDcListActiveByCid.values()) {
201 if (dataCallResponseListByCid.get(dc.mCid) == null) {
202 if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
206 if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
208 // Find which connections have changed state and send a notification or cleanup
209 // and any that are in active need to be retried.
210 ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();
212 boolean isAnyDataCallDormant = false;
213 boolean isAnyDataCallActive = false;
215 for (DataCallResponse newState : dcsList) {
217 DataConnection dc = mDcListActiveByCid.get(newState.cid);
219 // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
220 loge("onDataStateChanged: no associated DC yet, ignore");
224 if (dc.mApnContexts.size() == 0) {
225 if (DBG) loge("onDataStateChanged: no connected apns, ignore");
227 // Determine if the connection/apnContext should be cleaned up
228 // or just a notification should be sent out.
229 if (DBG) log("onDataStateChanged: Found ConnId=" + newState.cid
230 + " newState=" + newState.toString());
231 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
232 DcFailCause failCause = DcFailCause.fromInt(newState.status);
233 if (DBG) log("onDataStateChanged: inactive failCause=" + failCause);
234 if (failCause.isRestartRadioFail()) {
235 if (DBG) log("onDataStateChanged: X restart radio");
236 mDct.sendRestartRadio();
237 } else if (failCause.isPermanentFail()) {
238 if (DBG) log("onDataStateChanged: inactive, add to cleanup list");
239 apnsToCleanup.addAll(dc.mApnContexts);
241 if (DBG) log("onDataStateChanged: inactive, add to retry list");
245 // Its active so update the DataConnections link properties
246 UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
247 if (result.oldLp.equals(result.newLp)) {
248 if (DBG) log("onDataStateChanged: no change");
250 if (result.oldLp.isIdenticalInterfaceName(result.newLp)) {
251 if (! result.oldLp.isIdenticalDnses(result.newLp) ||
252 ! result.oldLp.isIdenticalRoutes(result.newLp) ||
253 ! result.oldLp.isIdenticalHttpProxy(result.newLp) ||
254 ! result.oldLp.isIdenticalAddresses(result.newLp)) {
255 // If the same address type was removed and
256 // added we need to cleanup
257 CompareResult<LinkAddress> car =
258 result.oldLp.compareAddresses(result.newLp);
260 log("onDataStateChanged: oldLp=" + result.oldLp +
261 " newLp=" + result.newLp + " car=" + car);
263 boolean needToClean = false;
264 for (LinkAddress added : car.added) {
265 for (LinkAddress removed : car.removed) {
266 if (NetworkUtils.addressTypeMatches(
267 removed.getAddress(),
268 added.getAddress())) {
276 log("onDataStateChanged: addr change," +
277 " cleanup apns=" + dc.mApnContexts +
278 " oldLp=" + result.oldLp +
279 " newLp=" + result.newLp);
281 apnsToCleanup.addAll(dc.mApnContexts);
283 if (DBG) log("onDataStateChanged: simple change");
284 for (ApnContext apnContext : dc.mApnContexts) {
285 mPhone.notifyDataConnection(
286 PhoneConstants.REASON_LINK_PROPERTIES_CHANGED,
287 apnContext.getApnType());
292 log("onDataStateChanged: no changes");
296 apnsToCleanup.addAll(dc.mApnContexts);
298 log("onDataStateChanged: interface change, cleanup apns="
306 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) {
307 isAnyDataCallActive = true;
309 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) {
310 isAnyDataCallDormant = true;
314 if (isAnyDataCallDormant && !isAnyDataCallActive) {
315 // There is no way to indicate link activity per APN right now. So
316 // Link Activity will be considered dormant only when all data calls
318 // If a single data call is in dormant state and none of the data
319 // calls are active broadcast overall link state as dormant.
321 log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
323 mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
326 log("onDataStateChanged: Data Activity updated to NONE. " +
327 "isAnyDataCallActive = " + isAnyDataCallActive +
328 " isAnyDataCallDormant = " + isAnyDataCallDormant);
330 if (isAnyDataCallActive) {
331 mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
336 lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
337 + " apnsToCleanup=" + apnsToCleanup);
340 // Cleanup connections that have changed
341 for (ApnContext apnContext : apnsToCleanup) {
342 mDct.sendCleanUpConnection(true, apnContext);
345 // Retry connections that have disappeared
346 for (DataConnection dc : dcsToRetry) {
347 if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
348 dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
351 if (DBG) log("onDataStateChanged: X");
356 * lr is short name for logAndAddLogRec
359 private void lr(String s) {
364 protected void log(String s) {
365 Rlog.d(getName(), s);
369 protected void loge(String s) {
370 Rlog.e(getName(), s);
374 * @return the string for msg.what as our info.
377 protected String getWhatToString(int what) {
379 info = DataConnection.cmdToString(what);
381 info = DcAsyncChannel.cmdToString(what);
387 public String toString() {
388 return "mDcListAll=" + mDcListAll + " mDcListActiveByCid=" + mDcListActiveByCid;
392 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
393 super.dump(fd, pw, args);
394 pw.println(" mPhone=" + mPhone);
395 pw.println(" mDcListAll=" + mDcListAll);
396 pw.println(" mDcListActiveByCid=" + mDcListActiveByCid);