1 /*
2  * Copyright (C) 2021 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.telephony.qns;
18 
19 import android.annotation.IntDef;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.telephony.AccessNetworkConstants;
24 import android.telephony.PreciseDataConnectionState;
25 import android.telephony.TelephonyManager;
26 import android.telephony.data.ApnSetting;
27 import android.util.Log;
28 import android.util.SparseArray;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 class DataConnectionStatusTracker {
33     private static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 11001;
34     protected final int mSlotIndex;
35     private final String mLogTag;
36     private final int mNetCapability;
37     @VisibleForTesting protected final Handler mHandler;
38     private DataConnectionChangedInfo mLastUpdatedDcChangedInfo;
39     private final QnsTelephonyListener mQnsTelephonyListener;
40     static final int STATE_INACTIVE = 0;
41     static final int STATE_CONNECTING = 1;
42     static final int STATE_CONNECTED = 2;
43     static final int STATE_HANDOVER = 3;
44 
45     @IntDef(
46             value = {
47                 STATE_INACTIVE,
48                 STATE_CONNECTING,
49                 STATE_CONNECTED,
50                 STATE_HANDOVER,
51             })
52     @interface DataConnectionState {}
53 
54     static final int EVENT_DATA_CONNECTION_DISCONNECTED = 0;
55     static final int EVENT_DATA_CONNECTION_STARTED = 1;
56     static final int EVENT_DATA_CONNECTION_CONNECTED = 2;
57     static final int EVENT_DATA_CONNECTION_FAILED = 3;
58     static final int EVENT_DATA_CONNECTION_HANDOVER_STARTED = 4;
59     static final int EVENT_DATA_CONNECTION_HANDOVER_SUCCESS = 5;
60     static final int EVENT_DATA_CONNECTION_HANDOVER_FAILED = 6;
61 
62     @IntDef(
63             value = {
64                 EVENT_DATA_CONNECTION_DISCONNECTED,
65                 EVENT_DATA_CONNECTION_STARTED,
66                 EVENT_DATA_CONNECTION_CONNECTED,
67                 EVENT_DATA_CONNECTION_FAILED,
68                 EVENT_DATA_CONNECTION_HANDOVER_STARTED,
69                 EVENT_DATA_CONNECTION_HANDOVER_SUCCESS,
70                 EVENT_DATA_CONNECTION_HANDOVER_FAILED,
71             })
72     @interface DataConnectionChangedEvent {}
73 
74     private final QnsRegistrantList mDataConnectionStatusRegistrants;
75     private int mState = STATE_INACTIVE;
76     private int mDataConnectionFailCause;
77     private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
78     private SparseArray<ApnSetting> mLastApnSettings = new SparseArray<>();
79 
80     /**
81      * Constructor to instantiate CellularQualityMonitor
82      *
83      * @param qnsTelephonyListener QnsTelephonyListener instance
84      * @param looper looper to bind class' handler.
85      * @param slotIndex slot index
86      * @param netCapability integer value of network capability
87      */
DataConnectionStatusTracker( QnsTelephonyListener qnsTelephonyListener, Looper looper, int slotIndex, int netCapability)88     DataConnectionStatusTracker(
89             QnsTelephonyListener qnsTelephonyListener,
90             Looper looper,
91             int slotIndex,
92             int netCapability) {
93         mLogTag =
94                 QnsConstants.QNS_TAG
95                         + "_"
96                         + DataConnectionStatusTracker.class.getSimpleName()
97                         + "_"
98                         + slotIndex
99                         + "_"
100                         + QnsUtils.getNameOfNetCapability(netCapability);
101 
102         mSlotIndex = slotIndex;
103         mNetCapability = netCapability;
104 
105         mHandler = new DataConnectionStatusTrackerHandler(looper);
106         mDataConnectionStatusRegistrants = new QnsRegistrantList();
107         mQnsTelephonyListener = qnsTelephonyListener;
108         mQnsTelephonyListener.registerPreciseDataConnectionStateChanged(
109                 mNetCapability, mHandler, EVENT_DATA_CONNECTION_STATE_CHANGED, null, true);
110     }
111 
log(String s)112     protected void log(String s) {
113         Log.d(mLogTag, s);
114     }
115 
isInactiveState()116     boolean isInactiveState() {
117         return mState == STATE_INACTIVE;
118     }
119 
isActiveState()120     boolean isActiveState() {
121         return mState == STATE_CONNECTED || mState == STATE_HANDOVER;
122     }
123 
isHandoverState()124     boolean isHandoverState() {
125         return mState == STATE_HANDOVER;
126     }
127 
isConnectionInProgress()128     boolean isConnectionInProgress() {
129         return mState == STATE_CONNECTING || mState == STATE_HANDOVER;
130     }
131 
getLastTransportType()132     int getLastTransportType() {
133         return mTransportType;
134     }
135 
getLastFailCause()136     int getLastFailCause() {
137         return mDataConnectionFailCause;
138     }
139 
140     /** Returns Latest APN setting for the transport type */
getLastApnSetting(int transportType)141     ApnSetting getLastApnSetting(int transportType) {
142         try {
143             return mLastApnSettings.get(transportType);
144         } catch (Exception e) {
145             return null;
146         }
147     }
148 
registerDataConnectionStatusChanged(Handler h, int what)149     void registerDataConnectionStatusChanged(Handler h, int what) {
150         if (h != null) {
151             mDataConnectionStatusRegistrants.addUnique(h, what, null);
152         }
153         if (mLastUpdatedDcChangedInfo != null) {
154             QnsRegistrant r = new QnsRegistrant(h, what, null);
155             r.notifyResult(mLastUpdatedDcChangedInfo);
156         }
157     }
158 
unRegisterDataConnectionStatusChanged(Handler h)159     void unRegisterDataConnectionStatusChanged(Handler h) {
160         if (h != null) {
161             mDataConnectionStatusRegistrants.remove(h);
162         }
163     }
164 
onDataConnectionStateChanged(PreciseDataConnectionState status)165     private void onDataConnectionStateChanged(PreciseDataConnectionState status) {
166         int transportType = status.getTransportType();
167         int state = status.getState();
168         mDataConnectionFailCause = status.getLastCauseCode();
169         log(
170                 "onDataConnectionChanged transportType:"
171                         + QnsConstants.transportTypeToString(transportType)
172                         + " state:"
173                         + QnsConstants.dataStateToString(status.getState())
174                         + " cause:"
175                         + status.getLastCauseCode());
176 
177         switch (state) {
178             case TelephonyManager.DATA_DISCONNECTED:
179                 if (mState == STATE_CONNECTED || mState == STATE_HANDOVER) {
180                     mState = STATE_INACTIVE;
181                     mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
182                     log("Connection Disconnected");
183                     notifyDataConnectionStatusChangedEvent(EVENT_DATA_CONNECTION_DISCONNECTED);
184                 } else {
185                     if (mState == STATE_CONNECTING) {
186                         // Initial connect Failed.
187                         mState = STATE_INACTIVE;
188                         mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
189                         log("Initial connect failed");
190                         notifyDataConnectionFailed(transportType);
191                     }
192                 }
193                 break;
194 
195             case TelephonyManager.DATA_CONNECTING:
196                 if (mState == STATE_INACTIVE) {
197                     mState = STATE_CONNECTING;
198                     log(
199                             "Initial Connect inited transport: "
200                                     + QnsConstants.transportTypeToString(transportType));
201                     notifyDataConnectionStarted(transportType);
202                 }
203                 break;
204 
205             case TelephonyManager.DATA_CONNECTED:
206                 if (mState == STATE_CONNECTING || mState == STATE_INACTIVE) {
207                     mState = STATE_CONNECTED;
208                     mTransportType = transportType;
209                     log(
210                             "Data Connected Transport: "
211                                     + QnsConstants.transportTypeToString(mTransportType));
212                     notifyDataConnectionStatusChangedEvent(EVENT_DATA_CONNECTION_CONNECTED);
213                 } else if (mState == STATE_HANDOVER && mTransportType != transportType) {
214                     mState = STATE_CONNECTED;
215                     mTransportType = transportType;
216                     log(
217                             "Handover completed to: "
218                                     + QnsConstants.transportTypeToString(mTransportType));
219                     notifyDataConnectionStatusChangedEvent(EVENT_DATA_CONNECTION_HANDOVER_SUCCESS);
220                 } else if (mState == STATE_HANDOVER && mTransportType == transportType) {
221                     mState = STATE_CONNECTED;
222                     log(
223                             "Handover failed and return to: "
224                                     + QnsConstants.transportTypeToString(mTransportType));
225                     notifyDataConnectionStatusChangedEvent(EVENT_DATA_CONNECTION_HANDOVER_FAILED);
226                 }
227                 break;
228 
229             case TelephonyManager.DATA_SUSPENDED:
230                 if (mState == STATE_HANDOVER && mTransportType != transportType) {
231                     mState = STATE_CONNECTED;
232                     mTransportType = transportType;
233                     log(
234                             "QNS assumes Handover completed to: "
235                                     + QnsConstants.transportTypeToString(mTransportType));
236                     notifyDataConnectionStatusChangedEvent(EVENT_DATA_CONNECTION_HANDOVER_SUCCESS);
237                 }
238                 break;
239 
240             case TelephonyManager.DATA_HANDOVER_IN_PROGRESS:
241                 if (mState == STATE_CONNECTED && mTransportType == transportType) {
242                     mState = STATE_HANDOVER;
243                     log(
244                             "Handover initiated from "
245                                     + QnsConstants.transportTypeToString(transportType));
246                     notifyDataConnectionStatusChangedEvent(EVENT_DATA_CONNECTION_HANDOVER_STARTED);
247                 } else {
248                     log(
249                             "Ignore STATE_HANDOVER since request is not for Src TransportType: "
250                                     + QnsConstants.transportTypeToString(mTransportType));
251                 }
252                 break;
253 
254             default:
255                 break;
256         }
257         mLastApnSettings.put(mTransportType, status.getApnSetting());
258     }
259 
notifyDataConnectionStarted(int transportType)260     private void notifyDataConnectionStarted(int transportType) {
261         DataConnectionChangedInfo info =
262                 new DataConnectionChangedInfo(EVENT_DATA_CONNECTION_STARTED, mState, transportType);
263         mLastUpdatedDcChangedInfo = info;
264         mDataConnectionStatusRegistrants.notifyResult(info);
265     }
266 
notifyDataConnectionFailed(int transportType)267     private void notifyDataConnectionFailed(int transportType) {
268         DataConnectionChangedInfo info =
269                 new DataConnectionChangedInfo(EVENT_DATA_CONNECTION_FAILED, mState, transportType);
270         mLastUpdatedDcChangedInfo = info;
271         mDataConnectionStatusRegistrants.notifyResult(info);
272     }
273 
notifyDataConnectionStatusChangedEvent(int event)274     private void notifyDataConnectionStatusChangedEvent(int event) {
275         DataConnectionChangedInfo info =
276                 new DataConnectionChangedInfo(event, mState, mTransportType);
277         mLastUpdatedDcChangedInfo = info;
278         mDataConnectionStatusRegistrants.notifyResult(info);
279     }
280 
close()281     void close() {
282         mQnsTelephonyListener.unregisterPreciseDataConnectionStateChanged(mNetCapability, mHandler);
283         mDataConnectionStatusRegistrants.removeAll();
284     }
285 
stateToString(int state)286     static String stateToString(int state) {
287         switch (state) {
288             case STATE_INACTIVE:
289                 return "STATE_INCATIVE";
290             case STATE_CONNECTING:
291                 return "STATE_CONNCTING";
292             case STATE_CONNECTED:
293                 return "STATE_CONNECTED";
294             case STATE_HANDOVER:
295                 return "STATE_HANDOVER";
296         }
297         return "INVALID";
298     }
299 
eventToString(int event)300     static String eventToString(int event) {
301         switch (event) {
302             case EVENT_DATA_CONNECTION_DISCONNECTED:
303                 return "EVENT_DATA_CONNECTION_DISCONNECTED";
304             case EVENT_DATA_CONNECTION_STARTED:
305                 return "EVENT_DATA_CONNECTION_STARTED";
306             case EVENT_DATA_CONNECTION_CONNECTED:
307                 return "EVENT_DATA_CONNECTION_CONNECTED";
308             case EVENT_DATA_CONNECTION_FAILED:
309                 return "EVENT_DATA_CONNECTION_FAILED";
310             case EVENT_DATA_CONNECTION_HANDOVER_STARTED:
311                 return "EVENT_DATA_CONNECTION_HANDOVER_STARTED";
312             case EVENT_DATA_CONNECTION_HANDOVER_SUCCESS:
313                 return "EVENT_DATA_CONNECTION_HANDOVER_SUCCESS";
314             case EVENT_DATA_CONNECTION_HANDOVER_FAILED:
315                 return "EVENT_DATA_CONNECTION_HANDOVER_FAILED";
316         }
317         return "INVALID";
318     }
319 
320     static class DataConnectionChangedInfo {
321         private final int mEvent;
322         private final int mState;
323         private final int mCurrentTransportType;
324 
325         @Override
toString()326         public String toString() {
327             return "DataConnectionChangedInfo{"
328                     + "mEvent="
329                     + eventToString(mEvent)
330                     + ", mState="
331                     + stateToString(mState)
332                     + ", mCurrentTransportType="
333                     + QnsConstants.transportTypeToString(mCurrentTransportType)
334                     + '}';
335         }
336 
DataConnectionChangedInfo( @ataConnectionChangedEvent int event, @DataConnectionState int state, @AccessNetworkConstants.TransportType int transportType)337         DataConnectionChangedInfo(
338                 @DataConnectionChangedEvent int event,
339                 @DataConnectionState int state,
340                 @AccessNetworkConstants.TransportType int transportType) {
341             mEvent = event;
342             mState = state;
343             mCurrentTransportType = transportType;
344         }
345 
getState()346         int getState() {
347             return mState;
348         }
349 
getEvent()350         int getEvent() {
351             return mEvent;
352         }
353 
getTransportType()354         int getTransportType() {
355             return mCurrentTransportType;
356         }
357     }
358 
359     class DataConnectionStatusTrackerHandler extends Handler {
DataConnectionStatusTrackerHandler(Looper l)360         DataConnectionStatusTrackerHandler(Looper l) {
361             super(l);
362         }
363 
364         @Override
handleMessage(Message message)365         public void handleMessage(Message message) {
366             log("handleMessage msg=" + message.what);
367             QnsAsyncResult ar = (QnsAsyncResult) message.obj;
368             switch (message.what) {
369                 case EVENT_DATA_CONNECTION_STATE_CHANGED:
370                     onDataConnectionStateChanged((PreciseDataConnectionState) ar.mResult);
371                     break;
372                 default:
373                     log("never reach here msg=" + message.what);
374             }
375         }
376     }
377 }
378