1af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/*
2af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Copyright (C) 2010 The Android Open Source Project
3af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
4af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Licensed under the Apache License, Version 2.0 (the "License");
5af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * you may not use this file except in compliance with the License.
6af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * You may obtain a copy of the License at
7af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
8af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *      http://www.apache.org/licenses/LICENSE-2.0
9af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
10af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Unless required by applicable law or agreed to in writing, software
11af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * distributed under the License is distributed on an "AS IS" BASIS,
12af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * See the License for the specific language governing permissions and
14af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * limitations under the License.
15af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock */
16af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
17af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpackage com.android.systemui.statusbar.policy;
18af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
19403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
20403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
21403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
22403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
23403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport static android.net.NetworkCapabilities.TRANSPORT_WIFI;
24af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.BroadcastReceiver;
25af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.Context;
26af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.Intent;
27af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.IntentFilter;
28af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.res.Resources;
29af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.net.ConnectivityManager;
30403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport android.net.NetworkCapabilities;
31af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.net.NetworkInfo;
32af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.net.wifi.WifiConfiguration;
33af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.net.wifi.WifiInfo;
34af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.net.wifi.WifiManager;
35af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.AsyncTask;
36af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Bundle;
37af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Handler;
38af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Message;
39af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Messenger;
40af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.provider.Settings;
41af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.telephony.PhoneStateListener;
42af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.telephony.ServiceState;
43af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.telephony.SignalStrength;
44b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport android.telephony.SubscriptionInfo;
45b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport android.telephony.SubscriptionManager;
46b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
47af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.telephony.TelephonyManager;
48b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport android.text.TextUtils;
49d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monkimport android.text.format.DateFormat;
50af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.util.Log;
513aaabd71242bf8aedb8edb87565943460a79226dJason Monkimport android.util.SparseArray;
52af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
53e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monkimport com.android.internal.annotations.VisibleForTesting;
54af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.internal.telephony.IccCardConstants;
55b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport com.android.internal.telephony.PhoneConstants;
56af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.internal.telephony.TelephonyIntents;
57af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.internal.telephony.cdma.EriInfo;
58af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.internal.util.AsyncChannel;
59af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.DemoMode;
60af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.R;
61af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
62af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.io.FileDescriptor;
63af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.io.PrintWriter;
64af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.util.ArrayList;
65403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colittiimport java.util.BitSet;
66b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport java.util.Collections;
67b574627ebc383d35cf8212ee241b191b26518a0dJason Monkimport java.util.Comparator;
68d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monkimport java.util.HashMap;
69af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.util.List;
70af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.util.Locale;
71d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monkimport java.util.Map;
72d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monkimport java.util.Objects;
73af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
74af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/** Platform implementation of the network controller. **/
75af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpublic class NetworkControllerImpl extends BroadcastReceiver
76af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        implements NetworkController, DemoMode {
77af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    // debug
78d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static final String TAG = "NetworkController";
79d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
80d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    // additional diagnostics, but not logspew
81d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static final boolean CHATTY =  Log.isLoggable(TAG + ".Chat", Log.DEBUG);
82b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // Save the previous SignalController.States of all SignalControllers for dumps.
83d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static final boolean RECORD_HISTORY = true;
84b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // If RECORD_HISTORY how many to save, must be a power of 2.
85d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static final int HISTORY_SIZE = 16;
86d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
87d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private static final int INET_CONDITION_THRESHOLD = 50;
88d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
89d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private final Context mContext;
90d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private final TelephonyManager mPhone;
91d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private final WifiManager mWifiManager;
92d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private final ConnectivityManager mConnectivityManager;
93b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private final SubscriptionManager mSubscriptionManager;
94d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private final boolean mHasMobileDataFeature;
953aaabd71242bf8aedb8edb87565943460a79226dJason Monk    private Config mConfig;
96d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
97d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    // Subcontrollers.
98d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    @VisibleForTesting
99d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    final WifiSignalController mWifiSignalController;
100d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    @VisibleForTesting
101b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    final Map<Integer, MobileSignalController> mMobileSignalControllers =
102b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            new HashMap<Integer, MobileSignalController>();
103b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // When no SIMs are around at setup, and one is added later, it seems to default to the first
104b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // SIM for most actions.  This may be null if there aren't any SIMs around.
105b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private MobileSignalController mDefaultSignalController;
1060e2400fb16f5a52f755d8a2dc2a4688cf0c9a247Jason Monk    private final AccessPointControllerImpl mAccessPoints;
107d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private final MobileDataControllerImpl mMobileDataController;
108af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
109403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    // Network types that replace the carrier label if the device does not support mobile data.
110af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private boolean mBluetoothTethered = false;
111403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    private boolean mEthernetConnected = false;
112af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
113403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    // state of inet connection
114af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private boolean mConnected = false;
115d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private boolean mInetCondition; // Used for Logging and demo.
116af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
117403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are
118403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    // connected and validated, respectively.
119403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    private final BitSet mConnectedTransports = new BitSet();
120403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    private final BitSet mValidatedTransports = new BitSet();
121403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti
122d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    // States that don't belong to a subcontroller.
123af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private boolean mAirplaneMode = false;
124b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private boolean mHasNoSims;
125af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private Locale mLocale = null;
126b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // This list holds our ordering.
127b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private List<SubscriptionInfo> mCurrentSubscriptions
128b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            = new ArrayList<SubscriptionInfo>();
129af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
130d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    // All the callbacks.
131d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>();
132d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private ArrayList<CarrierLabelListener> mCarrierListeners =
133d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            new ArrayList<CarrierLabelListener>();
134d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
135d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
136d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            new ArrayList<NetworkSignalChangedCallback>();
137c014decc74edc3a415efb78893f597f3b800a212Jason Monk    @VisibleForTesting
138c014decc74edc3a415efb78893f597f3b800a212Jason Monk    boolean mListening;
1397f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock
140403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    // The current user ID.
141403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    private int mCurrentUserId;
142403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti
143af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    /**
144af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock     * Construct this controller object and register for updates.
145af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock     */
146af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public NetworkControllerImpl(Context context) {
147f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk        this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
148f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
14929f7a7b0ae2264651c176ef50cc21cf9131247e3Jason Monk                (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
150b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                SubscriptionManager.from(context), Config.readConfig(context),
151b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                new AccessPointControllerImpl(context), new MobileDataControllerImpl(context));
152f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk        registerListeners();
153f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk    }
154f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk
155f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk    @VisibleForTesting
156f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk    NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
157b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            TelephonyManager telephonyManager, WifiManager wifiManager,
158b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            SubscriptionManager subManager, Config config,
159d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            AccessPointControllerImpl accessPointController,
160d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            MobileDataControllerImpl mobileDataController) {
161af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mContext = context;
162b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        mConfig = config;
163af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
164b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        mSubscriptionManager = subManager;
165f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk        mConnectivityManager = connectivityManager;
166f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk        mHasMobileDataFeature =
167f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk                mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
168af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
169af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        // telephony
170e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk        mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
171af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
172af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        // wifi
173f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk        mWifiManager = wifiManager;
174e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk
175d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mLocale = mContext.getResources().getConfiguration().locale;
17629f7a7b0ae2264651c176ef50cc21cf9131247e3Jason Monk        mAccessPoints = accessPointController;
17729f7a7b0ae2264651c176ef50cc21cf9131247e3Jason Monk        mMobileDataController = mobileDataController;
178d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mMobileDataController.setNetworkController(this);
179d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // TODO: Find a way to move this into MobileDataController.
180d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() {
181e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk            @Override
182e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk            public void onMobileDataEnabled(boolean enabled) {
183e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk                notifyMobileDataEnabled(enabled);
184e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk            }
185e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk        });
186d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
187d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSignalsChangedCallbacks, mSignalClusters, this);
188d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
189d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
190b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        updateAirplaneMode(true /* force callback */);
1910e2400fb16f5a52f755d8a2dc2a4688cf0c9a247Jason Monk        mAccessPoints.setNetworkController(this);
192e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk    }
193e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk
194f13b4b39f266e43239e40e3bf7d6997600555068Jason Monk    private void registerListeners() {
195b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
196b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mobileSignalController.registerListener();
197b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
198071743f0cbbfab36150db6ec945452579d02e8ddWink Saville        mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
199e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk
200af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        // broadcasts
201af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        IntentFilter filter = new IntentFilter();
202af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
203af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
204af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
205af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
206b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
207b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
208af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
20923dd176915fc118563e510647f048589c12e78e5Lorenzo Colitti        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
210af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
211af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
212af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
213e0cdb6079e9e9e8b87a71d67ef5aa1dc0e3e6840Jason Monk        mContext.registerReceiver(this, filter);
214b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        mListening = true;
215b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
216b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        updateMobileControllers();
217b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock    }
218b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock
219d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private void unregisterListeners() {
220b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        mListening = false;
221b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
222b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mobileSignalController.unregisterListener();
223b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
224071743f0cbbfab36150db6ec945452579d02e8ddWink Saville        mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
225d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mContext.unregisterReceiver(this);
226d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
227d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
2280e2400fb16f5a52f755d8a2dc2a4688cf0c9a247Jason Monk    public int getConnectedWifiLevel() {
2290e2400fb16f5a52f755d8a2dc2a4688cf0c9a247Jason Monk        return mWifiSignalController.getState().level;
2300e2400fb16f5a52f755d8a2dc2a4688cf0c9a247Jason Monk    }
2310e2400fb16f5a52f755d8a2dc2a4688cf0c9a247Jason Monk
2325d325074b14e102a90c211a8d7b65316158ac0e4Jason Monk    @Override
233d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public AccessPointController getAccessPointController() {
234d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        return mAccessPoints;
2355d325074b14e102a90c211a8d7b65316158ac0e4Jason Monk    }
2365d325074b14e102a90c211a8d7b65316158ac0e4Jason Monk
2375d325074b14e102a90c211a8d7b65316158ac0e4Jason Monk    @Override
238d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public MobileDataController getMobileDataController() {
239d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        return mMobileDataController;
240d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
241d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
242d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public void addEmergencyListener(EmergencyListener listener) {
243d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mEmergencyListeners.add(listener);
244b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        listener.setEmergencyCallsOnly(isEmergencyOnly());
245d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
246d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
247d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public void addCarrierLabel(CarrierLabelListener listener) {
248d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mCarrierListeners.add(listener);
249d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        refreshCarrierLabel();
2505d325074b14e102a90c211a8d7b65316158ac0e4Jason Monk    }
2515d325074b14e102a90c211a8d7b65316158ac0e4Jason Monk
252b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock    private void notifyMobileDataEnabled(boolean enabled) {
253b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        final int length = mSignalsChangedCallbacks.size();
254d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        for (int i = 0; i < length; i++) {
255d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled);
256b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock        }
257af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
258af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
259af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public boolean hasMobileDataFeature() {
260af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return mHasMobileDataFeature;
261af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
262af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
263af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public boolean hasVoiceCallingFeature() {
264af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
265af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
266af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
267b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private MobileSignalController getDataController() {
268b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        int dataSubId = SubscriptionManager.getDefaultDataSubId();
269a54bf6583380cf328b9858d942b4c387c6163f0fWink Saville        if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) {
270b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (DEBUG) Log.e(TAG, "No data sim selected");
271b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            return mDefaultSignalController;
272b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
273b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (mMobileSignalControllers.containsKey(dataSubId)) {
274b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            return mMobileSignalControllers.get(dataSubId);
275b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
276c846632762f0579d504d90e5b986183779fcf034Dan Sandler        if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
277b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        return mDefaultSignalController;
278b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    }
279b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
280d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public String getMobileNetworkName() {
281b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        MobileSignalController controller = getDataController();
282b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        return controller != null ? controller.getState().networkName : "";
283af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
284af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
285d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public boolean isEmergencyOnly() {
286b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        int voiceSubId = SubscriptionManager.getDefaultVoiceSubId();
287a54bf6583380cf328b9858d942b4c387c6163f0fWink Saville        if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
288a54bf6583380cf328b9858d942b4c387c6163f0fWink Saville            for (MobileSignalController mobileSignalController :
289a54bf6583380cf328b9858d942b4c387c6163f0fWink Saville                                            mMobileSignalControllers.values()) {
290b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (!mobileSignalController.isEmergencyOnly()) {
291b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    return false;
292b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
293b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
294b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
295b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (mMobileSignalControllers.containsKey(voiceSubId)) {
296b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            return mMobileSignalControllers.get(voiceSubId).isEmergencyOnly();
297b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
298c846632762f0579d504d90e5b986183779fcf034Dan Sandler        if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
299b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        // Something is wrong, better assume we can't make calls...
300b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        return true;
301af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
302af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
303d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    /**
304d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     * Emergency status may have changed (triggered by MobileSignalController),
305d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     * so we should recheck and send out the state to listeners.
306d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     */
307d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    void recalculateEmergency() {
308d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        final boolean emergencyOnly = isEmergencyOnly();
309b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        final int length = mEmergencyListeners.size();
310d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        for (int i = 0; i < length; i++) {
311d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly);
312d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
313b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        // If the emergency has a chance to change, then so does the carrier
314b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        // label.
315b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        refreshCarrierLabel();
316af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
317af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
318af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void addSignalCluster(SignalCluster cluster) {
319af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mSignalClusters.add(cluster);
320b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        cluster.setSubs(mCurrentSubscriptions);
321d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
322d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                R.string.accessibility_airplane_mode);
323b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        cluster.setNoSims(mHasNoSims);
324d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mWifiSignalController.notifyListeners();
325b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
326b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mobileSignalController.notifyListeners();
327b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
328af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
329af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
330af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
331af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mSignalsChangedCallbacks.add(cb);
332d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        cb.onAirplaneModeChanged(mAirplaneMode);
333b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        cb.onNoSimVisibleChanged(mHasNoSims);
334d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mWifiSignalController.notifyListeners();
335b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
336b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mobileSignalController.notifyListeners();
337b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
338af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
339af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
340af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
341af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mSignalsChangedCallbacks.remove(cb);
342af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
343af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
344af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    @Override
345af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void setWifiEnabled(final boolean enabled) {
346af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        new AsyncTask<Void, Void, Void>() {
347af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            @Override
348af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            protected Void doInBackground(Void... args) {
349af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                // Disable tethering if enabling Wifi
350af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                final int wifiApState = mWifiManager.getWifiApState();
351af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
352d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
353af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    mWifiManager.setWifiApEnabled(null, false);
354af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
355af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
356af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                mWifiManager.setWifiEnabled(enabled);
357af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                return null;
358af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
359af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }.execute();
360af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
361af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
362b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock    @Override
363403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    public void onUserSwitched(int newUserId) {
364403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mCurrentUserId = newUserId;
365403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mAccessPoints.onUserSwitched(newUserId);
366403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        updateConnectivity();
367403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        refreshCarrierLabel();
368403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    }
369403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti
370403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    @Override
371d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public void onReceive(Context context, Intent intent) {
372d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        if (CHATTY) {
373d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            Log.d(TAG, "onReceive: intent=" + intent);
374f7f00ea9d3b648bd3491091188bc7a3615ae4affJohn Spurlock        }
375d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        final String action = intent.getAction();
376d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) ||
377d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
378403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            updateConnectivity();
379d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            refreshCarrierLabel();
380d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
3813aaabd71242bf8aedb8edb87565943460a79226dJason Monk            mConfig = Config.readConfig(mContext);
3823aaabd71242bf8aedb8edb87565943460a79226dJason Monk            handleConfigurationChanged();
383d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
384d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            refreshLocale();
385d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            updateAirplaneMode(false);
386d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            refreshCarrierLabel();
387b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
388b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // We are using different subs now, we might be able to make calls.
389b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            recalculateEmergency();
390b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
391b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Notify every MobileSignalController so they can know whether they are the
392b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // data sim or not.
393b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            for (MobileSignalController controller : mMobileSignalControllers.values()) {
394b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                controller.handleBroadcast(intent);
395b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
396b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
397b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Might have different subscriptions now.
398b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            updateMobileControllers();
399b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        } else {
400b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
401b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
402a54bf6583380cf328b9858d942b4c387c6163f0fWink Saville            if (SubscriptionManager.isValidSubscriptionId(subId)) {
403b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (mMobileSignalControllers.containsKey(subId)) {
404b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mMobileSignalControllers.get(subId).handleBroadcast(intent);
405b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                } else {
406b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    // Can't find this subscription...  We must be out of date.
407b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    updateMobileControllers();
408b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
409b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            } else {
410b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // No sub id, must be for the wifi.
411b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mWifiSignalController.handleBroadcast(intent);
412b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
413b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
414b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    }
415b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
4163aaabd71242bf8aedb8edb87565943460a79226dJason Monk    @VisibleForTesting
4173aaabd71242bf8aedb8edb87565943460a79226dJason Monk    void handleConfigurationChanged() {
4183aaabd71242bf8aedb8edb87565943460a79226dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
4193aaabd71242bf8aedb8edb87565943460a79226dJason Monk            mobileSignalController.setConfiguration(mConfig);
4203aaabd71242bf8aedb8edb87565943460a79226dJason Monk        }
4213aaabd71242bf8aedb8edb87565943460a79226dJason Monk        refreshLocale();
4223aaabd71242bf8aedb8edb87565943460a79226dJason Monk        refreshCarrierLabel();
4233aaabd71242bf8aedb8edb87565943460a79226dJason Monk    }
4243aaabd71242bf8aedb8edb87565943460a79226dJason Monk
425b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private void updateMobileControllers() {
426b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (!mListening) {
427b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            return;
428b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
429b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
430c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk        if (subscriptions == null) {
431c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk            subscriptions = Collections.emptyList();
432c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk        }
433b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        // If there have been no relevant changes to any of the subscriptions, we can leave as is.
434b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (hasCorrectMobileControllers(subscriptions)) {
435b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Even if the controllers are correct, make sure we have the right no sims state.
436b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Such as on boot, don't need any controllers, because there are no sims,
437b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // but we still need to update the no sim state.
438b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            updateNoSims();
439b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            return;
440b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
441b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        setCurrentSubscriptions(subscriptions);
442b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        updateNoSims();
443b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    }
444b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
44521d05a08d8ab632608980371d78990890029f4e4Jason Monk    @VisibleForTesting
44621d05a08d8ab632608980371d78990890029f4e4Jason Monk    protected void updateNoSims() {
44721d05a08d8ab632608980371d78990890029f4e4Jason Monk        boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
448b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (hasNoSims != mHasNoSims) {
449b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mHasNoSims = hasNoSims;
450b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            notifyListeners();
451b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
452b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    }
453b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
454b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    @VisibleForTesting
455b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
456b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
457b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            @Override
458b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
459b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                return lhs.getSimSlotIndex() == rhs.getSimSlotIndex()
460b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        ? lhs.getSubscriptionId() - rhs.getSubscriptionId()
461b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        : lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
462b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
463b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        });
464b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        final int length = mSignalClusters.size();
465b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (int i = 0; i < length; i++) {
466b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mSignalClusters.get(i).setSubs(subscriptions);
467b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
468b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        mCurrentSubscriptions = subscriptions;
469b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
470b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        HashMap<Integer, MobileSignalController> cachedControllers =
471b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
472b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        mMobileSignalControllers.clear();
473b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        final int num = subscriptions.size();
474b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (int i = 0; i < num; i++) {
475b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            int subId = subscriptions.get(i).getSubscriptionId();
476b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // If we have a copy of this controller already reuse it, otherwise make a new one.
477b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (cachedControllers.containsKey(subId)) {
478c014decc74edc3a415efb78893f597f3b800a212Jason Monk                mMobileSignalControllers.put(subId, cachedControllers.remove(subId));
479b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            } else {
480b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
481b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters,
482b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        this, subscriptions.get(i));
483b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mMobileSignalControllers.put(subId, controller);
484b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (subscriptions.get(i).getSimSlotIndex() == 0) {
485b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mDefaultSignalController = controller;
486b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
487b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (mListening) {
488b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    controller.registerListener();
489b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
490b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
491b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
492b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (mListening) {
493b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            for (Integer key : cachedControllers.keySet()) {
494b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (cachedControllers.get(key) == mDefaultSignalController) {
495b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mDefaultSignalController = null;
496b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
497b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                cachedControllers.get(key).unregisterListener();
498b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
499d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
50025d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk        // There may be new MobileSignalControllers around, make sure they get the current
50125d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk        // inet condition and airplane mode.
50225d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk        pushConnectivityToSignals();
50325d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk        updateAirplaneMode(true /* force */);
504b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    }
505b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
506c014decc74edc3a415efb78893f597f3b800a212Jason Monk    @VisibleForTesting
507c014decc74edc3a415efb78893f597f3b800a212Jason Monk    boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
508c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk        if (allSubscriptions.size() != mMobileSignalControllers.size()) {
509c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk            return false;
510b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
511b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (SubscriptionInfo info : allSubscriptions) {
512b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) {
513b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                return false;
514b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
515b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
516b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        return true;
517b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock    }
518b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock
519d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private void updateAirplaneMode(boolean force) {
520d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
521d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
522d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        if (airplaneMode != mAirplaneMode || force) {
523d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mAirplaneMode = airplaneMode;
524b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
525b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mobileSignalController.setAirplaneMode(mAirplaneMode);
526b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
527b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            notifyListeners();
528d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            refreshCarrierLabel();
529d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
530b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock    }
531b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock
532d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private void refreshLocale() {
533d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        Locale current = mContext.getResources().getConfiguration().locale;
534b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (!current.equals(mLocale)) {
535d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mLocale = current;
536d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyAllListeners();
537d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
538b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock    }
539b98f747c714ff8252471ca0a3295c2cb9ccb4f3aJohn Spurlock
540d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    /**
541b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     * Forces update of all callbacks on both SignalClusters and
542b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     * NetworkSignalChangedCallbacks.
543b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     */
544d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private void notifyAllListeners() {
545b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        notifyListeners();
546b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
547b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mobileSignalController.notifyListeners();
548b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
549d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mWifiSignalController.notifyListeners();
550c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi    }
551c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi
552b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    /**
553b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     * Notifies listeners of changes in state of to the NetworkController, but
554b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     * does not notify for any info on SignalControllers, for that call
555b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     * notifyAllListeners.
556b574627ebc383d35cf8212ee241b191b26518a0dJason Monk     */
557b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private void notifyListeners() {
558d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        int length = mSignalClusters.size();
559d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        for (int i = 0; i < length; i++) {
560d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
561d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    R.string.accessibility_airplane_mode);
562b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mSignalClusters.get(i).setNoSims(mHasNoSims);
563d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
564d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        int signalsChangedLength = mSignalsChangedCallbacks.size();
565d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        for (int i = 0; i < signalsChangedLength; i++) {
566d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode);
567b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mSignalsChangedCallbacks.get(i).onNoSimVisibleChanged(mHasNoSims);
568d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
569c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi    }
570c737b9bd5a0bc60fd92150d55f8ddb530697b987Jorim Jaggi
571d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    /**
572d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     * Update the Inet conditions and what network we are connected to.
573d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     */
574403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti    private void updateConnectivity() {
575403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mConnectedTransports.clear();
576403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mValidatedTransports.clear();
577403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        for (NetworkCapabilities nc :
578403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
579403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            for (int transportType : nc.getTransportTypes()) {
580403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                mConnectedTransports.set(transportType);
581403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
582403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                    mValidatedTransports.set(transportType);
583403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                }
584403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            }
585af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
586af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
587d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        if (CHATTY) {
588403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
589403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
590af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
591d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
592403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mConnected = !mConnectedTransports.isEmpty();
593403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mInetCondition = !mValidatedTransports.isEmpty();
594403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mBluetoothTethered = mConnectedTransports.get(TRANSPORT_BLUETOOTH);
595403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        mEthernetConnected = mConnectedTransports.get(TRANSPORT_ETHERNET);
596d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
59725d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk        pushConnectivityToSignals();
59825d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk    }
59925d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk
60025d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk    /**
60125d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk     * Pushes the current connectivity state to all SignalControllers.
60225d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk     */
60325d8a48e7348c939a3f829fe87e47857307bdbf9Jason Monk    private void pushConnectivityToSignals() {
604d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // We want to update all the icons, all at once, for any condition change
605b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
606403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            mobileSignalController.setInetCondition(
607403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                    mInetCondition ? 1 : 0,
608403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                    mValidatedTransports.get(mobileSignalController.getTransportType()) ? 1 : 0);
609b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
610d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mWifiSignalController.setInetCondition(
611403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0);
612af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
613af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
614d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    /**
615d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     * Recalculate and update the carrier label.
616d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     */
617d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    void refreshCarrierLabel() {
618d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        Context context = mContext;
619af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
620d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        WifiSignalController.WifiState wifiState = mWifiSignalController.getState();
621b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        String label = "";
622b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController controller : mMobileSignalControllers.values()) {
623b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            label = controller.getLabel(label, mConnected, mHasMobileDataFeature);
624b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
625af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
626d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // TODO Simplify this ugliness, some of the flows below shouldn't be possible anymore
627d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // but stay for the sake of history.
628d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        if (mBluetoothTethered && !mHasMobileDataFeature) {
629d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            label = mContext.getString(R.string.bluetooth_tethered);
630af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
631af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
632403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        if (mEthernetConnected && !mHasMobileDataFeature) {
633d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            label = context.getString(R.string.ethernet_label);
634af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
635af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
636b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        if (mAirplaneMode && !isEmergencyOnly()) {
637d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // combined values from connected wifi take precedence over airplane mode
638d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (wifiState.connected && mHasMobileDataFeature) {
639d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // Suppress "No internet connection." from mobile if wifi connected.
640d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                label = "";
641d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
642d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                 if (!mHasMobileDataFeature) {
643d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                      label = context.getString(
644d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                              R.string.status_bar_settings_signal_meter_disconnected);
645d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                 }
646af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
647b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        } else if (!isMobileDataConnected() && !wifiState.connected && !mBluetoothTethered &&
648403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                 !mEthernetConnected && !mHasMobileDataFeature) {
649d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // Pretty much no connection.
650d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
651af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
652af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
653d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // for mobile devices, we always show mobile connection info here (SPN/PLMN)
654d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // for other devices, we show whatever network is connected
655d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // This is determined above by references to mHasMobileDataFeature.
656d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        int length = mCarrierListeners.size();
657d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        for (int i = 0; i < length; i++) {
658d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCarrierListeners.get(i).setCarrierLabel(label);
659af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
660d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
661af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
662b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private boolean isMobileDataConnected() {
663b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        MobileSignalController controller = getDataController();
664b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        return controller != null ? controller.getState().dataConnected : false;
665b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    }
666b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
667d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
668d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println("NetworkController state:");
669403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti
670d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println("  - telephony ------");
671d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.print("  hasVoiceCallingFeature()=");
672d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println(hasVoiceCallingFeature());
673af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
674d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println("  - Bluetooth ----");
675d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.print("  mBtReverseTethered=");
676d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println(mBluetoothTethered);
677d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
678d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println("  - connectivity ------");
679403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        pw.print("  mConnectedTransports=");
680403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        pw.println(mConnectedTransports);
681403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        pw.print("  mValidatedTransports=");
682403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        pw.println(mValidatedTransports);
683d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.print("  mInetCondition=");
684d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println(mInetCondition);
685d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.print("  mAirplaneMode=");
686d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println(mAirplaneMode);
687d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.print("  mLocale=");
688d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        pw.println(mLocale);
689d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
690b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
691b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mobileSignalController.dump(pw);
692b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        }
693d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        mWifiSignalController.dump(pw);
694d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
695d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
696d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private boolean mDemoMode;
697d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private int mDemoInetCondition;
698d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    private WifiSignalController.WifiState mDemoWifiState;
699d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
700d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    @Override
701d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public void dispatchDemoCommand(String command, Bundle args) {
702d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
703d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (DEBUG) Log.d(TAG, "Entering demo mode");
704d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            unregisterListeners();
705d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mDemoMode = true;
706d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mDemoInetCondition = mInetCondition ? 1 : 0;
707d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mDemoWifiState = mWifiSignalController.getState();
708d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
709d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (DEBUG) Log.d(TAG, "Exiting demo mode");
710d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mDemoMode = false;
711b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Update what MobileSignalControllers, because they may change
712b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // to set the number of sim slots.
713b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            updateMobileControllers();
714b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            for (MobileSignalController controller : mMobileSignalControllers.values()) {
715b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                controller.resetLastState();
716b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
717d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mWifiSignalController.resetLastState();
718d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            registerListeners();
719d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyAllListeners();
720d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            refreshCarrierLabel();
721d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
722d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String airplane = args.getString("airplane");
723d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (airplane != null) {
724d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                boolean show = airplane.equals("show");
725d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                int length = mSignalClusters.size();
726d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                for (int i = 0; i < length; i++) {
727d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mSignalClusters.get(i).setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON,
728d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            R.string.accessibility_airplane_mode);
729d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
730af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
731d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String fully = args.getString("fully");
732d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (fully != null) {
733d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
734d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mWifiSignalController.setInetCondition(mDemoInetCondition);
735b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                for (MobileSignalController controller : mMobileSignalControllers.values()) {
736b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    controller.setInetCondition(mDemoInetCondition, mDemoInetCondition);
737b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
738d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
739d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String wifi = args.getString("wifi");
740d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (wifi != null) {
741d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                boolean show = wifi.equals("show");
742d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                String level = args.getString("level");
743d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (level != null) {
744d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mDemoWifiState.level = level.equals("null") ? -1
745d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
746d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mDemoWifiState.connected = mDemoWifiState.level >= 0;
747d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
748d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDemoWifiState.enabled = show;
749d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mWifiSignalController.notifyListeners();
750af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
751b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            String sims = args.getString("sims");
752b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (sims != null) {
753b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                int num = Integer.parseInt(sims);
754b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>();
755b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (num != mMobileSignalControllers.size()) {
756b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mMobileSignalControllers.clear();
757b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
758b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    for (int i = start /* get out of normal index range */; i < start + num; i++) {
759b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        SubscriptionInfo info = new SubscriptionInfo(i, "", i, "", "", 0, 0, "", 0,
760b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                                null, 0, 0, "");
761b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        subs.add(info);
762b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        mMobileSignalControllers.put(i, new MobileSignalController(mContext,
763b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                                mConfig, mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks,
764b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                                mSignalClusters, this, info));
765b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    }
766b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
767b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                final int n = mSignalClusters.size();
768b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                for (int i = 0; i < n; i++) {
769b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mSignalClusters.get(i).setSubs(subs);
770b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
771b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
772b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            String nosim = args.getString("nosim");
773b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (nosim != null) {
774b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                boolean show = nosim.equals("show");
775b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                final int n = mSignalClusters.size();
776b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                for (int i = 0; i < n; i++) {
777b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mSignalClusters.get(i).setNoSims(show);
778b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
779b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
780d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String mobile = args.getString("mobile");
781d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mobile != null) {
782d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                boolean show = mobile.equals("show");
783d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                String datatype = args.getString("datatype");
784b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                String slotString = args.getString("slot");
785b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
786b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // Hack to index linearly for easy use.
787b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                MobileSignalController controller = mMobileSignalControllers
788b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        .values().toArray(new MobileSignalController[0])[slot];
789b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                controller.getState().dataSim = datatype != null;
790d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (datatype != null) {
791b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    controller.getState().iconGroup =
792d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("1x") ? TelephonyIcons.ONE_X :
793d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("3g") ? TelephonyIcons.THREE_G :
794d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("4g") ? TelephonyIcons.FOUR_G :
795d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("e") ? TelephonyIcons.E :
796d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("g") ? TelephonyIcons.G :
797d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("h") ? TelephonyIcons.H :
798d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("lte") ? TelephonyIcons.LTE :
799d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            datatype.equals("roam") ? TelephonyIcons.ROAMING :
800d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            TelephonyIcons.UNKNOWN;
801d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
802d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
803d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                String level = args.getString("level");
804d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (level != null) {
805b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    controller.getState().level = level.equals("null") ? -1
806d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            : Math.min(Integer.parseInt(level), icons[0].length - 1);
807b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    controller.getState().connected = controller.getState().level >= 0;
808d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
809b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                controller.getState().enabled = show;
810b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                controller.notifyListeners();
811af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
812d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            refreshCarrierLabel();
813af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
814af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
815af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
816b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    private final OnSubscriptionsChangedListener mSubscriptionListener =
817b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            new OnSubscriptionsChangedListener() {
818c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk        @Override
819c9f0571302a59b171ff16fa049e9727503ca2986Jason Monk        public void onSubscriptionsChanged() {
820b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            updateMobileControllers();
821b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        };
822b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    };
823b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
824b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // TODO: Move to its own file.
825d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static class WifiSignalController extends
826d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
827d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final WifiManager mWifiManager;
828d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final AsyncChannel mWifiChannel;
829d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final boolean mHasMobileData;
830d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
831d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public WifiSignalController(Context context, boolean hasMobileData,
832d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                List<NetworkSignalChangedCallback> signalCallbacks,
833d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
834403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
835403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                    signalCallbacks, signalClusters, networkController);
836d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
837d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mHasMobileData = hasMobileData;
838d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            Handler handler = new WifiHandler();
839d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mWifiChannel = new AsyncChannel();
840d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
841d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (wifiMessenger != null) {
842d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mWifiChannel.connect(context, handler, wifiMessenger);
843af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
844d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // WiFi only has one state.
845d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
846d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    "Wi-Fi Icons",
847d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiIcons.WIFI_SIGNAL_STRENGTH,
848d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
849d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
850d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiIcons.WIFI_NO_NETWORK,
851d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiIcons.QS_WIFI_NO_NETWORK,
852d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiIcons.WIFI_NO_NETWORK,
853d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiIcons.QS_WIFI_NO_NETWORK,
854d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    AccessibilityContentDescriptions.WIFI_NO_CONNECTION
855d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    );
856af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
857af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
858d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @Override
859b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        protected WifiState cleanState() {
860d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return new WifiState();
861d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
862af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
863d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @Override
864d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void notifyListeners() {
865d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // only show wifi in the cluster if connected or if wifi-only
866b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            boolean wifiVisible = mCurrentState.enabled
867d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    && (mCurrentState.connected || !mHasMobileData);
868b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
869b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
870d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String contentDescription = getStringIfExists(getContentDescription());
871d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int length = mSignalsChangedCallbacks.size();
872d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            for (int i = 0; i < length; i++) {
873d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled,
874d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        mCurrentState.connected, getQsCurrentIconId(),
875d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        ssidPresent && mCurrentState.activityIn,
876d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc);
877d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
878af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
879d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int signalClustersLength = mSignalClusters.size();
880d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            for (int i = 0; i < signalClustersLength; i++) {
881b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mSignalClusters.get(i).setWifiIndicators(wifiVisible, getCurrentIconId(),
882b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        contentDescription);
883af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
884af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
88523dd176915fc118563e510647f048589c12e78e5Lorenzo Colitti
886d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
887d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Extract wifi state directly from broadcasts about changes in wifi state.
888d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
889d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void handleBroadcast(Intent intent) {
890d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String action = intent.getAction();
891d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
892d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
893d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
894d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
895d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                final NetworkInfo networkInfo = (NetworkInfo)
896d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
897d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.connected = networkInfo != null && networkInfo.isConnected();
898d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // If Connected grab the signal strength and ssid.
899d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (mCurrentState.connected) {
900d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    // try getting it out of the intent first
901d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
902d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
903d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            : mWifiManager.getConnectionInfo();
904d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    if (info != null) {
905b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        mCurrentState.ssid = getSsid(info);
906af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    } else {
907d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        mCurrentState.ssid = null;
908af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    }
909d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                } else if (!mCurrentState.connected) {
910d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mCurrentState.ssid = null;
911d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
912d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
913b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // Default to -200 as its below WifiManager.MIN_RSSI.
914d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
915d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.level = WifiManager.calculateSignalLevel(
916d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT);
917af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
918af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
919d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyListenersIfNecessary();
920af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
921af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
922b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        private String getSsid(WifiInfo info) {
923d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String ssid = info.getSSID();
924d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (ssid != null) {
925d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return ssid;
926d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
927d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // OK, it's not in the connectionInfo; we have to go hunting for it
928d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
929d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int length = networks.size();
930d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            for (int i = 0; i < length; i++) {
931d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (networks.get(i).networkId == info.getNetworkId()) {
932d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    return networks.get(i).SSID;
933af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
934af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
935d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return null;
936af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
937af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
938d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @VisibleForTesting
939d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setActivity(int wifiActivity) {
940d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
941d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    || wifiActivity == WifiManager.DATA_ACTIVITY_IN;
942d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
943d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    || wifiActivity == WifiManager.DATA_ACTIVITY_OUT;
944d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyListenersIfNecessary();
9451cd44e3ab4c413df05a0281fc80dc35fdc932746Jason Monk        }
9461cd44e3ab4c413df05a0281fc80dc35fdc932746Jason Monk
947d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
948d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Handler to receive the data activity on wifi.
949d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
950d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        class WifiHandler extends Handler {
951d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
952d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void handleMessage(Message msg) {
953d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                switch (msg.what) {
954d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
955d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
956d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            mWifiChannel.sendMessage(Message.obtain(this,
957d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                                    AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
958d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        } else {
959d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            Log.e(mTag, "Failed to connect to wifi");
960d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        }
961af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                        break;
962d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    case WifiManager.DATA_ACTIVITY_NOTIFICATION:
963d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        setActivity(msg.arg1);
964af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                        break;
965af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                    default:
966d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        // Ignore
967af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                        break;
968af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
969af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
970af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
971af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
972d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        static class WifiState extends SignalController.State {
973d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String ssid;
974af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
975d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
976d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void copyFrom(State s) {
977b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                super.copyFrom(s);
978d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                WifiState state = (WifiState) s;
979d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                ssid = state.ssid;
980af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
981af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
982d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
983d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            protected void toString(StringBuilder builder) {
984d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                super.toString(builder);
985b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                builder.append(',').append("ssid=").append(ssid);
986d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
987af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
988d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
989d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public boolean equals(Object o) {
990d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return super.equals(o)
991d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && Objects.equals(((WifiState) o).ssid, ssid);
992af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
993af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
994af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
995af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
996b574627ebc383d35cf8212ee241b191b26518a0dJason Monk    // TODO: Move to its own file.
997c014decc74edc3a415efb78893f597f3b800a212Jason Monk    public static class MobileSignalController extends SignalController<
998c014decc74edc3a415efb78893f597f3b800a212Jason Monk            MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
999d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final TelephonyManager mPhone;
1000d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final String mNetworkNameDefault;
1001d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final String mNetworkNameSeparator;
1002b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        @VisibleForTesting
1003b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        final PhoneStateListener mPhoneStateListener;
1004b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        // Save entire info for logging, we only use the id.
1005b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        private final SubscriptionInfo mSubscriptionInfo;
1006d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1007d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // @VisibleForDemoMode
10083aaabd71242bf8aedb8edb87565943460a79226dJason Monk        final SparseArray<MobileIconGroup> mNetworkToIconLookup;
1009d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1010d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // Since some pieces of the phone state are interdependent we store it locally,
1011d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // this could potentially become part of MobileState for simplification/complication
1012d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // of code.
1013d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private IccCardConstants.State mSimState = IccCardConstants.State.READY;
1014d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
1015d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private int mDataState = TelephonyManager.DATA_DISCONNECTED;
1016d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private ServiceState mServiceState;
1017d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private SignalStrength mSignalStrength;
1018d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private MobileIconGroup mDefaultIcons;
10193aaabd71242bf8aedb8edb87565943460a79226dJason Monk        private Config mConfig;
1020d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1021d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
1022d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // need listener lists anymore.
1023d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public MobileSignalController(Context context, Config config, boolean hasMobileData,
1024d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                TelephonyManager phone, List<NetworkSignalChangedCallback> signalCallbacks,
1025b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                List<SignalCluster> signalClusters, NetworkControllerImpl networkController,
1026b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                SubscriptionInfo info) {
1027b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
1028403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti                    NetworkCapabilities.TRANSPORT_CELLULAR, signalCallbacks, signalClusters,
1029b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    networkController);
10303aaabd71242bf8aedb8edb87565943460a79226dJason Monk            mNetworkToIconLookup = new SparseArray<>();
1031d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mConfig = config;
1032d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mPhone = phone;
1033b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mSubscriptionInfo = info;
1034b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId());
1035d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
1036d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkNameDefault = getStringIfExists(
1037d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    com.android.internal.R.string.lockscreen_carrier_default);
1038d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1039d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mapIconSets();
1040d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1041d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault;
1042d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mLastState.enabled = mCurrentState.enabled = hasMobileData;
1043d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
1044b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Get initial data sim state.
1045b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            updateDataSim();
1046d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1047d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
10483aaabd71242bf8aedb8edb87565943460a79226dJason Monk        public void setConfiguration(Config config) {
10493aaabd71242bf8aedb8edb87565943460a79226dJason Monk            mConfig = config;
10503aaabd71242bf8aedb8edb87565943460a79226dJason Monk            mapIconSets();
10513aaabd71242bf8aedb8edb87565943460a79226dJason Monk            updateTelephony();
10523aaabd71242bf8aedb8edb87565943460a79226dJason Monk        }
10533aaabd71242bf8aedb8edb87565943460a79226dJason Monk
1054d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1055d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Get (the mobile parts of) the carrier string.
1056d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         *
1057d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * @param currentLabel can be used for concatenation, currently just empty
1058d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * @param connected whether the device has connection to the internet at all
1059d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * @param isMobileLabel whether to always return the network or just when data is connected
1060d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1061d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public String getLabel(String currentLabel, boolean connected, boolean isMobileLabel) {
1062d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (!mCurrentState.enabled) {
1063d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return "";
1064d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1065d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                String mobileLabel = "";
1066d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // We want to show the carrier name if in service and either:
1067d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // - We are connected to mobile data, or
1068d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // - We are not connected to mobile data, as long as the *reason* packets are not
1069d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                //   being routed over that link is that we have better connectivity via wifi.
1070d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
1071d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // is connected, we show nothing.
1072d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // Otherwise (nothing connected) we show "No internet connection".
1073d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (mCurrentState.dataConnected) {
1074d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mobileLabel = mCurrentState.networkName;
1075d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                } else if (connected || mCurrentState.isEmergency) {
1076d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    if (mCurrentState.connected || mCurrentState.isEmergency) {
1077d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        // The isEmergencyOnly test covers the case of a phone with no SIM
1078d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        mobileLabel = mCurrentState.networkName;
1079d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    }
1080d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                } else {
1081b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mobileLabel = mContext.getString(
1082b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            R.string.status_bar_settings_signal_meter_disconnected);
1083af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
1084d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1085b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                if (currentLabel.length() != 0) {
1086b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    currentLabel = currentLabel + mNetworkNameSeparator;
1087b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
1088d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // Now for things that should only be shown when actually using mobile data.
1089d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (isMobileLabel) {
1090b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    return currentLabel + mobileLabel;
1091af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                } else {
1092b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    return currentLabel
1093b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            + (mCurrentState.dataConnected ? mobileLabel : currentLabel);
1094af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
1095af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1096af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1097af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1098d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public int getDataContentDescription() {
1099d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return getIcons().mDataContentDescription;
1100d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1101af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1102d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void setAirplaneMode(boolean airplaneMode) {
1103d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.airplaneMode = airplaneMode;
1104d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyListenersIfNecessary();
1105af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1106af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1107d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void setInetCondition(int inetCondition, int inetConditionForNetwork) {
1108d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // For mobile data, use general inet condition for phone signal indexing,
1109d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // and network specific for data indexing (I think this might be a bug, but
1110d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // keeping for now).
1111d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // TODO: Update with explanation of why.
1112d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.inetForNetwork = inetConditionForNetwork;
1113d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            setInetCondition(inetCondition);
1114af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1115d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1116d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1117d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Start listening for phone state changes.
1118d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1119d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void registerListener() {
1120d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mPhone.listen(mPhoneStateListener,
1121d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    PhoneStateListener.LISTEN_SERVICE_STATE
1122d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
1123d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            | PhoneStateListener.LISTEN_CALL_STATE
1124d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
1125d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            | PhoneStateListener.LISTEN_DATA_ACTIVITY);
1126af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1127af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1128d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1129d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Stop listening for phone state changes.
1130d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1131d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void unregisterListener() {
1132d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mPhone.listen(mPhoneStateListener, 0);
1133d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1134af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1135d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1136d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Produce a mapping of data network types to icon groups for simple and quick use in
1137d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * updateTelephony.
1138d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1139d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private void mapIconSets() {
11403aaabd71242bf8aedb8edb87565943460a79226dJason Monk            mNetworkToIconLookup.clear();
1141d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1142d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
1143d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
1144d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
1145d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
1146d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
1147af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1148b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (!mConfig.showAtLeast3G) {
1149d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
1150d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        TelephonyIcons.UNKNOWN);
1151d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
1152d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
1153d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
1154d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1155d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDefaultIcons = TelephonyIcons.G;
1156af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            } else {
1157d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
1158d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        TelephonyIcons.THREE_G);
1159d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
1160d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        TelephonyIcons.THREE_G);
1161d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
1162d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        TelephonyIcons.THREE_G);
1163d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
1164d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        TelephonyIcons.THREE_G);
1165d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDefaultIcons = TelephonyIcons.THREE_G;
1166af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1167af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1168d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            MobileIconGroup hGroup = TelephonyIcons.THREE_G;
1169d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mConfig.hspaDataDistinguishable) {
1170d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                hGroup = TelephonyIcons.H;
1171d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1172d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
1173d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
1174d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
1175d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup);
1176af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1177d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mConfig.show4gForLte) {
1178d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
1179d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1180d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
1181d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1182af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1183af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1184d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @Override
1185d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void notifyListeners() {
1186d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            MobileIconGroup icons = getIcons();
1187d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1188d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String contentDescription = getStringIfExists(getContentDescription());
1189d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
1190ccda9966112b759a8bbc01f31046fe42be96b531Jeff Davidson
1191ccda9966112b759a8bbc01f31046fe42be96b531Jeff Davidson            boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
1192ccda9966112b759a8bbc01f31046fe42be96b531Jeff Davidson                    || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
1193ccda9966112b759a8bbc01f31046fe42be96b531Jeff Davidson
1194b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Only send data sim callbacks to QS.
1195b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (mCurrentState.dataSim) {
1196ccda9966112b759a8bbc01f31046fe42be96b531Jeff Davidson                int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
1197b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                int length = mSignalsChangedCallbacks.size();
1198b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                for (int i = 0; i < length; i++) {
1199b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
1200b36becff0831e48e208495a2c4ccf1a7330aeb58Jason Monk                            && !mCurrentState.isEmergency,
1201b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            getQsCurrentIconId(), contentDescription,
1202b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            qsTypeIcon,
1203b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            mCurrentState.dataConnected && mCurrentState.activityIn,
1204b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            mCurrentState.dataConnected && mCurrentState.activityOut,
1205b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            dataContentDescription,
1206b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            mCurrentState.isEmergency ? null : mCurrentState.networkName,
1207b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            // Only wide if actually showing something.
1208b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                            icons.mIsWide && qsTypeIcon != 0);
1209b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                }
1210d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1211d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int typeIcon = showDataIcon ? icons.mDataType : 0;
1212d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int signalClustersLength = mSignalClusters.size();
1213d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            for (int i = 0; i < signalClustersLength; i++) {
1214d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSignalClusters.get(i).setMobileDataIndicators(
1215d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        mCurrentState.enabled && !mCurrentState.airplaneMode,
1216d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        getCurrentIconId(),
1217d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        typeIcon,
1218d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        contentDescription,
1219d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        dataContentDescription,
1220d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        // Only wide if actually showing something.
1221b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        icons.mIsWide && typeIcon != 0,
1222b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        mSubscriptionInfo.getSubscriptionId());
1223d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1224af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1225af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1226d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @Override
1227b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        protected MobileState cleanState() {
1228d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return new MobileState();
1229d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1230d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1231d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private boolean hasService() {
1232d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mServiceState != null) {
1233d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // Consider the device to be in service if either voice or data
1234d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // service is available. Some SIM cards are marketed as data-only
1235d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // and do not support voice service, and on these SIM cards, we
1236d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // want to show signal bars for data service as well as the "no
1237d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // service" or "emergency calls only" text that indicates that voice
1238d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // is not available.
1239d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                switch (mServiceState.getVoiceRegState()) {
1240d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    case ServiceState.STATE_POWER_OFF:
1241d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        return false;
1242d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    case ServiceState.STATE_OUT_OF_SERVICE:
1243d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    case ServiceState.STATE_EMERGENCY_ONLY:
1244d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
1245d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    default:
1246d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        return true;
1247d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1248d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1249d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return false;
1250d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1251af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1252af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1253d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private boolean isCdma() {
1254d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return (mSignalStrength != null) && !mSignalStrength.isGsm();
1255af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1256af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1257d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public boolean isEmergencyOnly() {
1258d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return (mServiceState != null && mServiceState.isEmergencyOnly());
1259d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1260af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1261d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private boolean isRoaming() {
1262d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (isCdma()) {
1263d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                final int iconMode = mServiceState.getCdmaEriIconMode();
1264d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
1265d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
1266d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
1267d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1268d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return mServiceState != null && mServiceState.getRoaming();
1269d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1270d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1271af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1272d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void handleBroadcast(Intent intent) {
1273d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String action = intent.getAction();
1274b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
1275d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
1276d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
1277d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
1278d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
1279d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                notifyListenersIfNecessary();
1280b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
1281b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                updateDataSim();
1282d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1283d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1284af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1285b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        private void updateDataSim() {
1286b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            int defaultDataSub = SubscriptionManager.getDefaultDataSubId();
1287a54bf6583380cf328b9858d942b4c387c6163f0fWink Saville            if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) {
1288b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId();
1289af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            } else {
1290b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // There doesn't seem to be a data sim selected, however if
1291b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // there isn't a MobileSignalController with dataSim set, then
1292b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // QS won't get any callbacks and will be blank.  Instead
1293b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // lets just assume we are the data sim (which will basically
1294b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // show one at random) in QS until one is selected.  The user
1295b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // should pick one soon after, so we shouldn't be in this state
1296b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                // for long.
1297b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mCurrentState.dataSim = true;
1298af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1299b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            notifyListenersIfNecessary();
1300d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1301af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1302d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1303d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Updates the network's name based on incoming spn and plmn.
1304d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1305d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
1306d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (CHATTY) {
1307d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
1308d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        + " showPlmn=" + showPlmn + " plmn=" + plmn);
1309d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1310d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            StringBuilder str = new StringBuilder();
1311d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (showPlmn && plmn != null) {
1312d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                str.append(plmn);
1313d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1314d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (showSpn && spn != null) {
1315d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (str.length() != 0) {
1316d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    str.append(mNetworkNameSeparator);
1317d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1318d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                str.append(spn);
1319d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1320d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (str.length() != 0) {
1321d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.networkName = str.toString();
1322d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1323d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.networkName = mNetworkNameDefault;
1324af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1325af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1326af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1327d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1328d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
1329d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * mDataState, and mSimState.  It should be called any time one of these is updated.
1330d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * This will call listeners if necessary.
1331d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1332d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final void updateTelephony() {
1333d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (DEBUG) {
1334d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                Log.d(TAG, "updateTelephonySignalStrength: hasService=" + hasService()
1335d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        + " ss=" + mSignalStrength);
1336d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1337d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.connected = hasService() && mSignalStrength != null;
1338d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mCurrentState.connected) {
1339d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
1340d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mCurrentState.level = mSignalStrength.getCdmaLevel();
1341d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                } else {
1342d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mCurrentState.level = mSignalStrength.getLevel();
1343d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1344d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
13453aaabd71242bf8aedb8edb87565943460a79226dJason Monk            if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
1346d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
1347af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            } else {
1348d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.iconGroup = mDefaultIcons;
1349d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1350d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.dataConnected = mCurrentState.connected
1351d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    && mDataState == TelephonyManager.DATA_CONNECTED;
1352af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1353d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (isRoaming()) {
1354d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.iconGroup = TelephonyIcons.ROAMING;
1355d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1356d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (isEmergencyOnly() != mCurrentState.isEmergency) {
1357d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mCurrentState.isEmergency = isEmergencyOnly();
1358d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkController.recalculateEmergency();
1359af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1360b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            // Fill in the network name if we think we have it.
1361b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
1362b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    && mServiceState.getOperatorAlphaShort() != null) {
1363b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
1364b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
1365d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyListenersIfNecessary();
1366af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1367af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1368d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @VisibleForTesting
1369d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setActivity(int activity) {
1370d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
1371d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    || activity == TelephonyManager.DATA_ACTIVITY_IN;
1372d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
1373d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    || activity == TelephonyManager.DATA_ACTIVITY_OUT;
1374d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyListenersIfNecessary();
1375af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1376af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1377d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        @Override
1378d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void dump(PrintWriter pw) {
1379d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            super.dump(pw);
1380b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            pw.println("  mSubscription=" + mSubscriptionInfo + ",");
1381d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            pw.println("  mServiceState=" + mServiceState + ",");
1382d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            pw.println("  mSignalStrength=" + mSignalStrength + ",");
1383d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            pw.println("  mDataState=" + mDataState + ",");
1384d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            pw.println("  mDataNetType=" + mDataNetType + ",");
1385af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1386af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1387b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        class MobilePhoneStateListener extends PhoneStateListener {
1388b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            public MobilePhoneStateListener(int subId) {
1389b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                super(subId);
1390b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            }
1391b574627ebc383d35cf8212ee241b191b26518a0dJason Monk
1392d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1393d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void onSignalStrengthsChanged(SignalStrength signalStrength) {
1394d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (DEBUG) {
1395b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +
1396d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
1397af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
1398d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSignalStrength = signalStrength;
1399d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                updateTelephony();
1400af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1401af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1402d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1403d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void onServiceStateChanged(ServiceState state) {
1404d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (DEBUG) {
1405b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
1406d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            + " dataState=" + state.getDataRegState());
1407d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1408d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mServiceState = state;
1409d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                updateTelephony();
1410d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1411af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1412d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1413d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void onDataConnectionStateChanged(int state, int networkType) {
1414d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (DEBUG) {
1415b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    Log.d(mTag, "onDataConnectionStateChanged: state=" + state
1416d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            + " type=" + networkType);
1417d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1418d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDataState = state;
1419d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDataNetType = networkType;
1420d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                updateTelephony();
1421d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
142223dd176915fc118563e510647f048589c12e78e5Lorenzo Colitti
1423d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1424d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void onDataActivity(int direction) {
1425d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (DEBUG) {
1426b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                    Log.d(mTag, "onDataActivity: direction=" + direction);
1427d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1428d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                setActivity(direction);
1429d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1430d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        };
1431d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1432d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        static class MobileIconGroup extends SignalController.IconGroup {
1433d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mDataContentDescription; // mContentDescriptionDataType
1434d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mDataType;
1435d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final boolean mIsWide;
1436d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int[] mQsDataType;
1437d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1438d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
1439d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
1440d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    int discContentDesc, int dataContentDesc, int dataType, boolean isWide,
1441d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    int[] qsDataType) {
1442d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
1443d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        qsDiscState, discContentDesc);
1444d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDataContentDescription = dataContentDesc;
1445d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDataType = dataType;
1446d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mIsWide = isWide;
1447d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mQsDataType = qsDataType;
1448af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1449af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1450af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1451d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        static class MobileState extends SignalController.State {
1452d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            String networkName;
1453b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            boolean dataSim;
1454d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean dataConnected;
1455d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean isEmergency;
1456d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean airplaneMode;
1457d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int inetForNetwork;
1458af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1459d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1460d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void copyFrom(State s) {
1461b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                super.copyFrom(s);
1462d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                MobileState state = (MobileState) s;
1463b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                dataSim = state.dataSim;
1464d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                networkName = state.networkName;
1465d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                dataConnected = state.dataConnected;
1466d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                inetForNetwork = state.inetForNetwork;
1467d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                isEmergency = state.isEmergency;
1468d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                airplaneMode = state.airplaneMode;
1469af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1470af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1471d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1472d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            protected void toString(StringBuilder builder) {
1473b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                super.toString(builder);
1474b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                builder.append(',');
1475b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                builder.append("dataSim=").append(dataSim).append(',');
1476d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                builder.append("networkName=").append(networkName).append(',');
1477d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                builder.append("dataConnected=").append(dataConnected).append(',');
1478d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                builder.append("inetForNetwork=").append(inetForNetwork).append(',');
1479d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                builder.append("isEmergency=").append(isEmergency).append(',');
1480b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                builder.append("airplaneMode=").append(airplaneMode);
1481d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1482af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1483d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1484d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public boolean equals(Object o) {
1485d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return super.equals(o)
1486d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && Objects.equals(((MobileState) o).networkName, networkName)
1487b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                        && ((MobileState) o).dataSim == dataSim
1488d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && ((MobileState) o).dataConnected == dataConnected
1489d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && ((MobileState) o).isEmergency == isEmergency
1490d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && ((MobileState) o).airplaneMode == airplaneMode
1491d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && ((MobileState) o).inetForNetwork == inetForNetwork;
1492d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1493af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1494d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
1495af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1496d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    /**
1497d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     * Common base class for handling signal for both wifi and mobile data.
1498d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk     */
1499d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static abstract class SignalController<T extends SignalController.State,
1500d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            I extends SignalController.IconGroup> {
1501d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final String mTag;
1502d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final T mCurrentState;
1503d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final T mLastState;
1504403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        protected final int mTransportType;
1505d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final Context mContext;
1506d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // The owner of the SignalController (i.e. NetworkController will maintain the following
1507d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // lists and call notifyListeners whenever the list has changed to ensure everyone
1508d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // is aware of current state.
1509d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks;
1510d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final List<SignalCluster> mSignalClusters;
1511d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected final NetworkControllerImpl mNetworkController;
1512d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1513d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // Save the previous HISTORY_SIZE states for logging.
1514d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private final State[] mHistory;
1515d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        // Where to copy the next state into.
1516d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        private int mHistoryIndex;
1517d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1518d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public SignalController(String tag, Context context, int type,
1519d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                List<NetworkSignalChangedCallback> signalCallbacks,
1520d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
1521b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            mTag = TAG + "." + tag;
1522d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mNetworkController = networkController;
1523403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            mTransportType = type;
1524d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mContext = context;
1525d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mSignalsChangedCallbacks = signalCallbacks;
1526d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mSignalClusters = signalClusters;
1527d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState = cleanState();
1528d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mLastState = cleanState();
1529d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (RECORD_HISTORY) {
1530d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mHistory = new State[HISTORY_SIZE];
1531d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                for (int i = 0; i < HISTORY_SIZE; i++) {
1532d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    mHistory[i] = cleanState();
1533d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1534d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1535af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1536af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1537d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public T getState() {
1538d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return mCurrentState;
1539af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1540af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1541403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti        public int getTransportType() {
1542403aa2684e0e93b4792aabc0bbe1f32ac5e417afLorenzo Colitti            return mTransportType;
1543af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1544af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1545d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void setInetCondition(int inetCondition) {
1546d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.inetCondition = inetCondition;
1547d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            notifyListenersIfNecessary();
1548af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1549af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1550d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1551d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Used at the end of demo mode to clear out any ugly state that it has created.
1552d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Since we haven't had any callbacks, then isDirty will not have been triggered,
1553d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * so we can just take the last good state directly from there.
1554b574627ebc383d35cf8212ee241b191b26518a0dJason Monk         *
1555b574627ebc383d35cf8212ee241b191b26518a0dJason Monk         * Used for demo mode.
1556d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1557d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void resetLastState() {
1558d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.copyFrom(mLastState);
155923dd176915fc118563e510647f048589c12e78e5Lorenzo Colitti        }
156023dd176915fc118563e510647f048589c12e78e5Lorenzo Colitti
1561d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1562d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Determines if the state of this signal controller has changed and
1563d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * needs to trigger callbacks related to it.
1564d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1565d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public boolean isDirty() {
1566d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (!mLastState.equals(mCurrentState)) {
1567d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (DEBUG) {
1568d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    Log.d(mTag, "Change in state from: " + mLastState + "\n"
1569d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            + "\tto: " + mCurrentState);
1570d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1571d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return true;
1572d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1573d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return false;
1574af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1575af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1576d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void saveLastState() {
1577d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (RECORD_HISTORY) {
1578b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                recordLastState();
1579d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1580d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // Updates the current time.
1581d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mCurrentState.time = System.currentTimeMillis();
1582d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mLastState.copyFrom(mCurrentState);
1583d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1584d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1585d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1586d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Gets the signal icon for QS based on current state of connected, enabled, and level.
1587d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1588d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public int getQsCurrentIconId() {
1589d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mCurrentState.connected) {
1590d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level];
1591d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else if (mCurrentState.enabled) {
1592d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mQsDiscState;
1593d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1594d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mQsNullState;
1595d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1596af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1597af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1598d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1599d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Gets the signal icon for SB based on current state of connected, enabled, and level.
1600d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1601d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public int getCurrentIconId() {
1602d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mCurrentState.connected) {
1603d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
1604d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else if (mCurrentState.enabled) {
1605d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mSbDiscState;
1606d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            } else {
1607d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mSbNullState;
1608af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1609af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1610af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1611d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1612b574627ebc383d35cf8212ee241b191b26518a0dJason Monk         * Gets the content description id for the signal based on current state of connected and
1613d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * level.
1614d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1615d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public int getContentDescription() {
1616d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (mCurrentState.connected) {
1617d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mContentDesc[mCurrentState.level];
1618af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            } else {
1619d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return getIcons().mDiscContentDesc;
1620af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1621af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1622af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1623b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        public void notifyListenersIfNecessary() {
1624d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (isDirty()) {
1625d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                saveLastState();
1626d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                notifyListeners();
1627d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mNetworkController.refreshCarrierLabel();
1628af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1629af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1630af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1631d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1632d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Returns the resource if resId is not 0, and an empty string otherwise.
1633d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1634d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected String getStringIfExists(int resId) {
1635d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return resId != 0 ? mContext.getString(resId) : "";
1636af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1637af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1638d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        protected I getIcons() {
1639d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return (I) mCurrentState.iconGroup;
1640af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1641af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1642d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1643d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Saves the last state of any changes, so we can log the current
1644d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * and last value of any state data.
1645d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1646b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        protected void recordLastState() {
1647d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
1648d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1649af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1650d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public void dump(PrintWriter pw) {
1651d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            pw.println("  - " + mTag + " -----");
1652d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            pw.println("  Current State: " + mCurrentState);
1653d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            if (RECORD_HISTORY) {
1654d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // Count up the states that actually contain time stamps, and only display those.
1655d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                int size = 0;
1656d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                for (int i = 0; i < HISTORY_SIZE; i++) {
1657d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    if (mHistory[i].time != 0) size++;
1658d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1659d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                // Print out the previous states in ordered number.
1660d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                for (int i = mHistoryIndex + HISTORY_SIZE - 1;
1661d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
1662d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    pw.println("  Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + ": "
1663d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                            + mHistory[i & (HISTORY_SIZE - 1)]);
1664d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                }
1665d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1666d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1667af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1668d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1669d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Trigger callbacks based on current state.  The callbacks should be completely
1670d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * based on current state, and only need to be called in the scenario where
1671d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * mCurrentState != mLastState.
1672d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1673d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        public abstract void notifyListeners();
1674d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1675d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /**
1676d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Generate a blank T.
1677d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1678b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        protected abstract T cleanState();
1679d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1680d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        /*
1681d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * Holds icons for a given state. Arrays are generally indexed as inet
1682d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * state (full connectivity or not) first, and second dimension as
1683d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         * signal strength.
1684d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk         */
1685d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        static class IconGroup {
1686d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int[][] mSbIcons;
1687d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int[][] mQsIcons;
1688d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int[] mContentDesc;
1689d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mSbNullState;
1690d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mQsNullState;
1691d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mSbDiscState;
1692d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mQsDiscState;
1693d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final int mDiscContentDesc;
1694d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // For logging.
1695d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            final String mName;
1696d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1697d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
1698d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
1699d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    int discContentDesc) {
1700d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mName = name;
1701d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSbIcons = sbIcons;
1702d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mQsIcons = qsIcons;
1703d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mContentDesc = contentDesc;
1704d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSbNullState = sbNullState;
1705d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mQsNullState = qsNullState;
1706d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mSbDiscState = sbDiscState;
1707d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mQsDiscState = qsDiscState;
1708d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                mDiscContentDesc = discContentDesc;
1709d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            }
1710af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1711d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1712d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public String toString() {
1713d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return "IconGroup(" + mName + ")";
1714af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1715af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1716af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
1717d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        static class State {
1718d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean connected;
1719d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean enabled;
1720d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean activityIn;
1721d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            boolean activityOut;
1722d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int level;
1723d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            IconGroup iconGroup;
1724d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int inetCondition;
1725d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            int rssi; // Only for logging.
1726d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1727d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            // Not used for comparison, just used for logging.
1728d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            long time;
1729d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1730d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public void copyFrom(State state) {
1731d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                connected = state.connected;
1732d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                enabled = state.enabled;
1733d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                level = state.level;
1734d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                iconGroup = state.iconGroup;
1735d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                inetCondition = state.inetCondition;
1736d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                activityIn = state.activityIn;
1737d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                activityOut = state.activityOut;
1738d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                rssi = state.rssi;
1739d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                time = state.time;
1740af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1741d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1742d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1743d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public String toString() {
1744d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (time != 0) {
1745d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    StringBuilder builder = new StringBuilder();
1746d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    toString(builder);
1747d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    return builder.toString();
1748d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                } else {
1749d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    return "Empty " + getClass().getSimpleName();
1750af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
1751af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1752d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1753d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            protected void toString(StringBuilder builder) {
1754d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                builder.append("connected=").append(connected).append(',')
1755d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("enabled=").append(enabled).append(',')
1756d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("level=").append(level).append(',')
1757d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("inetCondition=").append(inetCondition).append(',')
1758d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("iconGroup=").append(iconGroup).append(',')
1759d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("activityIn=").append(activityIn).append(',')
1760d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("activityOut=").append(activityOut).append(',')
1761d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("rssi=").append(rssi).append(',')
1762d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time));
1763af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1764d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1765d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            @Override
1766d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            public boolean equals(Object o) {
1767d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                if (!o.getClass().equals(getClass())) {
1768d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    return false;
1769af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                }
1770d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                State other = (State) o;
1771d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                return other.connected == connected
1772d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.enabled == enabled
1773d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.level == level
1774d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.inetCondition == inetCondition
1775d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.iconGroup == iconGroup
1776d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.activityIn == activityIn
1777d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.activityOut == activityOut
1778d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                        && other.rssi == rssi;
1779af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
1780af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1781af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
1782d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1783d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public interface SignalCluster {
1784d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
1785d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1786d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
1787b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                String contentDescription, String typeContentDescription, boolean isTypeIconWide,
1788b574627ebc383d35cf8212ee241b191b26518a0dJason Monk                int subId);
1789b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        void setSubs(List<SubscriptionInfo> subs);
1790b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        void setNoSims(boolean show);
1791d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1792d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription);
1793d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
1794d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1795d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public interface EmergencyListener {
1796d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setEmergencyCallsOnly(boolean emergencyOnly);
1797d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
1798d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1799d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    public interface CarrierLabelListener {
1800d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        void setCarrierLabel(String label);
1801d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
1802d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1803d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    @VisibleForTesting
1804d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    static class Config {
1805b574627ebc383d35cf8212ee241b191b26518a0dJason Monk        boolean showAtLeast3G = false;
1806d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        boolean alwaysShowCdmaRssi = false;
1807d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        boolean show4gForLte = false;
1808d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        boolean hspaDataDistinguishable;
1809d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1810d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        static Config readConfig(Context context) {
1811d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            Config config = new Config();
1812d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            Resources res = context.getResources();
1813d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk
1814b574627ebc383d35cf8212ee241b191b26518a0dJason Monk            config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
1815d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            config.alwaysShowCdmaRssi =
1816d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
1817d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
1818d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            config.hspaDataDistinguishable =
1819d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk                    res.getBoolean(R.bool.config_hspa_data_distinguishable);
1820d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk            return config;
1821d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk        }
1822d2263cd9db36d5f74a124c5ffaf8594ef702408fJason Monk    }
1823af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock}
1824