1 /*
2  * Copyright (C) 2010 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.systemui.statusbar.policy;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
20 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
21 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
22 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
23 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
24 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
25 
26 import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
27 import static com.android.systemui.Dependency.BG_LOOPER_NAME;
28 
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.res.Configuration;
34 import android.content.res.Resources;
35 import android.net.ConnectivityManager;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.wifi.WifiManager;
39 import android.os.AsyncTask;
40 import android.os.Bundle;
41 import android.os.Handler;
42 import android.os.Looper;
43 import android.os.PersistableBundle;
44 import android.provider.Settings;
45 import android.telephony.CarrierConfigManager;
46 import android.telephony.PhoneStateListener;
47 import android.telephony.ServiceState;
48 import android.telephony.SignalStrength;
49 import android.telephony.SubscriptionInfo;
50 import android.telephony.SubscriptionManager;
51 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
52 import android.telephony.TelephonyManager;
53 import android.text.TextUtils;
54 import android.util.Log;
55 import android.util.MathUtils;
56 import android.util.SparseArray;
57 
58 import com.android.internal.annotations.GuardedBy;
59 import com.android.internal.annotations.VisibleForTesting;
60 import com.android.internal.telephony.PhoneConstants;
61 import com.android.internal.telephony.TelephonyIntents;
62 import com.android.settingslib.net.DataUsageController;
63 import com.android.systemui.ConfigurationChangedReceiver;
64 import com.android.systemui.DemoMode;
65 import com.android.systemui.Dumpable;
66 import com.android.systemui.R;
67 import com.android.systemui.settings.CurrentUserTracker;
68 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
69 import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
70 
71 import java.io.FileDescriptor;
72 import java.io.PrintWriter;
73 import java.util.ArrayList;
74 import java.util.BitSet;
75 import java.util.Collections;
76 import java.util.Comparator;
77 import java.util.HashMap;
78 import java.util.List;
79 import java.util.Locale;
80 import java.util.Map;
81 
82 import javax.inject.Inject;
83 import javax.inject.Named;
84 import javax.inject.Singleton;
85 
86 /** Platform implementation of the network controller. **/
87 @Singleton
88 public class NetworkControllerImpl extends BroadcastReceiver
89         implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
90         ConfigurationChangedReceiver, Dumpable {
91     // debug
92     static final String TAG = "NetworkController";
93     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
94     // additional diagnostics, but not logspew
95     static final boolean CHATTY =  Log.isLoggable(TAG + "Chat", Log.DEBUG);
96 
97     private static final int EMERGENCY_NO_CONTROLLERS = 0;
98     private static final int EMERGENCY_FIRST_CONTROLLER = 100;
99     private static final int EMERGENCY_VOICE_CONTROLLER = 200;
100     private static final int EMERGENCY_NO_SUB = 300;
101     private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
102 
103     private final Context mContext;
104     private final TelephonyManager mPhone;
105     private final WifiManager mWifiManager;
106     private final ConnectivityManager mConnectivityManager;
107     private final SubscriptionManager mSubscriptionManager;
108     private final boolean mHasMobileDataFeature;
109     private final SubscriptionDefaults mSubDefaults;
110     private final DataSaverController mDataSaverController;
111     private final CurrentUserTracker mUserTracker;
112     private final Object mLock = new Object();
113     private Config mConfig;
114 
115     private PhoneStateListener mPhoneStateListener;
116     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
117 
118     // Subcontrollers.
119     @VisibleForTesting
120     final WifiSignalController mWifiSignalController;
121 
122     @VisibleForTesting
123     final EthernetSignalController mEthernetSignalController;
124 
125     @VisibleForTesting
126     final SparseArray<MobileSignalController> mMobileSignalControllers = new SparseArray<>();
127     // When no SIMs are around at setup, and one is added later, it seems to default to the first
128     // SIM for most actions.  This may be null if there aren't any SIMs around.
129     private MobileSignalController mDefaultSignalController;
130     private final AccessPointControllerImpl mAccessPoints;
131     private final DataUsageController mDataUsageController;
132 
133     private boolean mInetCondition; // Used for Logging and demo.
134 
135     // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are
136     // connected and validated, respectively.
137     private final BitSet mConnectedTransports = new BitSet();
138     private final BitSet mValidatedTransports = new BitSet();
139 
140     // States that don't belong to a subcontroller.
141     private boolean mAirplaneMode = false;
142     private boolean mHasNoSubs;
143     private Locale mLocale = null;
144     // This list holds our ordering.
145     private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
146 
147     @VisibleForTesting
148     boolean mListening;
149 
150     // The current user ID.
151     private int mCurrentUserId;
152 
153     private OnSubscriptionsChangedListener mSubscriptionListener;
154 
155     // Handler that all broadcasts are received on.
156     private final Handler mReceiverHandler;
157     // Handler that all callbacks are made on.
158     private final CallbackHandler mCallbackHandler;
159 
160     private int mEmergencySource;
161     private boolean mIsEmergency;
162 
163     @VisibleForTesting
164     ServiceState mLastServiceState;
165     private boolean mUserSetup;
166     private boolean mSimDetected;
167 
168     /**
169      * Construct this controller object and register for updates.
170      */
171     @Inject
NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, DeviceProvisionedController deviceProvisionedController)172     public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
173             DeviceProvisionedController deviceProvisionedController) {
174         this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
175                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
176                 (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
177                 SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
178                 new CallbackHandler(),
179                 new AccessPointControllerImpl(context),
180                 new DataUsageController(context),
181                 new SubscriptionDefaults(),
182                 deviceProvisionedController);
183         mReceiverHandler.post(mRegisterListeners);
184     }
185 
186     @VisibleForTesting
NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, TelephonyManager telephonyManager, WifiManager wifiManager, SubscriptionManager subManager, Config config, Looper bgLooper, CallbackHandler callbackHandler, AccessPointControllerImpl accessPointController, DataUsageController dataUsageController, SubscriptionDefaults defaultsHandler, DeviceProvisionedController deviceProvisionedController)187     NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
188             TelephonyManager telephonyManager, WifiManager wifiManager,
189             SubscriptionManager subManager, Config config, Looper bgLooper,
190             CallbackHandler callbackHandler,
191             AccessPointControllerImpl accessPointController,
192             DataUsageController dataUsageController,
193             SubscriptionDefaults defaultsHandler,
194             DeviceProvisionedController deviceProvisionedController) {
195         mContext = context;
196         mConfig = config;
197         mReceiverHandler = new Handler(bgLooper);
198         mCallbackHandler = callbackHandler;
199         mDataSaverController = new DataSaverControllerImpl(context);
200 
201         mSubscriptionManager = subManager;
202         mSubDefaults = defaultsHandler;
203         mConnectivityManager = connectivityManager;
204         mHasMobileDataFeature =
205                 mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
206 
207         // telephony
208         mPhone = telephonyManager;
209 
210         // wifi
211         mWifiManager = wifiManager;
212 
213         mLocale = mContext.getResources().getConfiguration().locale;
214         mAccessPoints = accessPointController;
215         mDataUsageController = dataUsageController;
216         mDataUsageController.setNetworkController(this);
217         // TODO: Find a way to move this into DataUsageController.
218         mDataUsageController.setCallback(new DataUsageController.Callback() {
219             @Override
220             public void onMobileDataEnabled(boolean enabled) {
221                 mCallbackHandler.setMobileDataEnabled(enabled);
222             }
223         });
224         mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
225                 mCallbackHandler, this, mWifiManager);
226 
227         mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
228 
229         // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
230         updateAirplaneMode(true /* force callback */);
231         mUserTracker = new CurrentUserTracker(mContext) {
232             @Override
233             public void onUserSwitched(int newUserId) {
234                 NetworkControllerImpl.this.onUserSwitched(newUserId);
235             }
236         };
237         mUserTracker.startTracking();
238         deviceProvisionedController.addCallback(new DeviceProvisionedListener() {
239             @Override
240             public void onUserSetupChanged() {
241                 setUserSetupComplete(deviceProvisionedController.isUserSetup(
242                         deviceProvisionedController.getCurrentUser()));
243             }
244         });
245 
246         ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
247             private Network mLastNetwork;
248             private NetworkCapabilities mLastNetworkCapabilities;
249 
250             @Override
251             public void onCapabilitiesChanged(
252                 Network network, NetworkCapabilities networkCapabilities) {
253                 boolean lastValidated = (mLastNetworkCapabilities != null) &&
254                     mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
255                 boolean validated =
256                     networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
257 
258                 // This callback is invoked a lot (i.e. when RSSI changes), so avoid updating
259                 // icons when connectivity state has remained the same.
260                 if (network.equals(mLastNetwork) &&
261                     networkCapabilities.equalsTransportTypes(mLastNetworkCapabilities) &&
262                     validated == lastValidated) {
263                     return;
264                 }
265                 mLastNetwork = network;
266                 mLastNetworkCapabilities = networkCapabilities;
267                 updateConnectivity();
268             }
269         };
270         // Even though this callback runs on the receiver handler thread which also processes the
271         // CONNECTIVITY_ACTION broadcasts, the broadcast and callback might come in at different
272         // times. This is safe since updateConnectivity() builds the list of transports from
273         // scratch.
274         // TODO: Move off of the deprecated CONNECTIVITY_ACTION broadcast and rely on callbacks
275         // exclusively for status bar icons.
276         mConnectivityManager.registerDefaultNetworkCallback(callback, mReceiverHandler);
277         // Register the listener on our bg looper
278         mPhoneStateListener = new PhoneStateListener(bgLooper) {
279             @Override
280             public void onActiveDataSubscriptionIdChanged(int subId) {
281                 mActiveMobileDataSubscription = subId;
282                 doUpdateMobileControllers();
283             }
284         };
285     }
286 
getDataSaverController()287     public DataSaverController getDataSaverController() {
288         return mDataSaverController;
289     }
290 
registerListeners()291     private void registerListeners() {
292         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
293             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
294             mobileSignalController.registerListener();
295         }
296         if (mSubscriptionListener == null) {
297             mSubscriptionListener = new SubListener();
298         }
299         mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
300         mPhone.listen(mPhoneStateListener, LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
301 
302         // broadcasts
303         IntentFilter filter = new IntentFilter();
304         filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
305         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
306         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
307         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
308         filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
309         filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
310         filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
311         filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
312         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
313         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
314         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
315         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
316         mContext.registerReceiver(this, filter, null, mReceiverHandler);
317         mListening = true;
318 
319         updateMobileControllers();
320     }
321 
unregisterListeners()322     private void unregisterListeners() {
323         mListening = false;
324         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
325             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
326             mobileSignalController.unregisterListener();
327         }
328         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
329         mContext.unregisterReceiver(this);
330     }
331 
getConnectedWifiLevel()332     public int getConnectedWifiLevel() {
333         return mWifiSignalController.getState().level;
334     }
335 
336     @Override
getAccessPointController()337     public AccessPointController getAccessPointController() {
338         return mAccessPoints;
339     }
340 
341     @Override
getMobileDataController()342     public DataUsageController getMobileDataController() {
343         return mDataUsageController;
344     }
345 
addEmergencyListener(EmergencyListener listener)346     public void addEmergencyListener(EmergencyListener listener) {
347         mCallbackHandler.setListening(listener, true);
348         mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly());
349     }
350 
removeEmergencyListener(EmergencyListener listener)351     public void removeEmergencyListener(EmergencyListener listener) {
352         mCallbackHandler.setListening(listener, false);
353     }
354 
hasMobileDataFeature()355     public boolean hasMobileDataFeature() {
356         return mHasMobileDataFeature;
357     }
358 
hasVoiceCallingFeature()359     public boolean hasVoiceCallingFeature() {
360         return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
361     }
362 
getDataController()363     private MobileSignalController getDataController() {
364         int dataSubId = mSubDefaults.getDefaultDataSubId();
365         if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) {
366             if (DEBUG) Log.e(TAG, "No data sim selected");
367             return mDefaultSignalController;
368         }
369         if (mMobileSignalControllers.indexOfKey(dataSubId) >= 0) {
370             return mMobileSignalControllers.get(dataSubId);
371         }
372         if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
373         return mDefaultSignalController;
374     }
375 
376     @Override
getMobileDataNetworkName()377     public String getMobileDataNetworkName() {
378         MobileSignalController controller = getDataController();
379         return controller != null ? controller.getState().networkNameData : "";
380     }
381 
382     @Override
getNumberSubscriptions()383     public int getNumberSubscriptions() {
384         return mMobileSignalControllers.size();
385     }
386 
isEmergencyOnly()387     public boolean isEmergencyOnly() {
388         if (mMobileSignalControllers.size() == 0) {
389             // When there are no active subscriptions, determine emengency state from last
390             // broadcast.
391             mEmergencySource = EMERGENCY_NO_CONTROLLERS;
392             return mLastServiceState != null && mLastServiceState.isEmergencyOnly();
393         }
394         int voiceSubId = mSubDefaults.getDefaultVoiceSubId();
395         if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
396             for (int i = 0; i < mMobileSignalControllers.size(); i++) {
397                 MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
398                 if (!mobileSignalController.getState().isEmergency) {
399                     mEmergencySource = EMERGENCY_FIRST_CONTROLLER
400                             + mobileSignalController.mSubscriptionInfo.getSubscriptionId();
401                     if (DEBUG) Log.d(TAG, "Found emergency " + mobileSignalController.mTag);
402                     return false;
403                 }
404             }
405         }
406         if (mMobileSignalControllers.indexOfKey(voiceSubId) >= 0) {
407             mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId;
408             if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId);
409             return mMobileSignalControllers.get(voiceSubId).getState().isEmergency;
410         }
411         // If we have the wrong subId but there is only one sim anyway, assume it should be the
412         // default.
413         if (mMobileSignalControllers.size() == 1) {
414             mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER
415                     + mMobileSignalControllers.keyAt(0);
416             if (DEBUG) Log.d(TAG, "Getting assumed emergency from "
417                     + mMobileSignalControllers.keyAt(0));
418             return mMobileSignalControllers.valueAt(0).getState().isEmergency;
419         }
420         if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
421         mEmergencySource = EMERGENCY_NO_SUB + voiceSubId;
422         // Something is wrong, better assume we can't make calls...
423         return true;
424     }
425 
426     /**
427      * Emergency status may have changed (triggered by MobileSignalController),
428      * so we should recheck and send out the state to listeners.
429      */
recalculateEmergency()430     void recalculateEmergency() {
431         mIsEmergency = isEmergencyOnly();
432         mCallbackHandler.setEmergencyCallsOnly(mIsEmergency);
433     }
434 
addCallback(SignalCallback cb)435     public void addCallback(SignalCallback cb) {
436         cb.setSubs(mCurrentSubscriptions);
437         cb.setIsAirplaneMode(new IconState(mAirplaneMode,
438                 TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
439         cb.setNoSims(mHasNoSubs, mSimDetected);
440         mWifiSignalController.notifyListeners(cb);
441         mEthernetSignalController.notifyListeners(cb);
442         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
443             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
444             mobileSignalController.notifyListeners(cb);
445         }
446         mCallbackHandler.setListening(cb, true);
447     }
448 
449     @Override
removeCallback(SignalCallback cb)450     public void removeCallback(SignalCallback cb) {
451         mCallbackHandler.setListening(cb, false);
452     }
453 
454     @Override
setWifiEnabled(final boolean enabled)455     public void setWifiEnabled(final boolean enabled) {
456         new AsyncTask<Void, Void, Void>() {
457             @Override
458             protected Void doInBackground(Void... args) {
459                 mWifiManager.setWifiEnabled(enabled);
460                 return null;
461             }
462         }.execute();
463     }
464 
onUserSwitched(int newUserId)465     private void onUserSwitched(int newUserId) {
466         mCurrentUserId = newUserId;
467         mAccessPoints.onUserSwitched(newUserId);
468         updateConnectivity();
469     }
470 
471     @Override
onReceive(Context context, Intent intent)472     public void onReceive(Context context, Intent intent) {
473         if (CHATTY) {
474             Log.d(TAG, "onReceive: intent=" + intent);
475         }
476         final String action = intent.getAction();
477         switch (action) {
478             case ConnectivityManager.CONNECTIVITY_ACTION:
479             case ConnectivityManager.INET_CONDITION_ACTION:
480                 updateConnectivity();
481                 break;
482             case Intent.ACTION_AIRPLANE_MODE_CHANGED:
483                 refreshLocale();
484                 updateAirplaneMode(false);
485                 break;
486             case TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
487                 // We are using different subs now, we might be able to make calls.
488                 recalculateEmergency();
489                 break;
490             case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
491                 // Notify every MobileSignalController so they can know whether they are the
492                 // data sim or not.
493                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
494                     MobileSignalController controller = mMobileSignalControllers.valueAt(i);
495                     controller.handleBroadcast(intent);
496                 }
497                 mConfig = Config.readConfig(mContext);
498                 mReceiverHandler.post(this::handleConfigurationChanged);
499                 break;
500             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
501                 // Avoid rebroadcast because SysUI is direct boot aware.
502                 if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
503                     break;
504                 }
505                 // Might have different subscriptions now.
506                 updateMobileControllers();
507                 break;
508             case TelephonyIntents.ACTION_SERVICE_STATE_CHANGED:
509                 mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
510                 if (mMobileSignalControllers.size() == 0) {
511                     // If none of the subscriptions are active, we might need to recalculate
512                     // emergency state.
513                     recalculateEmergency();
514                 }
515                 break;
516             case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
517                 mConfig = Config.readConfig(mContext);
518                 mReceiverHandler.post(this::handleConfigurationChanged);
519                 break;
520             default:
521                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
522                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
523                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
524                     if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
525                         mMobileSignalControllers.get(subId).handleBroadcast(intent);
526                     } else {
527                         // Can't find this subscription...  We must be out of date.
528                         updateMobileControllers();
529                     }
530                 } else {
531                     // No sub id, must be for the wifi.
532                     mWifiSignalController.handleBroadcast(intent);
533                 }
534                 break;
535         }
536     }
537 
onConfigurationChanged(Configuration newConfig)538     public void onConfigurationChanged(Configuration newConfig) {
539         mConfig = Config.readConfig(mContext);
540         mReceiverHandler.post(new Runnable() {
541             @Override
542             public void run() {
543                 handleConfigurationChanged();
544             }
545         });
546     }
547 
548     @VisibleForTesting
handleConfigurationChanged()549     void handleConfigurationChanged() {
550         updateMobileControllers();
551         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
552             MobileSignalController controller = mMobileSignalControllers.valueAt(i);
553             controller.setConfiguration(mConfig);
554         }
555         refreshLocale();
556     }
557 
updateMobileControllers()558     private void updateMobileControllers() {
559         if (!mListening) {
560             return;
561         }
562         doUpdateMobileControllers();
563     }
564 
filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions)565     private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
566         if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
567             SubscriptionInfo info1 = subscriptions.get(0);
568             SubscriptionInfo info2 = subscriptions.get(1);
569             if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
570                 // If both subscriptions are primary, show both.
571                 if (!info1.isOpportunistic() && !info2.isOpportunistic()) return;
572 
573                 // If carrier required, always show signal bar of primary subscription.
574                 // Otherwise, show whichever subscription is currently active for Internet.
575                 boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
576                         .getBoolean(CarrierConfigManager
577                         .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
578                 if (alwaysShowPrimary) {
579                     subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
580                 } else {
581                     subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
582                             ? info2 : info1);
583                 }
584             }
585         }
586     }
587 
588     @VisibleForTesting
doUpdateMobileControllers()589     void doUpdateMobileControllers() {
590         List<SubscriptionInfo> subscriptions = mSubscriptionManager
591                 .getActiveSubscriptionInfoList(false);
592         if (subscriptions == null) {
593             subscriptions = Collections.emptyList();
594         }
595 
596         filterMobileSubscriptionInSameGroup(subscriptions);
597 
598         // If there have been no relevant changes to any of the subscriptions, we can leave as is.
599         if (hasCorrectMobileControllers(subscriptions)) {
600             // Even if the controllers are correct, make sure we have the right no sims state.
601             // Such as on boot, don't need any controllers, because there are no sims,
602             // but we still need to update the no sim state.
603             updateNoSims();
604             return;
605         }
606         synchronized (mLock) {
607             setCurrentSubscriptionsLocked(subscriptions);
608         }
609         updateNoSims();
610         recalculateEmergency();
611     }
612 
613     @VisibleForTesting
updateNoSims()614     protected void updateNoSims() {
615         boolean hasNoSubs = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
616         boolean simDetected = hasAnySim();
617         if (hasNoSubs != mHasNoSubs || simDetected != mSimDetected) {
618             mHasNoSubs = hasNoSubs;
619             mSimDetected = simDetected;
620             mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
621         }
622     }
623 
hasAnySim()624     private boolean hasAnySim() {
625         int simCount = mPhone.getSimCount();
626         for (int i = 0; i < simCount; i++) {
627             int state = mPhone.getSimState(i);
628             if (state != TelephonyManager.SIM_STATE_ABSENT
629                     && state != TelephonyManager.SIM_STATE_UNKNOWN) {
630                 return true;
631             }
632         }
633         return false;
634     }
635 
636     @GuardedBy("mLock")
637     @VisibleForTesting
setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions)638     public void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) {
639         Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
640             @Override
641             public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
642                 return lhs.getSimSlotIndex() == rhs.getSimSlotIndex()
643                         ? lhs.getSubscriptionId() - rhs.getSubscriptionId()
644                         : lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
645             }
646         });
647         mCurrentSubscriptions = subscriptions;
648 
649         SparseArray<MobileSignalController> cachedControllers =
650                 new SparseArray<MobileSignalController>();
651         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
652             cachedControllers.put(mMobileSignalControllers.keyAt(i),
653                     mMobileSignalControllers.valueAt(i));
654         }
655         mMobileSignalControllers.clear();
656         final int num = subscriptions.size();
657         for (int i = 0; i < num; i++) {
658             int subId = subscriptions.get(i).getSubscriptionId();
659             // If we have a copy of this controller already reuse it, otherwise make a new one.
660             if (cachedControllers.indexOfKey(subId) >= 0) {
661                 mMobileSignalControllers.put(subId, cachedControllers.get(subId));
662                 cachedControllers.remove(subId);
663             } else {
664                 MobileSignalController controller = new MobileSignalController(mContext, mConfig,
665                         mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
666                         mCallbackHandler, this, subscriptions.get(i),
667                         mSubDefaults, mReceiverHandler.getLooper());
668                 controller.setUserSetupComplete(mUserSetup);
669                 mMobileSignalControllers.put(subId, controller);
670                 if (subscriptions.get(i).getSimSlotIndex() == 0) {
671                     mDefaultSignalController = controller;
672                 }
673                 if (mListening) {
674                     controller.registerListener();
675                 }
676             }
677         }
678         if (mListening) {
679             for (int i = 0; i < cachedControllers.size(); i++) {
680                 int key = cachedControllers.keyAt(i);
681                 if (cachedControllers.get(key) == mDefaultSignalController) {
682                     mDefaultSignalController = null;
683                 }
684                 cachedControllers.get(key).unregisterListener();
685             }
686         }
687         mCallbackHandler.setSubs(subscriptions);
688         notifyAllListeners();
689 
690         // There may be new MobileSignalControllers around, make sure they get the current
691         // inet condition and airplane mode.
692         pushConnectivityToSignals();
693         updateAirplaneMode(true /* force */);
694     }
695 
setUserSetupComplete(final boolean userSetup)696     private void setUserSetupComplete(final boolean userSetup) {
697         mReceiverHandler.post(() -> handleSetUserSetupComplete(userSetup));
698     }
699 
handleSetUserSetupComplete(boolean userSetup)700     private void handleSetUserSetupComplete(boolean userSetup) {
701         mUserSetup = userSetup;
702         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
703             MobileSignalController controller = mMobileSignalControllers.valueAt(i);
704             controller.setUserSetupComplete(mUserSetup);
705         }
706     }
707 
708     @VisibleForTesting
hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions)709     boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
710         if (allSubscriptions.size() != mMobileSignalControllers.size()) {
711             return false;
712         }
713         for (SubscriptionInfo info : allSubscriptions) {
714             if (mMobileSignalControllers.indexOfKey(info.getSubscriptionId()) < 0) {
715                 return false;
716             }
717         }
718         return true;
719     }
720 
updateAirplaneMode(boolean force)721     private void updateAirplaneMode(boolean force) {
722         boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
723                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
724         if (airplaneMode != mAirplaneMode || force) {
725             mAirplaneMode = airplaneMode;
726             for (int i = 0; i < mMobileSignalControllers.size(); i++) {
727                 MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
728                 mobileSignalController.setAirplaneMode(mAirplaneMode);
729             }
730             notifyListeners();
731         }
732     }
733 
refreshLocale()734     private void refreshLocale() {
735         Locale current = mContext.getResources().getConfiguration().locale;
736         if (!current.equals(mLocale)) {
737             mLocale = current;
738             mWifiSignalController.refreshLocale();
739             notifyAllListeners();
740         }
741     }
742 
743     /**
744      * Forces update of all callbacks on both SignalClusters and
745      * NetworkSignalChangedCallbacks.
746      */
notifyAllListeners()747     private void notifyAllListeners() {
748         notifyListeners();
749         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
750             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
751             mobileSignalController.notifyListeners();
752         }
753         mWifiSignalController.notifyListeners();
754         mEthernetSignalController.notifyListeners();
755     }
756 
757     /**
758      * Notifies listeners of changes in state of to the NetworkController, but
759      * does not notify for any info on SignalControllers, for that call
760      * notifyAllListeners.
761      */
notifyListeners()762     private void notifyListeners() {
763         mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
764                 TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
765         mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
766     }
767 
768     /**
769      * Update the Inet conditions and what network we are connected to.
770      */
updateConnectivity()771     private void updateConnectivity() {
772         mConnectedTransports.clear();
773         mValidatedTransports.clear();
774         for (NetworkCapabilities nc :
775                 mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
776             for (int transportType : nc.getTransportTypes()) {
777                 mConnectedTransports.set(transportType);
778                 if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
779                     mValidatedTransports.set(transportType);
780                 }
781             }
782         }
783 
784         if (CHATTY) {
785             Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
786             Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
787         }
788 
789         mInetCondition = !mValidatedTransports.isEmpty();
790 
791         pushConnectivityToSignals();
792     }
793 
794     /**
795      * Pushes the current connectivity state to all SignalControllers.
796      */
pushConnectivityToSignals()797     private void pushConnectivityToSignals() {
798         // We want to update all the icons, all at once, for any condition change
799         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
800             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
801             mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
802         }
803         mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
804         mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
805     }
806 
dump(FileDescriptor fd, PrintWriter pw, String[] args)807     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
808         pw.println("NetworkController state:");
809 
810         pw.println("  - telephony ------");
811         pw.print("  hasVoiceCallingFeature()=");
812         pw.println(hasVoiceCallingFeature());
813 
814         pw.println("  - connectivity ------");
815         pw.print("  mConnectedTransports=");
816         pw.println(mConnectedTransports);
817         pw.print("  mValidatedTransports=");
818         pw.println(mValidatedTransports);
819         pw.print("  mInetCondition=");
820         pw.println(mInetCondition);
821         pw.print("  mAirplaneMode=");
822         pw.println(mAirplaneMode);
823         pw.print("  mLocale=");
824         pw.println(mLocale);
825         pw.print("  mLastServiceState=");
826         pw.println(mLastServiceState);
827         pw.print("  mIsEmergency=");
828         pw.println(mIsEmergency);
829         pw.print("  mEmergencySource=");
830         pw.println(emergencyToString(mEmergencySource));
831 
832         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
833             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
834             mobileSignalController.dump(pw);
835         }
836         mWifiSignalController.dump(pw);
837 
838         mEthernetSignalController.dump(pw);
839 
840         mAccessPoints.dump(pw);
841     }
842 
emergencyToString(int emergencySource)843     private static final String emergencyToString(int emergencySource) {
844         if (emergencySource > EMERGENCY_NO_SUB) {
845             return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER)
846                     + ")";
847         } else if (emergencySource > EMERGENCY_NO_SUB) {
848             return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")";
849         } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) {
850             return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")";
851         } else if (emergencySource > EMERGENCY_FIRST_CONTROLLER) {
852             return "FIRST_CONTROLLER(" + (emergencySource - EMERGENCY_FIRST_CONTROLLER) + ")";
853         } else if (emergencySource == EMERGENCY_NO_CONTROLLERS) {
854             return "NO_CONTROLLERS";
855         }
856         return "UNKNOWN_SOURCE";
857     }
858 
859     private boolean mDemoMode;
860     private boolean mDemoInetCondition;
861     private WifiSignalController.WifiState mDemoWifiState;
862 
863     @Override
dispatchDemoCommand(String command, Bundle args)864     public void dispatchDemoCommand(String command, Bundle args) {
865         if (!mDemoMode && command.equals(COMMAND_ENTER)) {
866             if (DEBUG) Log.d(TAG, "Entering demo mode");
867             unregisterListeners();
868             mDemoMode = true;
869             mDemoInetCondition = mInetCondition;
870             mDemoWifiState = mWifiSignalController.getState();
871             mDemoWifiState.ssid = "DemoMode";
872         } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
873             if (DEBUG) Log.d(TAG, "Exiting demo mode");
874             mDemoMode = false;
875             // Update what MobileSignalControllers, because they may change
876             // to set the number of sim slots.
877             updateMobileControllers();
878             for (int i = 0; i < mMobileSignalControllers.size(); i++) {
879                 MobileSignalController controller = mMobileSignalControllers.valueAt(i);
880                 controller.resetLastState();
881             }
882             mWifiSignalController.resetLastState();
883             mReceiverHandler.post(mRegisterListeners);
884             notifyAllListeners();
885         } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
886             String airplane = args.getString("airplane");
887             if (airplane != null) {
888                 boolean show = airplane.equals("show");
889                 mCallbackHandler.setIsAirplaneMode(new IconState(show,
890                         TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode,
891                         mContext));
892             }
893             String fully = args.getString("fully");
894             if (fully != null) {
895                 mDemoInetCondition = Boolean.parseBoolean(fully);
896                 BitSet connected = new BitSet();
897 
898                 if (mDemoInetCondition) {
899                     connected.set(mWifiSignalController.mTransportType);
900                 }
901                 mWifiSignalController.updateConnectivity(connected, connected);
902                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
903                     MobileSignalController controller = mMobileSignalControllers.valueAt(i);
904                     if (mDemoInetCondition) {
905                         connected.set(controller.mTransportType);
906                     }
907                     controller.updateConnectivity(connected, connected);
908                 }
909             }
910             String wifi = args.getString("wifi");
911             if (wifi != null) {
912                 boolean show = wifi.equals("show");
913                 String level = args.getString("level");
914                 if (level != null) {
915                     mDemoWifiState.level = level.equals("null") ? -1
916                             : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
917                     mDemoWifiState.connected = mDemoWifiState.level >= 0;
918                 }
919                 String activity = args.getString("activity");
920                 if (activity != null) {
921                     switch (activity) {
922                         case "inout":
923                             mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT);
924                             break;
925                         case "in":
926                             mWifiSignalController.setActivity(DATA_ACTIVITY_IN);
927                             break;
928                         case "out":
929                             mWifiSignalController.setActivity(DATA_ACTIVITY_OUT);
930                             break;
931                         default:
932                             mWifiSignalController.setActivity(DATA_ACTIVITY_NONE);
933                             break;
934                     }
935                 } else {
936                     mWifiSignalController.setActivity(DATA_ACTIVITY_NONE);
937                 }
938                 String ssid = args.getString("ssid");
939                 if (ssid != null) {
940                     mDemoWifiState.ssid = ssid;
941                 }
942                 mDemoWifiState.enabled = show;
943                 mWifiSignalController.notifyListeners();
944             }
945             String sims = args.getString("sims");
946             if (sims != null) {
947                 int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8);
948                 List<SubscriptionInfo> subs = new ArrayList<>();
949                 if (num != mMobileSignalControllers.size()) {
950                     mMobileSignalControllers.clear();
951                     int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
952                     for (int i = start /* get out of normal index range */; i < start + num; i++) {
953                         subs.add(addSignalController(i, i));
954                     }
955                     mCallbackHandler.setSubs(subs);
956                     for (int i = 0; i < mMobileSignalControllers.size(); i++) {
957                         int key = mMobileSignalControllers.keyAt(i);
958                         MobileSignalController controller = mMobileSignalControllers.get(key);
959                         controller.notifyListeners();
960                     }
961                 }
962             }
963             String nosim = args.getString("nosim");
964             if (nosim != null) {
965                 mHasNoSubs = nosim.equals("show");
966                 mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
967             }
968             String mobile = args.getString("mobile");
969             if (mobile != null) {
970                 boolean show = mobile.equals("show");
971                 String datatype = args.getString("datatype");
972                 String slotString = args.getString("slot");
973                 int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
974                 slot = MathUtils.constrain(slot, 0, 8);
975                 // Ensure we have enough sim slots
976                 List<SubscriptionInfo> subs = new ArrayList<>();
977                 while (mMobileSignalControllers.size() <= slot) {
978                     int nextSlot = mMobileSignalControllers.size();
979                     subs.add(addSignalController(nextSlot, nextSlot));
980                 }
981                 if (!subs.isEmpty()) {
982                     mCallbackHandler.setSubs(subs);
983                 }
984                 // Hack to index linearly for easy use.
985                 MobileSignalController controller = mMobileSignalControllers.valueAt(slot);
986                 controller.getState().dataSim = datatype != null;
987                 controller.getState().isDefault = datatype != null;
988                 controller.getState().dataConnected = datatype != null;
989                 if (datatype != null) {
990                     controller.getState().iconGroup =
991                             datatype.equals("1x") ? TelephonyIcons.ONE_X :
992                             datatype.equals("3g") ? TelephonyIcons.THREE_G :
993                             datatype.equals("4g") ? TelephonyIcons.FOUR_G :
994                             datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS :
995                             datatype.equals("e") ? TelephonyIcons.E :
996                             datatype.equals("g") ? TelephonyIcons.G :
997                             datatype.equals("h") ? TelephonyIcons.H :
998                             datatype.equals("h+") ? TelephonyIcons.H_PLUS :
999                             datatype.equals("lte") ? TelephonyIcons.LTE :
1000                             datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
1001                             datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED :
1002                             datatype.equals("not") ? TelephonyIcons.NOT_DEFAULT_DATA :
1003                             TelephonyIcons.UNKNOWN;
1004                 }
1005                 if (args.containsKey("roam")) {
1006                     controller.getState().roaming = "show".equals(args.getString("roam"));
1007                 }
1008                 String level = args.getString("level");
1009                 if (level != null) {
1010                     controller.getState().level = level.equals("null") ? -1
1011                             : Math.min(Integer.parseInt(level),
1012                                     SignalStrength.NUM_SIGNAL_STRENGTH_BINS);
1013                     controller.getState().connected = controller.getState().level >= 0;
1014                 }
1015                 if (args.containsKey("inflate")) {
1016                     for (int i = 0; i < mMobileSignalControllers.size(); i++) {
1017                         mMobileSignalControllers.valueAt(i).mInflateSignalStrengths =
1018                                 "true".equals(args.getString("inflate"));
1019                     }
1020                 }
1021                 String activity = args.getString("activity");
1022                 if (activity != null) {
1023                     controller.getState().dataConnected = true;
1024                     switch (activity) {
1025                         case "inout":
1026                             controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
1027                             break;
1028                         case "in":
1029                             controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN);
1030                             break;
1031                         case "out":
1032                             controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT);
1033                             break;
1034                         default:
1035                             controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE);
1036                             break;
1037                     }
1038                 } else {
1039                     controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE);
1040                 }
1041                 controller.getState().enabled = show;
1042                 controller.notifyListeners();
1043             }
1044             String carrierNetworkChange = args.getString("carriernetworkchange");
1045             if (carrierNetworkChange != null) {
1046                 boolean show = carrierNetworkChange.equals("show");
1047                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
1048                     MobileSignalController controller = mMobileSignalControllers.valueAt(i);
1049                     controller.setCarrierNetworkChangeMode(show);
1050                 }
1051             }
1052         }
1053     }
1054 
addSignalController(int id, int simSlotIndex)1055     private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
1056         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
1057                 null, null, null, "", false, null, null);
1058         MobileSignalController controller = new MobileSignalController(mContext,
1059                 mConfig, mHasMobileDataFeature,
1060                 mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this, info,
1061                 mSubDefaults, mReceiverHandler.getLooper());
1062         mMobileSignalControllers.put(id, controller);
1063         controller.getState().userSetup = true;
1064         return info;
1065     }
1066 
hasEmergencyCryptKeeperText()1067     public boolean hasEmergencyCryptKeeperText() {
1068         return EncryptionHelper.IS_DATA_ENCRYPTED;
1069     }
1070 
isRadioOn()1071     public boolean isRadioOn() {
1072         return !mAirplaneMode;
1073     }
1074 
1075     private class SubListener extends OnSubscriptionsChangedListener {
1076         @Override
onSubscriptionsChanged()1077         public void onSubscriptionsChanged() {
1078             updateMobileControllers();
1079         }
1080     }
1081 
1082     /**
1083      * Used to register listeners from the BG Looper, this way the PhoneStateListeners that
1084      * get created will also run on the BG Looper.
1085      */
1086     private final Runnable mRegisterListeners = new Runnable() {
1087         @Override
1088         public void run() {
1089             registerListeners();
1090         }
1091     };
1092 
1093     public static class SubscriptionDefaults {
getDefaultVoiceSubId()1094         public int getDefaultVoiceSubId() {
1095             return SubscriptionManager.getDefaultVoiceSubscriptionId();
1096         }
1097 
getDefaultDataSubId()1098         public int getDefaultDataSubId() {
1099             return SubscriptionManager.getDefaultDataSubscriptionId();
1100         }
1101     }
1102 
1103     @VisibleForTesting
1104     static class Config {
1105         static final int NR_CONNECTED_MMWAVE = 1;
1106         static final int NR_CONNECTED = 2;
1107         static final int NR_NOT_RESTRICTED = 3;
1108         static final int NR_RESTRICTED = 4;
1109 
1110         Map<Integer, MobileIconGroup> nr5GIconMap = new HashMap<>();
1111 
1112         boolean showAtLeast3G = false;
1113         boolean alwaysShowCdmaRssi = false;
1114         boolean show4gForLte = false;
1115         boolean hideLtePlus = false;
1116         boolean hspaDataDistinguishable;
1117         boolean inflateSignalStrengths = false;
1118         boolean alwaysShowDataRatIcon = false;
1119         public String patternOfCarrierSpecificDataIcon = "";
1120 
1121         /**
1122          * Mapping from NR 5G status string to an integer. The NR 5G status string should match
1123          * those in carrier config.
1124          */
1125         private static final Map<String, Integer> NR_STATUS_STRING_TO_INDEX;
1126         static {
1127             NR_STATUS_STRING_TO_INDEX = new HashMap<>(4);
1128             NR_STATUS_STRING_TO_INDEX.put("connected_mmwave", NR_CONNECTED_MMWAVE);
1129             NR_STATUS_STRING_TO_INDEX.put("connected", NR_CONNECTED);
1130             NR_STATUS_STRING_TO_INDEX.put("not_restricted", NR_NOT_RESTRICTED);
1131             NR_STATUS_STRING_TO_INDEX.put("restricted", NR_RESTRICTED);
1132         }
1133 
readConfig(Context context)1134         static Config readConfig(Context context) {
1135             Config config = new Config();
1136             Resources res = context.getResources();
1137 
1138             config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
1139             config.alwaysShowCdmaRssi =
1140                     res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
1141             config.hspaDataDistinguishable =
1142                     res.getBoolean(R.bool.config_hspa_data_distinguishable);
1143             config.inflateSignalStrengths = res.getBoolean(
1144                     com.android.internal.R.bool.config_inflateSignalStrength);
1145 
1146             CarrierConfigManager configMgr = (CarrierConfigManager)
1147                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
1148             // Handle specific carrier config values for the default data SIM
1149             int defaultDataSubId = SubscriptionManager.from(context)
1150                     .getDefaultDataSubscriptionId();
1151             PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId);
1152             if (b != null) {
1153                 config.alwaysShowDataRatIcon = b.getBoolean(
1154                         CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
1155                 config.show4gForLte = b.getBoolean(
1156                         CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
1157                 config.hideLtePlus = b.getBoolean(
1158                         CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
1159                 config.patternOfCarrierSpecificDataIcon = b.getString(
1160                         CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
1161                 String nr5GIconConfiguration =
1162                         b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
1163                 if (!TextUtils.isEmpty(nr5GIconConfiguration)) {
1164                     String[] nr5GIconConfigPairs = nr5GIconConfiguration.trim().split(",");
1165                     for (String pair : nr5GIconConfigPairs) {
1166                         add5GIconMapping(pair, config);
1167                     }
1168                 }
1169             }
1170 
1171             return config;
1172         }
1173 
1174         /**
1175          * Add a mapping from NR 5G status to the 5G icon. All the icon resources come from
1176          * {@link TelephonyIcons}.
1177          *
1178          * @param keyValuePair the NR 5G status and icon name separated by a colon.
1179          * @param config container that used to store the parsed configs.
1180          */
1181         @VisibleForTesting
add5GIconMapping(String keyValuePair, Config config)1182         static void add5GIconMapping(String keyValuePair, Config config) {
1183             String[] kv = (keyValuePair.trim().toLowerCase()).split(":");
1184 
1185             if (kv.length != 2) {
1186                 if (DEBUG) Log.e(TAG, "Invalid 5G icon configuration, config = " + keyValuePair);
1187                 return;
1188             }
1189 
1190             String key = kv[0], value = kv[1];
1191 
1192             // There is no icon config for the specific 5G status.
1193             if (value.equals("none")) return;
1194 
1195             if (NR_STATUS_STRING_TO_INDEX.containsKey(key)
1196                     && TelephonyIcons.ICON_NAME_TO_ICON.containsKey(value)) {
1197                 config.nr5GIconMap.put(
1198                         NR_STATUS_STRING_TO_INDEX.get(key),
1199                         TelephonyIcons.ICON_NAME_TO_ICON.get(value));
1200             }
1201         }
1202     }
1203 }
1204