]> nv-tegra.nvidia Code Review - android/platform/frameworks/opt/telephony.git/blob - src/java/com/android/internal/telephony/dataconnection/DcController.java
Handle DATA_CALL_LIST in DCC and update dataActivity properly
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / dataconnection / DcController.java
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.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;
33
34 import java.io.FileDescriptor;
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38
39 /**
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.
43  */
44 class DcController extends StateMachine {
45     private static final boolean DBG = true;
46     private static final boolean VDBG = false;
47
48     private PhoneBase mPhone;
49     private DcTrackerBase mDct;
50     private DcTesterDeactivateAll mDcTesterDeactivateAll;
51
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>();
56
57     /**
58      * Constants for the data connection activity:
59      * physical link down/up
60      *
61      * TODO: Move to RILConstants.java
62      */
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;
66
67     private DccDefaultState mDccDefaultState = new DccDefaultState();
68
69     /**
70      * Constructor.
71      *
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
76      */
77     private DcController(String name, PhoneBase phone, DcTrackerBase dct,
78             Handler handler) {
79         super(name, handler);
80         setLogRecSize(300);
81         log("E ctor");
82         mPhone = phone;
83         mDct = dct;
84         addState(mDccDefaultState);
85         setInitialState(mDccDefaultState);
86         log("X ctor");
87     }
88
89     static DcController makeDcc(PhoneBase phone, DcTrackerBase dct, Handler handler) {
90         DcController dcc = new DcController("Dcc", phone, dct, handler);
91         dcc.start();
92         return dcc;
93     }
94
95     void dispose() {
96         log("dispose: call quiteNow()");
97         quitNow();
98     }
99
100     void addDc(DataConnection dc) {
101         mDcListAll.add(dc);
102     }
103
104     void removeDc(DataConnection dc) {
105         mDcListActiveByCid.remove(dc.mCid);
106         mDcListAll.remove(dc);
107     }
108
109     void addActiveDcByCid(DataConnection dc) {
110         if (DBG && dc.mCid < 0) {
111             log("addActiveDcByCid dc.mCid < 0 dc=" + dc);
112         }
113         mDcListActiveByCid.put(dc.mCid, dc);
114     }
115
116     void removeActiveDcByCid(DataConnection dc) {
117         DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
118         if (DBG && removedDc == null) {
119             log("removeActiveDcByCid removedDc=null dc=" + dc);
120         }
121     }
122
123     private class DccDefaultState extends State {
124         @Override
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());
133             }
134         }
135
136         @Override
137         public void exit() {
138             if (mPhone != null) {
139                 mPhone.mCi.unregisterForRilConnected(getHandler());
140                 mPhone.mCi.unregisterForDataNetworkStateChanged(getHandler());
141             }
142             if (mDcTesterDeactivateAll != null) {
143                 mDcTesterDeactivateAll.dispose();
144             }
145         }
146
147         @Override
148         public boolean processMessage(Message msg) {
149             AsyncResult ar;
150
151             switch (msg.what) {
152                 case DataConnection.EVENT_RIL_CONNECTED:
153                     ar = (AsyncResult)msg.obj;
154                     if (ar.exception == null) {
155                         if (DBG) {
156                             log("DccDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
157                                 ar.result);
158                         }
159                     } else {
160                         log("DccDefaultState: Unexpected exception on EVENT_RIL_CONNECTED");
161                     }
162                     break;
163
164                 case DataConnection.EVENT_DATA_STATE_CHANGED:
165                     ar = (AsyncResult)msg.obj;
166                     if (ar.exception == null) {
167                         onDataStateChanged((ArrayList<DataCallResponse>)ar.result);
168                     } else {
169                         log("DccDefaultState: EVENT_DATA_STATE_CHANGED:" +
170                                     " exception; likely radio not available, ignore");
171                     }
172                     break;
173             }
174             return HANDLED;
175         }
176
177         /**
178          * Process the new list of "known" Data Calls
179          * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
180          */
181         private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
182             if (DBG) {
183                 lr("onDataStateChanged: dcsList=" + dcsList
184                         + " mDcListActiveByCid=" + mDcListActiveByCid);
185             }
186             if (VDBG) {
187                 log("onDataStateChanged: mDcListAll=" + mDcListAll);
188             }
189
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);
195             }
196
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);
203                     dcsToRetry.add(dc);
204                 }
205             }
206             if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
207
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>();
211
212             boolean isAnyDataCallDormant = false;
213             boolean isAnyDataCallActive = false;
214
215             for (DataCallResponse newState : dcsList) {
216
217                 DataConnection dc = mDcListActiveByCid.get(newState.cid);
218                 if (dc == null) {
219                     // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
220                     loge("onDataStateChanged: no associated DC yet, ignore");
221                     continue;
222                 }
223
224                 if (dc.mApnContexts.size() == 0) {
225                     if (DBG) loge("onDataStateChanged: no connected apns, ignore");
226                 } else {
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);
240                         } else {
241                             if (DBG) log("onDataStateChanged: inactive, add to retry list");
242                             dcsToRetry.add(dc);
243                         }
244                     } else {
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");
249                         } else {
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);
259                                     if (DBG) {
260                                         log("onDataStateChanged: oldLp=" + result.oldLp +
261                                                 " newLp=" + result.newLp + " car=" + car);
262                                     }
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())) {
269                                                 needToClean = true;
270                                                 break;
271                                             }
272                                         }
273                                     }
274                                     if (needToClean) {
275                                         if (DBG) {
276                                             log("onDataStateChanged: addr change," +
277                                                     " cleanup apns=" + dc.mApnContexts +
278                                                     " oldLp=" + result.oldLp +
279                                                     " newLp=" + result.newLp);
280                                         }
281                                         apnsToCleanup.addAll(dc.mApnContexts);
282                                     } else {
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());
288                                         }
289                                     }
290                                 } else {
291                                     if (DBG) {
292                                         log("onDataStateChanged: no changes");
293                                     }
294                                 }
295                             } else {
296                                 apnsToCleanup.addAll(dc.mApnContexts);
297                                 if (DBG) {
298                                     log("onDataStateChanged: interface change, cleanup apns="
299                                             + dc.mApnContexts);
300                                 }
301                             }
302                         }
303                     }
304                 }
305
306                 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) {
307                     isAnyDataCallActive = true;
308                 }
309                 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) {
310                     isAnyDataCallDormant = true;
311                 }
312             }
313
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
317                 // are dormant.
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.
320                 if (DBG) {
321                     log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
322                 }
323                 mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
324             } else {
325                 if (DBG) {
326                     log("onDataStateChanged: Data Activity updated to NONE. " +
327                             "isAnyDataCallActive = " + isAnyDataCallActive +
328                             " isAnyDataCallDormant = " + isAnyDataCallDormant);
329                 }
330                 if (isAnyDataCallActive) {
331                     mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
332                 }
333             }
334
335             if (DBG) {
336                 lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
337                         + " apnsToCleanup=" + apnsToCleanup);
338             }
339
340             // Cleanup connections that have changed
341             for (ApnContext apnContext : apnsToCleanup) {
342                mDct.sendCleanUpConnection(true, apnContext);
343             }
344
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);
349             }
350
351             if (DBG) log("onDataStateChanged: X");
352         }
353     }
354
355     /**
356      * lr is short name for logAndAddLogRec
357      * @param s
358      */
359     private void lr(String s) {
360         logAndAddLogRec(s);
361     }
362
363     @Override
364     protected void log(String s) {
365         Rlog.d(getName(), s);
366     }
367
368     @Override
369     protected void loge(String s) {
370         Rlog.e(getName(), s);
371     }
372
373     /**
374      * @return the string for msg.what as our info.
375      */
376     @Override
377     protected String getWhatToString(int what) {
378         String info = null;
379         info = DataConnection.cmdToString(what);
380         if (info == null) {
381             info = DcAsyncChannel.cmdToString(what);
382         }
383         return info;
384     }
385
386     @Override
387     public String toString() {
388         return "mDcListAll=" + mDcListAll + " mDcListActiveByCid=" + mDcListActiveByCid;
389     }
390
391     @Override
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);
397     }
398 }