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 package com.android.telephony.qns;
17 
18 import static android.telephony.CellInfo.UNAVAILABLE;
19 
20 import android.annotation.NonNull;
21 import android.content.Context;
22 import android.net.NetworkCapabilities;
23 import android.os.Binder;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.telephony.AccessNetworkConstants.AccessNetworkType;
29 import android.telephony.CellSignalStrength;
30 import android.telephony.CellSignalStrengthGsm;
31 import android.telephony.CellSignalStrengthLte;
32 import android.telephony.CellSignalStrengthNr;
33 import android.telephony.CellSignalStrengthWcdma;
34 import android.telephony.SignalStrength;
35 import android.telephony.SignalStrengthUpdateRequest;
36 import android.telephony.SignalThresholdInfo;
37 import android.telephony.TelephonyCallback;
38 import android.telephony.TelephonyManager;
39 import android.util.Log;
40 import android.util.SparseArray;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 
44 import java.io.PrintWriter;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Set;
52 import java.util.concurrent.ConcurrentHashMap;
53 import java.util.concurrent.Executor;
54 
55 /**
56  * This class manages cellular threshold information registered from AccessNetworkEvaluator. It
57  * extends QualityMonitor class to implement and notify the signal changes in Cellular RAT.
58  */
59 class CellularQualityMonitor extends QualityMonitor {
60 
61     private static final int MAX_THRESHOLD_COUNT =
62             SignalThresholdInfo.MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
63     private final String mTag;
64     private TelephonyManager mTelephonyManager;
65     private QnsCarrierConfigManager mConfigManager;
66     private int mSubId;
67     private final int mSlotIndex;
68     private boolean mIsQnsListenerRegistered;
69     private final List<SignalThresholdInfo> mSignalThresholdInfoList;
70     private final HandlerThread mHandlerThread;
71 
72     /**
73      * thresholdMatrix stores the thresholds according to measurement type and netCapability. For
74      * ex: LTE_RSRP: {TYPE_IMS: [-112, -110, -90], TYPE_XCAP: [-100, -99]} LTE_RSSNR:{TYPE_IMS:
75      * [-10, -15], TYPE_EMERGENCY: [-15]}
76      */
77     private final ConcurrentHashMap<String, SparseArray<List<Integer>>> mThresholdMatrix =
78             new ConcurrentHashMap<>();
79 
80     private final HashMap<String, int[]> mThresholdsRegistered = new HashMap<>();
81     private HashMap<String, Integer> mThresholdWaitTimer = new HashMap<>();
82     private SignalStrengthUpdateRequest mSSUpdateRequest;
83     private final CellularSignalStrengthListener mSignalStrengthListener;
84     private final QnsTelephonyListener mQnsTelephonyListener;
85     @VisibleForTesting final Handler mHandler;
86     /**
87      * Constructor to instantiate CellularQualityMonitor
88      *
89      * @param context application context
90      * @param listener QnsTelephonyListener instance
91      * @param slotIndex slot index
92      */
CellularQualityMonitor(Context context, QnsCarrierConfigManager configMgr, QnsTelephonyListener listener, int slotIndex)93     CellularQualityMonitor(Context context,
94             QnsCarrierConfigManager configMgr,
95             QnsTelephonyListener listener,
96             int slotIndex) {
97         super(QualityMonitor.class.getSimpleName() + "-C-" + slotIndex);
98         mContext = context;
99         mSlotIndex = slotIndex;
100         mQnsTelephonyListener = listener;
101 
102         mTag = CellularQualityMonitor.class.getSimpleName() + "-" + mSlotIndex;
103         mSubId = QnsUtils.getSubId(mContext, mSlotIndex);
104         mIsQnsListenerRegistered = false;
105         mSignalThresholdInfoList = new ArrayList<>();
106         mHandlerThread = new HandlerThread(mTag);
107         mHandlerThread.start();
108         mHandler = new CellularEventsHandler(mHandlerThread.getLooper());
109         mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
110         mQnsTelephonyListener.registerSubscriptionIdListener(
111                 mHandler, EVENT_SUBSCRIPTION_ID_CHANGED, null);
112         if (mTelephonyManager != null) {
113             mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
114         } else {
115             Log.e(mTag, "Failed to get Telephony Service");
116         }
117         mConfigManager = configMgr;
118         mSignalStrengthListener = new CellularSignalStrengthListener(mContext.getMainExecutor());
119         mSignalStrengthListener.setSignalStrengthListener(this::onSignalStrengthsChanged);
120     }
121 
122     /** Listener for change of signal strength. */
123     private interface OnSignalStrengthListener {
124         /** Notify the cellular signal strength changed. */
onSignalStrengthsChanged(SignalStrength signalStrength)125         void onSignalStrengthsChanged(SignalStrength signalStrength);
126     }
127 
128     /** {@link TelephonyCallback} to listen to Cellular Service State Changed. */
129     private class CellularSignalStrengthListener extends TelephonyCallback
130             implements TelephonyCallback.SignalStrengthsListener {
131         private OnSignalStrengthListener mSignalStrengthListener;
132         private Executor mExecutor;
133 
CellularSignalStrengthListener(Executor executor)134         CellularSignalStrengthListener(Executor executor) {
135             super();
136             mExecutor = executor;
137         }
138 
setSignalStrengthListener(OnSignalStrengthListener listener)139         void setSignalStrengthListener(OnSignalStrengthListener listener) {
140             mSignalStrengthListener = listener;
141         }
142 
143         /** Register a TelephonyCallback for this listener. */
register()144         void register() {
145             long identity = Binder.clearCallingIdentity();
146             try {
147                 mTelephonyManager.registerTelephonyCallback(mExecutor, this);
148             } finally {
149                 Binder.restoreCallingIdentity(identity);
150             }
151         }
152 
153         /** Unregister a TelephonyCallback for this listener. */
unregister()154         void unregister() {
155             mTelephonyManager.unregisterTelephonyCallback(this);
156         }
157 
158         @Override
onSignalStrengthsChanged(@onNull SignalStrength signalStrength)159         public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
160             if (mSignalStrengthListener != null) {
161                 Log.d(mTag, "Signal Strength Changed : " + signalStrength);
162                 mSignalStrengthListener.onSignalStrengthsChanged(signalStrength);
163             }
164         }
165     }
166 
onSignalStrengthsChanged(SignalStrength signalStrength)167     private void onSignalStrengthsChanged(SignalStrength signalStrength) {
168         List<CellSignalStrength> ss = signalStrength.getCellSignalStrengths();
169         if (!ss.isEmpty()) {
170             for (CellSignalStrength cs : ss) {
171                 checkAndNotifySignalStrength(cs);
172             }
173         }
174     }
175 
checkAndNotifySignalStrength(CellSignalStrength cellSignalStrength)176     private void checkAndNotifySignalStrength(CellSignalStrength cellSignalStrength) {
177         Log.d(mTag, "CellSignalStrength Changed: " + cellSignalStrength);
178 
179         int signalStrength;
180         for (Map.Entry<String, List<Threshold>> entry : mThresholdsList.entrySet()) {
181             // check if key is in waiting list of backhaul
182             if (mWaitingThresholds.getOrDefault(entry.getKey(), false)) {
183                 Log.d(mTag, "Backhaul timer already running for the threshold");
184                 continue;
185             }
186             List<Threshold> matchedThresholds = new ArrayList<>();
187             Threshold threshold;
188             for (Threshold th : entry.getValue()) {
189                 signalStrength =
190                         getSignalStrength(
191                                 th.getAccessNetwork(), th.getMeasurementType(), cellSignalStrength);
192                 if (signalStrength != UNAVAILABLE && th.isMatching(signalStrength)) {
193                     threshold = th.copy();
194                     threshold.setThreshold(signalStrength);
195                     matchedThresholds.add(threshold);
196                 }
197             }
198             if (matchedThresholds.size() > 0) {
199                 notifyThresholdChange(entry.getKey(), matchedThresholds.toArray(new Threshold[0]));
200             }
201         }
202     }
203 
204     @Override
getCurrentQuality(int accessNetwork, int measurementType)205     synchronized int getCurrentQuality(int accessNetwork, int measurementType) {
206         SignalStrength ss = mTelephonyManager.getSignalStrength();
207         int quality = SignalStrength.INVALID; // Int Max Value
208         if (ss != null) {
209             List<CellSignalStrength> cellSignalStrengthList = ss.getCellSignalStrengths();
210             for (CellSignalStrength cs : cellSignalStrengthList) {
211                 quality = getSignalStrength(accessNetwork, measurementType, cs);
212                 if (quality != UNAVAILABLE) {
213                     return quality;
214                 }
215             }
216         }
217         return quality;
218     }
219 
220     @Override
registerThresholdChange( ThresholdCallback thresholdCallback, int netCapability, Threshold[] ths, int slotIndex)221     synchronized void registerThresholdChange(
222             ThresholdCallback thresholdCallback,
223             int netCapability,
224             Threshold[] ths,
225             int slotIndex) {
226         Log.d(mTag, "registerThresholdChange for netCapability= " + netCapability);
227         super.registerThresholdChange(thresholdCallback, netCapability, ths, slotIndex);
228         updateThresholdsForNetCapability(netCapability, slotIndex, ths);
229     }
230 
231     @Override
unregisterThresholdChange(int netCapability, int slotIndex)232     synchronized void unregisterThresholdChange(int netCapability, int slotIndex) {
233         Log.d(mTag, "unregisterThresholdChange for netCapability= " + netCapability);
234         super.unregisterThresholdChange(netCapability, slotIndex);
235         updateThresholdsMatrix(netCapability, null);
236         if (updateRegisteredThresholdsArray()) {
237             createSignalThresholdsInfoList();
238             listenRequests();
239         }
240     }
241 
242     @Override
updateThresholdsForNetCapability( int netCapability, int slotIndex, Threshold[] ths)243     synchronized void updateThresholdsForNetCapability(
244             int netCapability, int slotIndex, Threshold[] ths) {
245         Log.d(mTag, "updateThresholdsForNetCapability for netCapability= " + netCapability);
246         super.updateThresholdsForNetCapability(netCapability, slotIndex, ths);
247         if (ths != null && ths.length > 0 && !validateThresholdList(ths)) {
248             throw new IllegalStateException("Thresholds are not in valid range.");
249         }
250         updateThresholdsMatrix(netCapability, ths);
251         if (updateRegisteredThresholdsArray()) {
252             createSignalThresholdsInfoList();
253             listenRequests();
254         }
255     }
256 
257     @Override
notifyThresholdChange(String key, Threshold[] ths)258     protected void notifyThresholdChange(String key, Threshold[] ths) {
259         IThresholdListener listener = mThresholdCallbackMap.get(key);
260         Log.d(mTag, "Notify Threshold Change to listener = " + listener);
261         if (listener != null) {
262             listener.onCellularThresholdChanged(ths);
263         }
264     }
265 
createSignalThresholdsInfoList()266     private void createSignalThresholdsInfoList() {
267         mSignalThresholdInfoList.clear();
268         for (Map.Entry<String, int[]> entry : mThresholdsRegistered.entrySet()) {
269             if (entry.getValue().length == 0) continue;
270             int networkType = Integer.parseInt(entry.getKey().split("_")[0]);
271             int measurementType = Integer.parseInt(entry.getKey().split("_")[1]);
272             SignalThresholdInfo.Builder builder =
273                     new SignalThresholdInfo.Builder()
274                             .setRadioAccessNetworkType(networkType)
275                             .setSignalMeasurementType(measurementType)
276                             .setThresholds(entry.getValue());
277             int backhaulTime = mThresholdWaitTimer.getOrDefault(entry.getKey(), -1);
278             if (backhaulTime > 0) {
279                 builder.setHysteresisMs(backhaulTime);
280             }
281             int hysteresisDb = mConfigManager.getWwanHysteresisDbLevel(networkType,
282                     measurementType);
283             builder.setHysteresisDb(hysteresisDb);
284             mSignalThresholdInfoList.add(builder.build());
285             Log.d(mTag, "Updated SignalThresholdInfo List: " + mSignalThresholdInfoList);
286         }
287     }
288 
updateRegisteredThresholdsArray()289     private boolean updateRegisteredThresholdsArray() {
290         boolean isUpdated = false;
291         for (Map.Entry<String, SparseArray<List<Integer>>> entry : mThresholdMatrix.entrySet()) {
292             SparseArray<List<Integer>> netCapabilityThresholds =
293                     mThresholdMatrix.getOrDefault(entry.getKey(), new SparseArray<>());
294             Set<Integer> thresholdsSet = new HashSet<>(); // to store unique thresholds
295             int count = 0;
296             for (int i = 0;
297                     (i < netCapabilityThresholds.size() && count <= MAX_THRESHOLD_COUNT);
298                     i++) {
299                 List<Integer> thresholdsList =
300                         netCapabilityThresholds.get(
301                                 netCapabilityThresholds.keyAt(i), new ArrayList<>());
302                 for (int t : thresholdsList) {
303                     if (thresholdsSet.add(t)) {
304                         count++;
305                     }
306                     if (count >= MAX_THRESHOLD_COUNT) {
307                         break;
308                     }
309                 }
310             }
311             int[] newThresholds = new int[thresholdsSet.size()];
312             count = 0;
313             for (int i : thresholdsSet) {
314                 newThresholds[count++] = i;
315             }
316             Arrays.sort(newThresholds);
317             int[] oldThresholds = mThresholdsRegistered.get(entry.getKey());
318             Log.d(
319                     mTag,
320                     "For measurement type= "
321                             + entry.getKey()
322                             + " old Threshold= "
323                             + Arrays.toString(oldThresholds)
324                             + " new Threshold= "
325                             + Arrays.toString(newThresholds));
326             if (!Arrays.equals(newThresholds, oldThresholds)) {
327                 mThresholdsRegistered.put(entry.getKey(), newThresholds);
328                 isUpdated = true;
329             }
330         }
331         return isUpdated;
332     }
333 
updateThresholdsMatrix(int netCapability, Threshold[] ths)334     private void updateThresholdsMatrix(int netCapability, Threshold[] ths) {
335 
336         Log.d(mTag, "Current threshold matrix: " + mThresholdMatrix);
337         // clear old threshold for the netCapability in given netCapability from threshold matrix.
338         for (Map.Entry<String, SparseArray<List<Integer>>> entry : mThresholdMatrix.entrySet()) {
339             SparseArray<List<Integer>> netCapabilityThresholds =
340                     mThresholdMatrix.get(entry.getKey());
341             if (netCapabilityThresholds != null) {
342                 netCapabilityThresholds.remove(netCapability);
343             }
344         }
345         if (ths == null || ths.length == 0) {
346             return;
347         }
348 
349         // store new thresholds in threshold matrix
350         for (Threshold th : ths) {
351             String key = th.getAccessNetwork() + "_" + th.getMeasurementType();
352             SparseArray<List<Integer>> netCapabilityThresholds =
353                     mThresholdMatrix.getOrDefault(key, new SparseArray<>());
354             List<Integer> thresholdsList =
355                     netCapabilityThresholds.get(netCapability, new ArrayList<>());
356             thresholdsList.add(th.getThreshold());
357             netCapabilityThresholds.put(netCapability, thresholdsList);
358             mThresholdMatrix.put(key, netCapabilityThresholds);
359             mThresholdWaitTimer.put(key, th.getWaitTime());
360         }
361         Log.d(mTag, "updated thresholds matrix: " + mThresholdMatrix);
362     }
363 
364     /** This methods stops listening for the thresholds. */
clearOldRequests()365     private synchronized void clearOldRequests() {
366         if (mSSUpdateRequest != null) {
367             Log.d(mTag, "Clearing request: " + mSSUpdateRequest);
368             mTelephonyManager.clearSignalStrengthUpdateRequest(mSSUpdateRequest);
369             mSSUpdateRequest = null;
370         }
371         mSignalStrengthListener.unregister();
372     }
373 
374     /** This methods starts listening for the thresholds. */
listenRequests()375     private void listenRequests() {
376         clearOldRequests();
377         if (mSignalThresholdInfoList.size() > 0) {
378             mSSUpdateRequest =
379                     new SignalStrengthUpdateRequest.Builder()
380                             .setSignalThresholdInfos(mSignalThresholdInfoList)
381                             .setReportingRequestedWhileIdle(true)
382                             .build();
383             mTelephonyManager.setSignalStrengthUpdateRequest(mSSUpdateRequest);
384             Log.d(mTag, "Listening to request: " + mSSUpdateRequest);
385             mSignalStrengthListener.register();
386             if (!mIsQnsListenerRegistered) {
387                 mQnsTelephonyListener.registerQnsTelephonyInfoChanged(
388                         NetworkCapabilities.NET_CAPABILITY_IMS,
389                         mHandler,
390                         EVENT_CELLULAR_QNS_TELEPHONY_INFO_CHANGED,
391                         null,
392                         false);
393                 mIsQnsListenerRegistered = true;
394             }
395         } else {
396             Log.d(mTag, "No requests are pending to listen");
397             mQnsTelephonyListener.unregisterQnsTelephonyInfoChanged(
398                     NetworkCapabilities.NET_CAPABILITY_IMS, mHandler);
399             mIsQnsListenerRegistered = false;
400         }
401     }
402 
getSignalStrength(int accessNetwork, int measurementType, CellSignalStrength css)403     private int getSignalStrength(int accessNetwork, int measurementType, CellSignalStrength css) {
404         int signalStrength = UNAVAILABLE;
405         switch (measurementType) {
406             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
407                 if (accessNetwork == AccessNetworkType.GERAN
408                         && css instanceof CellSignalStrengthGsm) {
409                     signalStrength = ((CellSignalStrengthGsm) css).getRssi();
410                 }
411                 break;
412             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
413                 if (accessNetwork == AccessNetworkType.UTRAN
414                         && css instanceof CellSignalStrengthWcdma) {
415                     signalStrength = ((CellSignalStrengthWcdma) css).getDbm();
416                 }
417                 break;
418             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
419                 if (accessNetwork == AccessNetworkType.EUTRAN
420                         && css instanceof CellSignalStrengthLte) {
421                     signalStrength = ((CellSignalStrengthLte) css).getRsrp();
422                 }
423                 break;
424             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
425                 if (accessNetwork == AccessNetworkType.EUTRAN
426                         && css instanceof CellSignalStrengthLte) {
427                     signalStrength = ((CellSignalStrengthLte) css).getRsrq();
428                 }
429                 break;
430             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR:
431                 if (accessNetwork == AccessNetworkType.EUTRAN
432                         && css instanceof CellSignalStrengthLte) {
433                     signalStrength = ((CellSignalStrengthLte) css).getRssnr();
434                 }
435                 break;
436             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP:
437                 if (accessNetwork == AccessNetworkType.NGRAN
438                         && css instanceof CellSignalStrengthNr) {
439                     signalStrength = ((CellSignalStrengthNr) css).getSsRsrp();
440                 }
441                 break;
442             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
443                 if (accessNetwork == AccessNetworkType.NGRAN
444                         && css instanceof CellSignalStrengthNr) {
445                     signalStrength = ((CellSignalStrengthNr) css).getSsRsrq();
446                 }
447                 break;
448             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR:
449                 if (accessNetwork == AccessNetworkType.NGRAN
450                         && css instanceof CellSignalStrengthNr) {
451                     signalStrength = ((CellSignalStrengthNr) css).getSsSinr();
452                 }
453                 break;
454             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO:
455                 if (accessNetwork == AccessNetworkType.UTRAN
456                         && css instanceof CellSignalStrengthWcdma) {
457                     signalStrength = ((CellSignalStrengthWcdma) css).getEcNo();
458                 }
459                 break;
460             default:
461                 Log.d(mTag, "measurement type = " + measurementType + " not handled.");
462                 break;
463         }
464         return signalStrength;
465     }
466 
validateThresholdList(Threshold[] ths)467     private boolean validateThresholdList(Threshold[] ths) {
468         for (Threshold threshold : ths) {
469             if (!isValidThreshold(threshold.getMeasurementType(), threshold.getThreshold())) {
470                 return false;
471             }
472         }
473         return true;
474     }
475 
476     /** Return true if signal measurement type is valid and the threshold value is in range. */
isValidThreshold(int type, int threshold)477     private static boolean isValidThreshold(int type, int threshold) {
478         switch (type) {
479             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
480                 return threshold >= SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE
481                         && threshold <= SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE;
482             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
483                 return threshold >= SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE
484                         && threshold <= SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE;
485             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
486                 return threshold >= SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE
487                         && threshold <= SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE;
488             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
489                 return threshold >= SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE
490                         && threshold <= SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE;
491             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR:
492                 return threshold >= SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE
493                         && threshold <= SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE;
494             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP:
495                 return threshold >= SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE
496                         && threshold <= SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE;
497             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
498                 return threshold >= SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE
499                         && threshold <= SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE;
500             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR:
501                 return threshold >= SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE
502                         && threshold <= SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE;
503             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO:
504                 return threshold >= SignalThresholdInfo.SIGNAL_ECNO_MIN_VALUE
505                         && threshold <= SignalThresholdInfo.SIGNAL_ECNO_MAX_VALUE;
506 
507             default:
508                 return false;
509         }
510     }
511 
512     @VisibleForTesting
getSignalThresholdInfo()513     List<SignalThresholdInfo> getSignalThresholdInfo() {
514         return mSignalThresholdInfoList;
515     }
516 
517     private class CellularEventsHandler extends Handler {
CellularEventsHandler(Looper looper)518         CellularEventsHandler(Looper looper) {
519             super(looper);
520         }
521 
522         @Override
handleMessage(@onNull Message msg)523         public void handleMessage(@NonNull Message msg) {
524             Log.d(mTag, "handleMessage what = " + msg.what);
525             QnsAsyncResult ar;
526             switch (msg.what) {
527                 case EVENT_CELLULAR_QNS_TELEPHONY_INFO_CHANGED:
528                     ar = (QnsAsyncResult) msg.obj;
529                     if (ar.mException == null
530                             && ar.mResult instanceof QnsTelephonyListener.QnsTelephonyInfo) {
531                         QnsTelephonyListener.QnsTelephonyInfo info =
532                                 (QnsTelephonyListener.QnsTelephonyInfo) ar.mResult;
533                         onQnsTelephonyInfoChanged(info);
534                     }
535                     break;
536                 case EVENT_SUBSCRIPTION_ID_CHANGED:
537                     ar = (QnsAsyncResult) msg.obj;
538                     int newSubId = (int) ar.mResult;
539                     clearOldRequests();
540                     mSubId = newSubId;
541                     mTelephonyManager =
542                             mContext.getSystemService(TelephonyManager.class)
543                                     .createForSubscriptionId(mSubId);
544                     break;
545                 default:
546                     Log.d(mTag, "Not Handled !");
547             }
548         }
549 
550         QnsTelephonyListener.QnsTelephonyInfo mLastQnsTelephonyInfo = null;
551 
onQnsTelephonyInfoChanged(QnsTelephonyListener.QnsTelephonyInfo info)552         private void onQnsTelephonyInfoChanged(QnsTelephonyListener.QnsTelephonyInfo info) {
553             if (mLastQnsTelephonyInfo == null
554                     || mLastQnsTelephonyInfo.getDataNetworkType() != info.getDataNetworkType()
555                     || mLastQnsTelephonyInfo.getDataRegState() != info.getDataRegState()
556                     || mLastQnsTelephonyInfo.isCellularAvailable() != info.isCellularAvailable()) {
557                 if (!info.isCellularAvailable()) {
558                     clearOldRequests();
559                 }
560                 mLastQnsTelephonyInfo = info;
561             }
562         }
563     }
564 
565     @VisibleForTesting
566     @Override
close()567     public void close() {
568         mQnsTelephonyListener.unregisterSubscriptionIdChanged(mHandler);
569         clearOldRequests();
570         mSignalThresholdInfoList.clear();
571         mIsQnsListenerRegistered = false;
572         if (mHandlerThread != null) {
573             mHandlerThread.quit();
574         }
575     }
576 
577     @Override
dump(PrintWriter pw, String prefix)578     void dump(PrintWriter pw, String prefix) {
579         pw.println(prefix + "------------------------------");
580         pw.println(prefix + "CellularQualityMonitor[" + mSlotIndex + "]:");
581         pw.println(prefix + "mSubId=" + mSubId);
582         super.dump(pw, prefix);
583         pw.println(prefix + "mIsQnsListenerRegistered=" + mIsQnsListenerRegistered);
584         pw.println(prefix + "mSignalThresholdInfoList=" + mSignalThresholdInfoList);
585         pw.println(prefix + "mSSUpdateRequest=" + mSSUpdateRequest);
586         pw.println(prefix + "mThresholdMatrix=" + mThresholdMatrix);
587         pw.println(prefix + "mThresholdsRegistered=" + mThresholdsRegistered);
588         pw.println(prefix + "mThresholdWaitTimer=" + mThresholdWaitTimer);
589     }
590 }
591