1 /*
2  * Copyright (C) 2020 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;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.os.AsyncResult;
24 import android.os.Message;
25 import android.os.PersistableBundle;
26 import android.telephony.AccessNetworkConstants;
27 import android.telephony.Annotation;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.NetworkRegistrationInfo;
30 import android.telephony.RadioAccessFamily;
31 import android.telephony.ServiceState;
32 import android.telephony.SubscriptionManager;
33 import android.telephony.TelephonyDisplayInfo;
34 import android.telephony.TelephonyManager;
35 import android.text.TextUtils;
36 
37 import com.android.internal.telephony.dataconnection.DcController;
38 import com.android.internal.telephony.dataconnection.DcController.PhysicalLinkState;
39 import com.android.internal.util.IState;
40 import com.android.internal.util.IndentingPrintWriter;
41 import com.android.internal.util.State;
42 import com.android.internal.util.StateMachine;
43 import com.android.telephony.Rlog;
44 
45 import java.io.FileDescriptor;
46 import java.io.PrintWriter;
47 import java.util.ArrayList;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.regex.Matcher;
52 import java.util.regex.Pattern;
53 
54 /**
55  * The NetworkTypeController evaluates the override network type of {@link TelephonyDisplayInfo}
56  * and sends it to {@link DisplayInfoController}. The override network type can replace the signal
57  * icon displayed on the status bar. It is affected by changes in data RAT, NR state, NR frequency,
58  * data activity, physical channel config, and carrier configurations. Based on carrier configs,
59  * NetworkTypeController also allows timers between various 5G states to prevent flickering.
60  */
61 public class NetworkTypeController extends StateMachine {
62     private static final boolean DBG = true;
63     private static final String TAG = "NetworkTypeController";
64     private static final String ICON_5G = "5g";
65     private static final String ICON_5G_PLUS = "5g_plus";
66     private static final String STATE_CONNECTED_MMWAVE = "connected_mmwave";
67     private static final String STATE_CONNECTED = "connected";
68     private static final String STATE_NOT_RESTRICTED_RRC_IDLE = "not_restricted_rrc_idle";
69     private static final String STATE_NOT_RESTRICTED_RRC_CON = "not_restricted_rrc_con";
70     private static final String STATE_RESTRICTED = "restricted";
71     private static final String STATE_ANY = "any";
72     private static final String STATE_LEGACY = "legacy";
73     private static final String[] ALL_STATES = { STATE_CONNECTED_MMWAVE, STATE_CONNECTED,
74             STATE_NOT_RESTRICTED_RRC_IDLE, STATE_NOT_RESTRICTED_RRC_CON, STATE_RESTRICTED,
75             STATE_LEGACY };
76 
77     /** Stop all timers and go to current state. */
78     public static final int EVENT_UPDATE = 0;
79     /** Quit after processing all existing messages. */
80     public static final int EVENT_QUIT = 1;
81     private static final int EVENT_DATA_RAT_CHANGED = 2;
82     private static final int EVENT_NR_STATE_CHANGED = 3;
83     private static final int EVENT_NR_FREQUENCY_CHANGED = 4;
84     private static final int EVENT_PHYSICAL_LINK_STATE_CHANGED = 5;
85     private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6;
86     private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
87     private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8;
88     private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9;
89     private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
90     private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
91     private static final int EVENT_INITIALIZE = 12;
92     // events that don't reset the timer
93     private static final int[] ALL_EVENTS = { EVENT_DATA_RAT_CHANGED, EVENT_NR_STATE_CHANGED,
94             EVENT_NR_FREQUENCY_CHANGED, EVENT_PHYSICAL_LINK_STATE_CHANGED,
95             EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, EVENT_PRIMARY_TIMER_EXPIRED,
96             EVENT_SECONDARY_TIMER_EXPIRED};
97 
98     private static final String[] sEvents = new String[EVENT_INITIALIZE + 1];
99     static {
100         sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
101         sEvents[EVENT_QUIT] = "EVENT_QUIT";
102         sEvents[EVENT_DATA_RAT_CHANGED] = "EVENT_DATA_RAT_CHANGED";
103         sEvents[EVENT_NR_STATE_CHANGED] = "EVENT_NR_STATE_CHANGED";
104         sEvents[EVENT_NR_FREQUENCY_CHANGED] = "EVENT_NR_FREQUENCY_CHANGED";
105         sEvents[EVENT_PHYSICAL_LINK_STATE_CHANGED] = "EVENT_PHYSICAL_LINK_STATE_CHANGED";
106         sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED] =
107                 "EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED";
108         sEvents[EVENT_CARRIER_CONFIG_CHANGED] = "EVENT_CARRIER_CONFIG_CHANGED";
109         sEvents[EVENT_PRIMARY_TIMER_EXPIRED] = "EVENT_PRIMARY_TIMER_EXPIRED";
110         sEvents[EVENT_SECONDARY_TIMER_EXPIRED] = "EVENT_SECONDARY_TIMER_EXPIRED";
111         sEvents[EVENT_RADIO_OFF_OR_UNAVAILABLE] = "EVENT_RADIO_OFF_OR_UNAVAILABLE";
112         sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
113         sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
114     }
115 
116     private final Phone mPhone;
117     private final DisplayInfoController mDisplayInfoController;
118     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
119         @Override
120         public void onReceive(Context context, Intent intent) {
121             if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
122                     && intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
123                     SubscriptionManager.INVALID_PHONE_INDEX) == mPhone.getPhoneId()
124                     && !intent.getBooleanExtra(CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK,
125                     false)) {
126                 sendMessage(EVENT_CARRIER_CONFIG_CHANGED);
127             }
128         }
129     };
130 
131     private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
132     private String mLteEnhancedPattern = "";
133     private int mOverrideNetworkType;
134     private boolean mIsPhysicalChannelConfigOn;
135     private boolean mIsPrimaryTimerActive;
136     private boolean mIsSecondaryTimerActive;
137     private boolean mIsTimerResetEnabledForLegacyStateRRCIdle;
138     private String mPrimaryTimerState;
139     private String mSecondaryTimerState;
140     private String mPreviousState;
141     private @PhysicalLinkState int mPhysicalLinkState;
142 
143     /**
144      * NetworkTypeController constructor.
145      *
146      * @param phone Phone object.
147      * @param displayInfoController DisplayInfoController to send override network types to.
148      */
NetworkTypeController(Phone phone, DisplayInfoController displayInfoController)149     public NetworkTypeController(Phone phone, DisplayInfoController displayInfoController) {
150         super(TAG, displayInfoController);
151         mPhone = phone;
152         mDisplayInfoController = displayInfoController;
153         mOverrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
154         mIsPhysicalChannelConfigOn = true;
155         addState(mDefaultState);
156         addState(mLegacyState, mDefaultState);
157         addState(mIdleState, mDefaultState);
158         addState(mLteConnectedState, mDefaultState);
159         addState(mNrConnectedState, mDefaultState);
160         setInitialState(mDefaultState);
161         start();
162         sendMessage(EVENT_INITIALIZE);
163     }
164 
165     /**
166      * @return The current override network type, used to create TelephonyDisplayInfo in
167      * DisplayInfoController.
168      */
getOverrideNetworkType()169     public @Annotation.OverrideNetworkType int getOverrideNetworkType() {
170         return mOverrideNetworkType;
171     }
172 
registerForAllEvents()173     private void registerForAllEvents() {
174         mPhone.registerForRadioOffOrNotAvailable(getHandler(),
175                 EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
176         mPhone.registerForPreferredNetworkTypeChanged(getHandler(),
177                 EVENT_PREFERRED_NETWORK_MODE_CHANGED, null);
178         mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
179                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(),
180                 EVENT_DATA_RAT_CHANGED, null);
181         mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
182                 .registerForPhysicalLinkStateChanged(getHandler(),
183                         EVENT_PHYSICAL_LINK_STATE_CHANGED);
184         mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
185                 EVENT_NR_STATE_CHANGED, null);
186         mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
187                 EVENT_NR_FREQUENCY_CHANGED, null);
188         mPhone.getDeviceStateMonitor().registerForPhysicalChannelConfigNotifChanged(getHandler(),
189                 EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, null);
190         IntentFilter filter = new IntentFilter();
191         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
192         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
193     }
194 
unRegisterForAllEvents()195     private void unRegisterForAllEvents() {
196         mPhone.unregisterForRadioOffOrNotAvailable(getHandler());
197         mPhone.unregisterForPreferredNetworkTypeChanged(getHandler());
198         mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(
199                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler());
200         mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
201         mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
202         mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
203         mPhone.getContext().unregisterReceiver(mIntentReceiver);
204     }
205 
parseCarrierConfigs()206     private void parseCarrierConfigs() {
207         String nrIconConfiguration = CarrierConfigManager.getDefaultConfig().getString(
208                 CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
209         String overrideTimerRule = CarrierConfigManager.getDefaultConfig().getString(
210                 CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
211         String overrideSecondaryTimerRule = CarrierConfigManager.getDefaultConfig().getString(
212                 CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
213         mLteEnhancedPattern = CarrierConfigManager.getDefaultConfig().getString(
214                 CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
215         mIsTimerResetEnabledForLegacyStateRRCIdle =
216                 CarrierConfigManager.getDefaultConfig().getBoolean(
217                         CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
218 
219         CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
220                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
221         if (configManager != null) {
222             PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
223             if (b != null) {
224                 if (b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING) != null) {
225                     nrIconConfiguration = b.getString(
226                             CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
227                 }
228                 if (b.getString(CarrierConfigManager
229                         .KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING) != null) {
230                     overrideTimerRule = b.getString(
231                             CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
232                 }
233                 if (b.getString(CarrierConfigManager
234                         .KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING) != null) {
235                     overrideSecondaryTimerRule = b.getString(
236                             CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
237                 }
238                 if (b.getString(CarrierConfigManager
239                         .KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING) != null) {
240                     mLteEnhancedPattern = b.getString(
241                             CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
242                 }
243                 mIsTimerResetEnabledForLegacyStateRRCIdle = b.getBoolean(
244                         CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
245             }
246         }
247         createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
248     }
249 
createTimerRules(String icons, String timers, String secondaryTimers)250     private void createTimerRules(String icons, String timers, String secondaryTimers) {
251         Map<String, OverrideTimerRule> tempRules = new HashMap<>();
252         if (!TextUtils.isEmpty(icons)) {
253             // Format: "STATE:ICON,STATE2:ICON2"
254             for (String pair : icons.trim().split(",")) {
255                 String[] kv = (pair.trim().toLowerCase()).split(":");
256                 if (kv.length != 2) {
257                     if (DBG) loge("Invalid 5G icon configuration, config = " + pair);
258                     continue;
259                 }
260                 int icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
261                 if (kv[1].equals(ICON_5G)) {
262                     icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
263                 } else if (kv[1].equals(ICON_5G_PLUS)) {
264                     icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
265                 } else {
266                     if (DBG) loge("Invalid 5G icon = " + kv[1]);
267                 }
268                 tempRules.put(kv[0], new OverrideTimerRule(kv[0], icon));
269             }
270         }
271         // Ensure all states have an associated OverrideTimerRule and icon
272         for (String state : ALL_STATES) {
273             if (!tempRules.containsKey(state)) {
274                 tempRules.put(state, new OverrideTimerRule(
275                         state, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE));
276             }
277         }
278 
279         if (!TextUtils.isEmpty(timers)) {
280             // Format: "FROM_STATE,TO_STATE,DURATION;FROM_STATE_2,TO_STATE_2,DURATION_2"
281             for (String triple : timers.trim().split(";")) {
282                 String[] kv = (triple.trim().toLowerCase()).split(",");
283                 if (kv.length != 3) {
284                     if (DBG) loge("Invalid 5G icon timer configuration, config = " + triple);
285                     continue;
286                 }
287                 int duration;
288                 try {
289                     duration = Integer.parseInt(kv[2]);
290                 } catch (NumberFormatException e) {
291                     continue;
292                 }
293                 if (kv[0].equals(STATE_ANY)) {
294                     for (String state : ALL_STATES) {
295                         OverrideTimerRule node = tempRules.get(state);
296                         node.addTimer(kv[1], duration);
297                     }
298                 } else {
299                     OverrideTimerRule node = tempRules.get(kv[0]);
300                     node.addTimer(kv[1], duration);
301                 }
302             }
303         }
304 
305         if (!TextUtils.isEmpty(secondaryTimers)) {
306             // Format: "PRIMARY_STATE,TO_STATE,DURATION;PRIMARY_STATE_2,TO_STATE_2,DURATION_2"
307             for (String triple : secondaryTimers.trim().split(";")) {
308                 String[] kv = (triple.trim().toLowerCase()).split(",");
309                 if (kv.length != 3) {
310                     if (DBG) {
311                         loge("Invalid 5G icon secondary timer configuration, config = " + triple);
312                     }
313                     continue;
314                 }
315                 int duration;
316                 try {
317                     duration = Integer.parseInt(kv[2]);
318                 } catch (NumberFormatException e) {
319                     continue;
320                 }
321                 if (kv[0].equals(STATE_ANY)) {
322                     for (String state : ALL_STATES) {
323                         OverrideTimerRule node = tempRules.get(state);
324                         node.addSecondaryTimer(kv[1], duration);
325                     }
326                 } else {
327                     OverrideTimerRule node = tempRules.get(kv[0]);
328                     node.addSecondaryTimer(kv[1], duration);
329                 }
330             }
331         }
332 
333         mOverrideTimerRules = tempRules;
334         if (DBG) log("mOverrideTimerRules: " + mOverrideTimerRules);
335     }
336 
updateOverrideNetworkType()337     private void updateOverrideNetworkType() {
338         if (mIsPrimaryTimerActive || mIsSecondaryTimerActive) {
339             if (DBG) log("Skip updating override network type since timer is active.");
340             return;
341         }
342         mOverrideNetworkType = getCurrentOverrideNetworkType();
343         mDisplayInfoController.updateTelephonyDisplayInfo();
344     }
345 
getCurrentOverrideNetworkType()346     private @Annotation.OverrideNetworkType int getCurrentOverrideNetworkType() {
347         int displayNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
348         int dataNetworkType = mPhone.getServiceState().getDataNetworkType();
349         // NR display is not accurate when physical channel config notifications are off
350         if (mIsPhysicalChannelConfigOn
351                 && (mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE
352                 || dataNetworkType == TelephonyManager.NETWORK_TYPE_NR)) {
353             // Process NR display network type
354             displayNetworkType = getNrDisplayType();
355             if (displayNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
356                 // Use LTE values if 5G values aren't defined
357                 displayNetworkType = getLteDisplayType();
358             }
359         } else if (isLte(dataNetworkType)) {
360             // Process LTE display network type
361             displayNetworkType = getLteDisplayType();
362         }
363         return displayNetworkType;
364     }
365 
getNrDisplayType()366     private @Annotation.OverrideNetworkType int getNrDisplayType() {
367         // Don't show 5G icon if preferred network type does not include 5G
368         if ((RadioAccessFamily.getRafFromNetworkType(mPhone.getCachedPreferredNetworkType())
369                 & TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
370             return TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
371         }
372         // Icon display keys in order of priority
373         List<String> keys = new ArrayList<>();
374         // TODO: Update for NR SA
375         switch (mPhone.getServiceState().getNrState()) {
376             case NetworkRegistrationInfo.NR_STATE_CONNECTED:
377                 if (isNrMmwave()) {
378                     keys.add(STATE_CONNECTED_MMWAVE);
379                 }
380                 keys.add(STATE_CONNECTED);
381                 break;
382             case NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED:
383                 keys.add(isPhysicalLinkActive() ? STATE_NOT_RESTRICTED_RRC_CON
384                         : STATE_NOT_RESTRICTED_RRC_IDLE);
385                 break;
386             case NetworkRegistrationInfo.NR_STATE_RESTRICTED:
387                 keys.add(STATE_RESTRICTED);
388                 break;
389         }
390 
391         for (String key : keys) {
392             OverrideTimerRule rule = mOverrideTimerRules.get(key);
393             if (rule != null && rule.mOverrideType
394                     != TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
395                 return rule.mOverrideType;
396             }
397         }
398         return TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
399     }
400 
getLteDisplayType()401     private @Annotation.OverrideNetworkType int getLteDisplayType() {
402         int value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
403         if (mPhone.getServiceState().getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA
404                 || mPhone.getServiceState().isUsingCarrierAggregation()) {
405             value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA;
406         }
407         if (isLteEnhancedAvailable()) {
408             value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO;
409         }
410         return value;
411     }
412 
isLteEnhancedAvailable()413     private boolean isLteEnhancedAvailable() {
414         if (TextUtils.isEmpty(mLteEnhancedPattern)) {
415             return false;
416         }
417         Pattern stringPattern = Pattern.compile(mLteEnhancedPattern);
418         for (String opName : new String[] {mPhone.getServiceState().getOperatorAlphaLongRaw(),
419                 mPhone.getServiceState().getOperatorAlphaShortRaw()}) {
420             if (!TextUtils.isEmpty(opName)) {
421                 Matcher matcher = stringPattern.matcher(opName);
422                 if (matcher.find()) {
423                     return true;
424                 }
425             }
426         }
427         return false;
428     }
429 
430     /**
431      * The parent state for all other states.
432      */
433     private final class DefaultState extends State {
434         @Override
processMessage(Message msg)435         public boolean processMessage(Message msg) {
436             if (DBG) log("DefaultState: process " + getEventName(msg.what));
437             switch (msg.what) {
438                 case EVENT_UPDATE:
439                     resetAllTimers();
440                     transitionToCurrentState();
441                     break;
442                 case EVENT_QUIT:
443                     resetAllTimers();
444                     unRegisterForAllEvents();
445                     quit();
446                     break;
447                 case EVENT_INITIALIZE:
448                     // The reason that we do it here is because some of the works below requires
449                     // other modules (e.g. DcTracker, ServiceStateTracker), which is not created
450                     // yet when NetworkTypeController is created.
451                     registerForAllEvents();
452                     parseCarrierConfigs();
453                     break;
454                 case EVENT_DATA_RAT_CHANGED:
455                 case EVENT_NR_STATE_CHANGED:
456                 case EVENT_NR_FREQUENCY_CHANGED:
457                     // ignored
458                     break;
459                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
460                     AsyncResult ar = (AsyncResult) msg.obj;
461                     mPhysicalLinkState = (int) ar.result;
462                     break;
463                 case EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED:
464                     AsyncResult result = (AsyncResult) msg.obj;
465                     mIsPhysicalChannelConfigOn = (boolean) result.result;
466                     if (DBG) {
467                         log("mIsPhysicalChannelConfigOn changed to: " + mIsPhysicalChannelConfigOn);
468                     }
469                     for (int event : ALL_EVENTS) {
470                         removeMessages(event);
471                     }
472                     if (!mIsPhysicalChannelConfigOn) {
473                         resetAllTimers();
474                     }
475                     transitionToCurrentState();
476                     break;
477                 case EVENT_CARRIER_CONFIG_CHANGED:
478                     for (int event : ALL_EVENTS) {
479                         removeMessages(event);
480                     }
481                     parseCarrierConfigs();
482                     resetAllTimers();
483                     transitionToCurrentState();
484                     break;
485                 case EVENT_PRIMARY_TIMER_EXPIRED:
486                     transitionWithSecondaryTimerTo((IState) msg.obj);
487                     break;
488                 case EVENT_SECONDARY_TIMER_EXPIRED:
489                     mIsSecondaryTimerActive = false;
490                     mSecondaryTimerState = "";
491                     updateTimers();
492                     updateOverrideNetworkType();
493                     break;
494                 case EVENT_RADIO_OFF_OR_UNAVAILABLE:
495                     resetAllTimers();
496                     transitionTo(mLegacyState);
497                     break;
498                 case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
499                     resetAllTimers();
500                     transitionToCurrentState();
501                     break;
502                 default:
503                     throw new RuntimeException("Received invalid event: " + msg.what);
504             }
505             return HANDLED;
506         }
507     }
508 
509     private final DefaultState mDefaultState = new DefaultState();
510 
511     /**
512      * Device does not have NR available, due to any of the below reasons:
513      * <ul>
514      *   <li> LTE cell does not support EN-DC
515      *   <li> LTE cell supports EN-DC, but the use of NR is restricted
516      *   <li> Data network type is not LTE, NR NSA, or NR SA
517      * </ul>
518      * This is the initial state.
519      */
520     private final class LegacyState extends State {
521         private Boolean mIsNrRestricted = false;
522 
523         @Override
enter()524         public void enter() {
525             if (DBG) log("Entering LegacyState");
526             updateTimers();
527             updateOverrideNetworkType();
528             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
529                 mIsNrRestricted = isNrRestricted();
530                 mPreviousState = getName();
531             }
532         }
533 
534         @Override
processMessage(Message msg)535         public boolean processMessage(Message msg) {
536             if (DBG) log("LegacyState: process " + getEventName(msg.what));
537             updateTimers();
538             int rat = mPhone.getServiceState().getDataNetworkType();
539             switch (msg.what) {
540                 case EVENT_DATA_RAT_CHANGED:
541                     if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
542                         transitionTo(mNrConnectedState);
543                     } else if (isLte(rat) && isNrNotRestricted()) {
544                         transitionWithTimerTo(isPhysicalLinkActive()
545                                 ? mLteConnectedState : mIdleState);
546                     } else {
547                         if (!isLte(rat)) {
548                             // Rat is 3G or 2G, and it doesn't need NR timer.
549                             resetAllTimers();
550                         }
551                         updateOverrideNetworkType();
552                     }
553                     mIsNrRestricted = isNrRestricted();
554                     break;
555                 case EVENT_NR_STATE_CHANGED:
556                     if (isNrConnected()) {
557                         transitionTo(mNrConnectedState);
558                     } else if (isLte(rat) && isNrNotRestricted()) {
559                         transitionWithTimerTo(isPhysicalLinkActive()
560                                 ? mLteConnectedState : mIdleState);
561                     } else if (isLte(rat) && isNrRestricted()) {
562                         updateOverrideNetworkType();
563                     }
564                     mIsNrRestricted = isNrRestricted();
565                     break;
566                 case EVENT_NR_FREQUENCY_CHANGED:
567                     // ignored
568                     break;
569                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
570                     AsyncResult ar = (AsyncResult) msg.obj;
571                     mPhysicalLinkState = (int) ar.result;
572                     if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
573                         resetAllTimers();
574                         updateOverrideNetworkType();
575                     }
576                     break;
577                 default:
578                     return NOT_HANDLED;
579             }
580             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
581                 mPreviousState = getName();
582             }
583             return HANDLED;
584         }
585 
586         @Override
getName()587         public String getName() {
588             return mIsNrRestricted  ? STATE_RESTRICTED : STATE_LEGACY;
589         }
590     }
591 
592     private final LegacyState mLegacyState = new LegacyState();
593 
594     /**
595      * Device does not have any physical connection with the cell (RRC idle).
596      */
597     private final class IdleState extends State {
598         @Override
enter()599         public void enter() {
600             if (DBG) log("Entering IdleState");
601             updateTimers();
602             updateOverrideNetworkType();
603             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
604                 mPreviousState = getName();
605             }
606         }
607 
608         @Override
processMessage(Message msg)609         public boolean processMessage(Message msg) {
610             if (DBG) log("IdleState: process " + getEventName(msg.what));
611             updateTimers();
612             switch (msg.what) {
613                 case EVENT_DATA_RAT_CHANGED:
614                     int rat = mPhone.getServiceState().getDataNetworkType();
615                     if (rat == TelephonyManager.NETWORK_TYPE_NR) {
616                         transitionTo(mNrConnectedState);
617                     } else if (!isLte(rat) || !isNrNotRestricted()) {
618                         transitionWithTimerTo(mLegacyState);
619                     }
620                     break;
621                 case EVENT_NR_STATE_CHANGED:
622                     if (isNrConnected()) {
623                         transitionTo(mNrConnectedState);
624                     } else if (!isNrNotRestricted()) {
625                         transitionWithTimerTo(mLegacyState);
626                     }
627                     break;
628                 case EVENT_NR_FREQUENCY_CHANGED:
629                     // ignore
630                     break;
631                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
632                     AsyncResult ar = (AsyncResult) msg.obj;
633                     mPhysicalLinkState = (int) ar.result;
634                     if (isNrNotRestricted()) {
635                         // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
636                         if (isPhysicalLinkActive()) {
637                             transitionWithTimerTo(mLteConnectedState);
638                         }
639                     } else {
640                         log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
641                         sendMessage(EVENT_NR_STATE_CHANGED);
642                     }
643                     break;
644                 default:
645                     return NOT_HANDLED;
646             }
647             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
648                 mPreviousState = getName();
649             }
650             return HANDLED;
651         }
652 
653         @Override
getName()654         public String getName() {
655             return STATE_NOT_RESTRICTED_RRC_IDLE;
656         }
657     }
658 
659     private final IdleState mIdleState = new IdleState();
660 
661     /**
662      * Device is connected to LTE as the primary cell (RRC connected).
663      */
664     private final class LteConnectedState extends State {
665         @Override
enter()666         public void enter() {
667             if (DBG) log("Entering LteConnectedState");
668             updateTimers();
669             updateOverrideNetworkType();
670             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
671                 mPreviousState = getName();
672             }
673         }
674 
675         @Override
processMessage(Message msg)676         public boolean processMessage(Message msg) {
677             if (DBG) log("LteConnectedState: process " + getEventName(msg.what));
678             updateTimers();
679             switch (msg.what) {
680                 case EVENT_DATA_RAT_CHANGED:
681                     int rat = mPhone.getServiceState().getDataNetworkType();
682                     if (rat == TelephonyManager.NETWORK_TYPE_NR) {
683                         transitionTo(mNrConnectedState);
684                     } else if (!isLte(rat) || !isNrNotRestricted()) {
685                         transitionWithTimerTo(mLegacyState);
686                     }
687                     break;
688                 case EVENT_NR_STATE_CHANGED:
689                     if (isNrConnected()) {
690                         transitionTo(mNrConnectedState);
691                     } else if (!isNrNotRestricted()) {
692                         transitionWithTimerTo(mLegacyState);
693                     }
694                     break;
695                 case EVENT_NR_FREQUENCY_CHANGED:
696                     // ignore
697                     break;
698                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
699                     AsyncResult ar = (AsyncResult) msg.obj;
700                     mPhysicalLinkState = (int) ar.result;
701                     if (isNrNotRestricted()) {
702                         // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
703                         if (!isPhysicalLinkActive()) {
704                             transitionWithTimerTo(mIdleState);
705                         }
706                     } else {
707                         log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
708                         sendMessage(EVENT_NR_STATE_CHANGED);
709                     }
710                     break;
711                 default:
712                     return NOT_HANDLED;
713             }
714             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
715                 mPreviousState = getName();
716             }
717             return HANDLED;
718         }
719 
720         @Override
getName()721         public String getName() {
722             return STATE_NOT_RESTRICTED_RRC_CON;
723         }
724     }
725 
726     private final LteConnectedState mLteConnectedState = new LteConnectedState();
727 
728     /**
729      * Device is connected to 5G NR as the secondary cell.
730      */
731     private final class NrConnectedState extends State {
732         private Boolean mIsNrMmwave = false;
733 
734         @Override
enter()735         public void enter() {
736             if (DBG) log("Entering NrConnectedState");
737             updateTimers();
738             updateOverrideNetworkType();
739             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
740                 mIsNrMmwave = isNrMmwave();
741                 mPreviousState = getName();
742             }
743         }
744 
745         @Override
processMessage(Message msg)746         public boolean processMessage(Message msg) {
747             if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
748             updateTimers();
749             int rat = mPhone.getServiceState().getDataNetworkType();
750             switch (msg.what) {
751                 case EVENT_DATA_RAT_CHANGED:
752                     if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
753                         updateOverrideNetworkType();
754                     } else if (isLte(rat) && isNrNotRestricted()) {
755                         transitionWithTimerTo(isPhysicalLinkActive()
756                                 ? mLteConnectedState : mIdleState);
757                     } else {
758                         transitionWithTimerTo(mLegacyState);
759                     }
760                     break;
761                 case EVENT_NR_STATE_CHANGED:
762                     if (isLte(rat) && isNrNotRestricted()) {
763                         transitionWithTimerTo(isPhysicalLinkActive()
764                                 ? mLteConnectedState : mIdleState);
765                     } else if (rat != TelephonyManager.NETWORK_TYPE_NR && !isNrConnected()) {
766                         transitionWithTimerTo(mLegacyState);
767                     }
768                     break;
769                 case EVENT_NR_FREQUENCY_CHANGED:
770                     if (!isNrConnected()) {
771                         log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
772                         sendMessage(EVENT_NR_STATE_CHANGED);
773                         break;
774                     }
775                     if (!isNrMmwave()) {
776                         // STATE_CONNECTED_MMWAVE -> STATE_CONNECTED
777                         transitionWithTimerTo(mNrConnectedState);
778                     } else {
779                         // STATE_CONNECTED -> STATE_CONNECTED_MMWAVE
780                         transitionTo(mNrConnectedState);
781                     }
782                     mIsNrMmwave = isNrMmwave();
783                     break;
784                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
785                     AsyncResult ar = (AsyncResult) msg.obj;
786                     mPhysicalLinkState = (int) ar.result;
787                     if (!isNrConnected()) {
788                         log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
789                         sendMessage(EVENT_NR_STATE_CHANGED);
790                     }
791                     break;
792                 default:
793                     return NOT_HANDLED;
794             }
795             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
796                 mPreviousState = getName();
797             }
798             return HANDLED;
799         }
800 
801         @Override
getName()802         public String getName() {
803             return mIsNrMmwave ? STATE_CONNECTED_MMWAVE : STATE_CONNECTED;
804         }
805     }
806 
807     private final NrConnectedState mNrConnectedState = new NrConnectedState();
808 
transitionWithTimerTo(IState destState)809     private void transitionWithTimerTo(IState destState) {
810         String destName = destState.getName();
811         OverrideTimerRule rule = mOverrideTimerRules.get(mPreviousState);
812         if (rule != null && rule.getTimer(destName) > 0) {
813             if (DBG) log("Primary timer started for state: " + mPreviousState);
814             mPrimaryTimerState = mPreviousState;
815             mPreviousState = getCurrentState().getName();
816             mIsPrimaryTimerActive = true;
817             sendMessageDelayed(EVENT_PRIMARY_TIMER_EXPIRED, destState,
818                     rule.getTimer(destName) * 1000);
819         }
820         transitionTo(destState);
821     }
822 
transitionWithSecondaryTimerTo(IState destState)823     private void transitionWithSecondaryTimerTo(IState destState) {
824         String currentName = getCurrentState().getName();
825         OverrideTimerRule rule = mOverrideTimerRules.get(mPrimaryTimerState);
826         if (rule != null && rule.getSecondaryTimer(currentName) > 0) {
827             if (DBG) log("Secondary timer started for state: " + currentName);
828             mSecondaryTimerState = currentName;
829             mPreviousState = currentName;
830             mIsSecondaryTimerActive = true;
831             sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, destState,
832                     rule.getSecondaryTimer(currentName) * 1000);
833         }
834         mIsPrimaryTimerActive = false;
835         transitionTo(getCurrentState());
836     }
837 
transitionToCurrentState()838     private void transitionToCurrentState() {
839         int dataRat = mPhone.getServiceState().getDataNetworkType();
840         IState transitionState;
841         if (dataRat == TelephonyManager.NETWORK_TYPE_NR || isNrConnected()) {
842             transitionState = mNrConnectedState;
843             mPreviousState = isNrMmwave() ? STATE_CONNECTED_MMWAVE : STATE_CONNECTED;
844         } else if (isLte(dataRat) && isNrNotRestricted()) {
845             if (isPhysicalLinkActive()) {
846                 transitionState = mLteConnectedState;
847                 mPreviousState = STATE_NOT_RESTRICTED_RRC_CON;
848             } else {
849                 transitionState = mIdleState;
850                 mPreviousState = STATE_NOT_RESTRICTED_RRC_IDLE;
851             }
852         } else {
853             transitionState = mLegacyState;
854             mPreviousState = isNrRestricted() ? STATE_RESTRICTED : STATE_LEGACY;
855         }
856         if (!transitionState.equals(getCurrentState())) {
857             transitionTo(transitionState);
858         } else {
859             updateOverrideNetworkType();
860         }
861     }
862 
updateTimers()863     private void updateTimers() {
864         String currentState = getCurrentState().getName();
865 
866         if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()) {
867             // remove primary timer if device goes back to the original icon
868             if (DBG) {
869                 log("Remove primary timer since icon of primary state and current icon equal: "
870                         + mPrimaryTimerState);
871             }
872             removeMessages(EVENT_PRIMARY_TIMER_EXPIRED);
873             mIsPrimaryTimerActive = false;
874             mPrimaryTimerState = "";
875         }
876 
877         if (mIsSecondaryTimerActive && !mSecondaryTimerState.equals(currentState)) {
878             // remove secondary timer if devices is no longer in secondary timer state
879             if (DBG) {
880                 log("Remove secondary timer since current state (" +  currentState
881                         + ") is no longer secondary timer state (" + mSecondaryTimerState + ").");
882             }
883             removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
884             mIsSecondaryTimerActive = false;
885             mSecondaryTimerState = "";
886         }
887 
888         if (currentState.equals(STATE_CONNECTED_MMWAVE)) {
889             resetAllTimers();
890         }
891 
892         int rat = mPhone.getServiceState().getDataNetworkType();
893         if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) {
894             // Rat is 3G or 2G, and it doesn't need NR timer.
895             resetAllTimers();
896         }
897     }
898 
resetAllTimers()899     private void resetAllTimers() {
900         if (DBG) {
901             log("Remove all timers");
902         }
903         removeMessages(EVENT_PRIMARY_TIMER_EXPIRED);
904         removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
905         mIsPrimaryTimerActive = false;
906         mIsSecondaryTimerActive = false;
907         mPrimaryTimerState = "";
908         mSecondaryTimerState = "";
909     }
910 
911     /**
912      * Private class defining timer rules between states to prevent flickering. These rules are
913      * created in {@link #parseCarrierConfigs()} based on various carrier configs.
914      */
915     private class OverrideTimerRule {
916         /** The 5G state this timer rule applies for. See {@link #ALL_STATES}. */
917         final String mState;
918 
919         /**
920          * The override network type associated with this 5G state. This is the icon that will be
921          * displayed on the status bar. An override type of NONE will display the LTE value instead.
922          */
923         final int mOverrideType;
924 
925         /**
926          * A map of destination states and associated timers. If the 5G state changes from mState
927          * to the destination state, keep the override type until either the primary timer expires
928          * or mState is regained.
929          */
930         final Map<String, Integer> mPrimaryTimers;
931 
932         /**
933          * A map of secondary states and associated timers. After the primary timer expires, keep
934          * the override type until either the secondary timer expires or the device is no longer in
935          * the secondary state.
936          */
937         final Map<String, Integer> mSecondaryTimers;
938 
OverrideTimerRule(String state, int overrideType)939         OverrideTimerRule(String state, int overrideType) {
940             mState = state;
941             mOverrideType = overrideType;
942             mPrimaryTimers = new HashMap<>();
943             mSecondaryTimers = new HashMap<>();
944         }
945 
946         /**
947          * Add a primary timer.
948          * @param destination Transitions from mState to the destination state.
949          * @param duration How long to keep the override type after transition to destination state.
950          */
addTimer(String destination, int duration)951         public void addTimer(String destination, int duration) {
952             mPrimaryTimers.put(destination, duration);
953         }
954 
955         /**
956          * Add a secondary timer
957          * @param secondaryState Stays in secondaryState after primary timer expires.
958          * @param duration How long to keep the override type while in secondaryState.
959          */
addSecondaryTimer(String secondaryState, int duration)960         public void addSecondaryTimer(String secondaryState, int duration) {
961             mSecondaryTimers.put(secondaryState, duration);
962         }
963 
964         /**
965          * @return Primary timer duration from mState to destination state, or 0 if not defined.
966          */
getTimer(String destination)967         public int getTimer(String destination) {
968             Integer timer = mPrimaryTimers.get(destination);
969             timer = timer == null ? mPrimaryTimers.get(STATE_ANY) : timer;
970             return timer == null ? 0 : timer;
971         }
972 
973         /**
974          * @return Secondary timer duration for secondaryState, or 0 if not defined.
975          */
getSecondaryTimer(String secondaryState)976         public int getSecondaryTimer(String secondaryState) {
977             Integer secondaryTimer = mSecondaryTimers.get(secondaryState);
978             secondaryTimer = secondaryTimer == null
979                     ? mSecondaryTimers.get(STATE_ANY) : secondaryTimer;
980             return secondaryTimer == null ? 0 : secondaryTimer;
981         }
982 
983         @Override
toString()984         public String toString() {
985             return "{mState=" + mState
986                     + ", mOverrideType="
987                     + TelephonyDisplayInfo.overrideNetworkTypeToString(mOverrideType)
988                     + ", mPrimaryTimers=" + mPrimaryTimers
989                     + ", mSecondaryTimers=" + mSecondaryTimers + "}";
990         }
991     }
992 
isNrConnected()993     private boolean isNrConnected() {
994         return mPhone.getServiceState().getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
995     }
996 
isNrNotRestricted()997     private boolean isNrNotRestricted() {
998         return mPhone.getServiceState().getNrState()
999                 == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
1000     }
1001 
isNrRestricted()1002     private boolean isNrRestricted() {
1003         return mPhone.getServiceState().getNrState()
1004                 == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
1005     }
1006 
isNrMmwave()1007     private boolean isNrMmwave() {
1008         return mPhone.getServiceState().getNrFrequencyRange()
1009                 == ServiceState.FREQUENCY_RANGE_MMWAVE;
1010     }
1011 
isLte(int rat)1012     private boolean isLte(int rat) {
1013         return rat == TelephonyManager.NETWORK_TYPE_LTE
1014                 || rat == TelephonyManager.NETWORK_TYPE_LTE_CA;
1015     }
1016 
isPhysicalLinkActive()1017     private boolean isPhysicalLinkActive() {
1018         return mPhysicalLinkState == DcController.PHYSICAL_LINK_ACTIVE;
1019     }
1020 
getEventName(int event)1021     private String getEventName(int event) {
1022         try {
1023             return sEvents[event];
1024         } catch (ArrayIndexOutOfBoundsException e) {
1025             return "EVENT_NOT_DEFINED";
1026         }
1027     }
1028 
log(String s)1029     protected void log(String s) {
1030         Rlog.d(TAG, "[" + mPhone.getPhoneId() + "] " + s);
1031     }
1032 
loge(String s)1033     protected void loge(String s) {
1034         Rlog.e(TAG, "[" + mPhone.getPhoneId() + "] " + s);
1035     }
1036 
1037     @Override
toString()1038     public String toString() {
1039         return "mOverrideTimerRules=" + mOverrideTimerRules.toString()
1040                 + ", mLteEnhancedPattern=" + mLteEnhancedPattern
1041                 + ", mIsPhysicalChannelConfigOn=" + mIsPhysicalChannelConfigOn
1042                 + ", mIsPrimaryTimerActive=" + mIsPrimaryTimerActive
1043                 + ", mIsSecondaryTimerActive=" + mIsSecondaryTimerActive
1044                 + ", mPrimaryTimerState=" + mPrimaryTimerState
1045                 + ", mSecondaryTimerState=" + mSecondaryTimerState
1046                 + ", mPreviousState=" + mPreviousState;
1047     }
1048 
1049     @Override
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)1050     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
1051         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
1052         pw.print("NetworkTypeController: ");
1053         super.dump(fd, pw, args);
1054         pw.flush();
1055         pw.increaseIndent();
1056         pw.println("mSubId=" + mPhone.getSubId());
1057         pw.println("mOverrideTimerRules=" + mOverrideTimerRules.toString());
1058         pw.println("mLteEnhancedPattern=" + mLteEnhancedPattern);
1059         pw.println("mIsPhysicalChannelConfigOn=" + mIsPhysicalChannelConfigOn);
1060         pw.println("mIsPrimaryTimerActive=" + mIsPrimaryTimerActive);
1061         pw.println("mIsSecondaryTimerActive=" + mIsSecondaryTimerActive);
1062         pw.println("mPrimaryTimerState=" + mPrimaryTimerState);
1063         pw.println("mSecondaryTimerState=" + mSecondaryTimerState);
1064         pw.println("mPreviousState=" + mPreviousState);
1065         pw.decreaseIndent();
1066         pw.flush();
1067     }
1068 }
1069