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