1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar.policy;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.content.res.Resources;
24import android.net.ConnectivityManager;
25import android.net.NetworkCapabilities;
26import android.net.wifi.WifiManager;
27import android.os.AsyncTask;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.Looper;
31import android.provider.Settings;
32import android.telephony.ServiceState;
33import android.telephony.SubscriptionInfo;
34import android.telephony.SubscriptionManager;
35import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
36import android.telephony.TelephonyManager;
37import android.text.TextUtils;
38import android.util.Log;
39import android.util.MathUtils;
40
41import com.android.internal.annotations.VisibleForTesting;
42import com.android.internal.telephony.PhoneConstants;
43import com.android.internal.telephony.TelephonyIntents;
44import com.android.settingslib.net.DataUsageController;
45import com.android.systemui.DemoMode;
46import com.android.systemui.R;
47
48import java.io.FileDescriptor;
49import java.io.PrintWriter;
50import java.util.ArrayList;
51import java.util.BitSet;
52import java.util.Collections;
53import java.util.Comparator;
54import java.util.HashMap;
55import java.util.List;
56import java.util.Locale;
57import java.util.Map;
58
59import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
60
61/** Platform implementation of the network controller. **/
62public class NetworkControllerImpl extends BroadcastReceiver
63        implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider {
64    // debug
65    static final String TAG = "NetworkController";
66    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
67    // additional diagnostics, but not logspew
68    static final boolean CHATTY =  Log.isLoggable(TAG + "Chat", Log.DEBUG);
69
70    private static final int EMERGENCY_NO_CONTROLLERS = 0;
71    private static final int EMERGENCY_FIRST_CONTROLLER = 100;
72    private static final int EMERGENCY_VOICE_CONTROLLER = 200;
73    private static final int EMERGENCY_NO_SUB = 300;
74
75    private final Context mContext;
76    private final TelephonyManager mPhone;
77    private final WifiManager mWifiManager;
78    private final ConnectivityManager mConnectivityManager;
79    private final SubscriptionManager mSubscriptionManager;
80    private final boolean mHasMobileDataFeature;
81    private final SubscriptionDefaults mSubDefaults;
82    private final DataSaverController mDataSaverController;
83    private Config mConfig;
84
85    // Subcontrollers.
86    @VisibleForTesting
87    final WifiSignalController mWifiSignalController;
88
89    @VisibleForTesting
90    final EthernetSignalController mEthernetSignalController;
91
92    @VisibleForTesting
93    final Map<Integer, MobileSignalController> mMobileSignalControllers =
94            new HashMap<Integer, MobileSignalController>();
95    // When no SIMs are around at setup, and one is added later, it seems to default to the first
96    // SIM for most actions.  This may be null if there aren't any SIMs around.
97    private MobileSignalController mDefaultSignalController;
98    private final AccessPointControllerImpl mAccessPoints;
99    private final DataUsageController mDataUsageController;
100
101    private boolean mInetCondition; // Used for Logging and demo.
102
103    // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are
104    // connected and validated, respectively.
105    private final BitSet mConnectedTransports = new BitSet();
106    private final BitSet mValidatedTransports = new BitSet();
107
108    // States that don't belong to a subcontroller.
109    private boolean mAirplaneMode = false;
110    private boolean mHasNoSims;
111    private Locale mLocale = null;
112    // This list holds our ordering.
113    private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
114
115    @VisibleForTesting
116    boolean mListening;
117
118    // The current user ID.
119    private int mCurrentUserId;
120
121    private OnSubscriptionsChangedListener mSubscriptionListener;
122
123    // Handler that all broadcasts are received on.
124    private final Handler mReceiverHandler;
125    // Handler that all callbacks are made on.
126    private final CallbackHandler mCallbackHandler;
127
128    private int mEmergencySource;
129    private boolean mIsEmergency;
130
131    @VisibleForTesting
132    ServiceState mLastServiceState;
133    private boolean mUserSetup;
134
135    /**
136     * Construct this controller object and register for updates.
137     */
138    public NetworkControllerImpl(Context context, Looper bgLooper) {
139        this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
140                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
141                (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
142                SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
143                new CallbackHandler(),
144                new AccessPointControllerImpl(context, bgLooper),
145                new DataUsageController(context),
146                new SubscriptionDefaults());
147        mReceiverHandler.post(mRegisterListeners);
148    }
149
150    @VisibleForTesting
151    NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
152            TelephonyManager telephonyManager, WifiManager wifiManager,
153            SubscriptionManager subManager, Config config, Looper bgLooper,
154            CallbackHandler callbackHandler,
155            AccessPointControllerImpl accessPointController,
156            DataUsageController dataUsageController,
157            SubscriptionDefaults defaultsHandler) {
158        mContext = context;
159        mConfig = config;
160        mReceiverHandler = new Handler(bgLooper);
161        mCallbackHandler = callbackHandler;
162        mDataSaverController = new DataSaverController(context);
163
164        mSubscriptionManager = subManager;
165        mSubDefaults = defaultsHandler;
166        mConnectivityManager = connectivityManager;
167        mHasMobileDataFeature =
168                mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
169
170        // telephony
171        mPhone = telephonyManager;
172
173        // wifi
174        mWifiManager = wifiManager;
175
176        mLocale = mContext.getResources().getConfiguration().locale;
177        mAccessPoints = accessPointController;
178        mDataUsageController = dataUsageController;
179        mDataUsageController.setNetworkController(this);
180        // TODO: Find a way to move this into DataUsageController.
181        mDataUsageController.setCallback(new DataUsageController.Callback() {
182            @Override
183            public void onMobileDataEnabled(boolean enabled) {
184                mCallbackHandler.setMobileDataEnabled(enabled);
185            }
186        });
187        mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
188                mCallbackHandler, this);
189
190        mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
191
192        // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
193        updateAirplaneMode(true /* force callback */);
194    }
195
196    public DataSaverController getDataSaverController() {
197        return mDataSaverController;
198    }
199
200    private void registerListeners() {
201        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
202            mobileSignalController.registerListener();
203        }
204        if (mSubscriptionListener == null) {
205            mSubscriptionListener = new SubListener();
206        }
207        mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
208
209        // broadcasts
210        IntentFilter filter = new IntentFilter();
211        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
212        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
213        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
214        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
215        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
216        filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
217        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
218        filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
219        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
220        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
221        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
222        mContext.registerReceiver(this, filter, null, mReceiverHandler);
223        mListening = true;
224
225        updateMobileControllers();
226    }
227
228    private void unregisterListeners() {
229        mListening = false;
230        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
231            mobileSignalController.unregisterListener();
232        }
233        mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
234        mContext.unregisterReceiver(this);
235    }
236
237    public int getConnectedWifiLevel() {
238        return mWifiSignalController.getState().level;
239    }
240
241    @Override
242    public AccessPointController getAccessPointController() {
243        return mAccessPoints;
244    }
245
246    @Override
247    public DataUsageController getMobileDataController() {
248        return mDataUsageController;
249    }
250
251    public void addEmergencyListener(EmergencyListener listener) {
252        mCallbackHandler.setListening(listener, true);
253        mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly());
254    }
255
256    public void removeEmergencyListener(EmergencyListener listener) {
257        mCallbackHandler.setListening(listener, false);
258    }
259
260    public boolean hasMobileDataFeature() {
261        return mHasMobileDataFeature;
262    }
263
264    public boolean hasVoiceCallingFeature() {
265        return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
266    }
267
268    private MobileSignalController getDataController() {
269        int dataSubId = mSubDefaults.getDefaultDataSubId();
270        if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) {
271            if (DEBUG) Log.e(TAG, "No data sim selected");
272            return mDefaultSignalController;
273        }
274        if (mMobileSignalControllers.containsKey(dataSubId)) {
275            return mMobileSignalControllers.get(dataSubId);
276        }
277        if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
278        return mDefaultSignalController;
279    }
280
281    public String getMobileDataNetworkName() {
282        MobileSignalController controller = getDataController();
283        return controller != null ? controller.getState().networkNameData : "";
284    }
285
286    public boolean isEmergencyOnly() {
287        if (mMobileSignalControllers.size() == 0) {
288            // When there are no active subscriptions, determine emengency state from last
289            // broadcast.
290            mEmergencySource = EMERGENCY_NO_CONTROLLERS;
291            return mLastServiceState != null && mLastServiceState.isEmergencyOnly();
292        }
293        int voiceSubId = mSubDefaults.getDefaultVoiceSubId();
294        if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
295            for (MobileSignalController mobileSignalController :
296                                            mMobileSignalControllers.values()) {
297                if (!mobileSignalController.getState().isEmergency) {
298                    mEmergencySource = EMERGENCY_FIRST_CONTROLLER
299                            + mobileSignalController.mSubscriptionInfo.getSubscriptionId();
300                    if (DEBUG) Log.d(TAG, "Found emergency " + mobileSignalController.mTag);
301                    return false;
302                }
303            }
304        }
305        if (mMobileSignalControllers.containsKey(voiceSubId)) {
306            mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId;
307            if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId);
308            return mMobileSignalControllers.get(voiceSubId).getState().isEmergency;
309        }
310        if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
311        mEmergencySource = EMERGENCY_NO_SUB + voiceSubId;
312        // Something is wrong, better assume we can't make calls...
313        return true;
314    }
315
316    /**
317     * Emergency status may have changed (triggered by MobileSignalController),
318     * so we should recheck and send out the state to listeners.
319     */
320    void recalculateEmergency() {
321        mIsEmergency = isEmergencyOnly();
322        mCallbackHandler.setEmergencyCallsOnly(mIsEmergency);
323    }
324
325    public void addSignalCallback(SignalCallback cb) {
326        cb.setSubs(mCurrentSubscriptions);
327        cb.setIsAirplaneMode(new IconState(mAirplaneMode,
328                TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
329        cb.setNoSims(mHasNoSims);
330        mWifiSignalController.notifyListeners(cb);
331        mEthernetSignalController.notifyListeners(cb);
332        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
333            mobileSignalController.notifyListeners(cb);
334        }
335        mCallbackHandler.setListening(cb, true);
336    }
337
338    @Override
339    public void removeSignalCallback(SignalCallback cb) {
340        mCallbackHandler.setListening(cb, false);
341    }
342
343    @Override
344    public void setWifiEnabled(final boolean enabled) {
345        new AsyncTask<Void, Void, Void>() {
346            @Override
347            protected Void doInBackground(Void... args) {
348                // Disable tethering if enabling Wifi
349                final int wifiApState = mWifiManager.getWifiApState();
350                if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
351                        (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
352                    mWifiManager.setWifiApEnabled(null, false);
353                }
354
355                mWifiManager.setWifiEnabled(enabled);
356                return null;
357            }
358        }.execute();
359    }
360
361    @Override
362    public void onUserSwitched(int newUserId) {
363        mCurrentUserId = newUserId;
364        mAccessPoints.onUserSwitched(newUserId);
365        updateConnectivity();
366    }
367
368    @Override
369    public void onReceive(Context context, Intent intent) {
370        if (CHATTY) {
371            Log.d(TAG, "onReceive: intent=" + intent);
372        }
373        final String action = intent.getAction();
374        if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
375                action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
376            updateConnectivity();
377        } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
378            refreshLocale();
379            updateAirplaneMode(false);
380        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
381            // We are using different subs now, we might be able to make calls.
382            recalculateEmergency();
383        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
384            // Notify every MobileSignalController so they can know whether they are the
385            // data sim or not.
386            for (MobileSignalController controller : mMobileSignalControllers.values()) {
387                controller.handleBroadcast(intent);
388            }
389        } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
390            // Might have different subscriptions now.
391            updateMobileControllers();
392        } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
393            mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
394            if (mMobileSignalControllers.size() == 0) {
395                // If none of the subscriptions are active, we might need to recalculate
396                // emergency state.
397                recalculateEmergency();
398            }
399        } else {
400            int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
401                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
402            if (SubscriptionManager.isValidSubscriptionId(subId)) {
403                if (mMobileSignalControllers.containsKey(subId)) {
404                    mMobileSignalControllers.get(subId).handleBroadcast(intent);
405                } else {
406                    // Can't find this subscription...  We must be out of date.
407                    updateMobileControllers();
408                }
409            } else {
410                // No sub id, must be for the wifi.
411                mWifiSignalController.handleBroadcast(intent);
412            }
413        }
414    }
415
416    public void onConfigurationChanged() {
417        mConfig = Config.readConfig(mContext);
418        mReceiverHandler.post(new Runnable() {
419            @Override
420            public void run() {
421                handleConfigurationChanged();
422            }
423        });
424    }
425
426    @VisibleForTesting
427    void handleConfigurationChanged() {
428        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
429            mobileSignalController.setConfiguration(mConfig);
430        }
431        refreshLocale();
432    }
433
434    private void updateMobileControllers() {
435        if (!mListening) {
436            return;
437        }
438        doUpdateMobileControllers();
439    }
440
441    @VisibleForTesting
442    void doUpdateMobileControllers() {
443        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
444        if (subscriptions == null) {
445            subscriptions = Collections.emptyList();
446        }
447        // If there have been no relevant changes to any of the subscriptions, we can leave as is.
448        if (hasCorrectMobileControllers(subscriptions)) {
449            // Even if the controllers are correct, make sure we have the right no sims state.
450            // Such as on boot, don't need any controllers, because there are no sims,
451            // but we still need to update the no sim state.
452            updateNoSims();
453            return;
454        }
455        setCurrentSubscriptions(subscriptions);
456        updateNoSims();
457        recalculateEmergency();
458    }
459
460    @VisibleForTesting
461    protected void updateNoSims() {
462        boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
463        if (hasNoSims != mHasNoSims) {
464            mHasNoSims = hasNoSims;
465            mCallbackHandler.setNoSims(mHasNoSims);
466        }
467    }
468
469    @VisibleForTesting
470    void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
471        Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
472            @Override
473            public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
474                return lhs.getSimSlotIndex() == rhs.getSimSlotIndex()
475                        ? lhs.getSubscriptionId() - rhs.getSubscriptionId()
476                        : lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
477            }
478        });
479        mCurrentSubscriptions = subscriptions;
480
481        HashMap<Integer, MobileSignalController> cachedControllers =
482                new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
483        mMobileSignalControllers.clear();
484        final int num = subscriptions.size();
485        for (int i = 0; i < num; i++) {
486            int subId = subscriptions.get(i).getSubscriptionId();
487            // If we have a copy of this controller already reuse it, otherwise make a new one.
488            if (cachedControllers.containsKey(subId)) {
489                mMobileSignalControllers.put(subId, cachedControllers.remove(subId));
490            } else {
491                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
492                        mHasMobileDataFeature, mPhone, mCallbackHandler,
493                        this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper());
494                controller.setUserSetupComplete(mUserSetup);
495                mMobileSignalControllers.put(subId, controller);
496                if (subscriptions.get(i).getSimSlotIndex() == 0) {
497                    mDefaultSignalController = controller;
498                }
499                if (mListening) {
500                    controller.registerListener();
501                }
502            }
503        }
504        if (mListening) {
505            for (Integer key : cachedControllers.keySet()) {
506                if (cachedControllers.get(key) == mDefaultSignalController) {
507                    mDefaultSignalController = null;
508                }
509                cachedControllers.get(key).unregisterListener();
510            }
511        }
512        mCallbackHandler.setSubs(subscriptions);
513        notifyAllListeners();
514
515        // There may be new MobileSignalControllers around, make sure they get the current
516        // inet condition and airplane mode.
517        pushConnectivityToSignals();
518        updateAirplaneMode(true /* force */);
519    }
520
521    public void setUserSetupComplete(final boolean userSetup) {
522        mReceiverHandler.post(new Runnable() {
523            @Override
524            public void run() {
525                handleSetUserSetupComplete(userSetup);
526            }
527        });
528    }
529
530    @VisibleForTesting
531    void handleSetUserSetupComplete(boolean userSetup) {
532        mUserSetup = userSetup;
533        for (MobileSignalController controller : mMobileSignalControllers.values()) {
534            controller.setUserSetupComplete(mUserSetup);
535        }
536    }
537
538    @VisibleForTesting
539    boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
540        if (allSubscriptions.size() != mMobileSignalControllers.size()) {
541            return false;
542        }
543        for (SubscriptionInfo info : allSubscriptions) {
544            if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) {
545                return false;
546            }
547        }
548        return true;
549    }
550
551    private void updateAirplaneMode(boolean force) {
552        boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
553                Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
554        if (airplaneMode != mAirplaneMode || force) {
555            mAirplaneMode = airplaneMode;
556            for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
557                mobileSignalController.setAirplaneMode(mAirplaneMode);
558            }
559            notifyListeners();
560        }
561    }
562
563    private void refreshLocale() {
564        Locale current = mContext.getResources().getConfiguration().locale;
565        if (!current.equals(mLocale)) {
566            mLocale = current;
567            notifyAllListeners();
568        }
569    }
570
571    /**
572     * Forces update of all callbacks on both SignalClusters and
573     * NetworkSignalChangedCallbacks.
574     */
575    private void notifyAllListeners() {
576        notifyListeners();
577        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
578            mobileSignalController.notifyListeners();
579        }
580        mWifiSignalController.notifyListeners();
581        mEthernetSignalController.notifyListeners();
582    }
583
584    /**
585     * Notifies listeners of changes in state of to the NetworkController, but
586     * does not notify for any info on SignalControllers, for that call
587     * notifyAllListeners.
588     */
589    private void notifyListeners() {
590        mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
591                TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
592        mCallbackHandler.setNoSims(mHasNoSims);
593    }
594
595    /**
596     * Update the Inet conditions and what network we are connected to.
597     */
598    private void updateConnectivity() {
599        mConnectedTransports.clear();
600        mValidatedTransports.clear();
601        for (NetworkCapabilities nc :
602                mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
603            for (int transportType : nc.getTransportTypes()) {
604                mConnectedTransports.set(transportType);
605                if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
606                    mValidatedTransports.set(transportType);
607                }
608            }
609        }
610
611        if (CHATTY) {
612            Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
613            Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
614        }
615
616        mInetCondition = !mValidatedTransports.isEmpty();
617
618        pushConnectivityToSignals();
619    }
620
621    /**
622     * Pushes the current connectivity state to all SignalControllers.
623     */
624    private void pushConnectivityToSignals() {
625        // We want to update all the icons, all at once, for any condition change
626        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
627            mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
628        }
629        mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
630        mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
631    }
632
633    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
634        pw.println("NetworkController state:");
635
636        pw.println("  - telephony ------");
637        pw.print("  hasVoiceCallingFeature()=");
638        pw.println(hasVoiceCallingFeature());
639
640        pw.println("  - connectivity ------");
641        pw.print("  mConnectedTransports=");
642        pw.println(mConnectedTransports);
643        pw.print("  mValidatedTransports=");
644        pw.println(mValidatedTransports);
645        pw.print("  mInetCondition=");
646        pw.println(mInetCondition);
647        pw.print("  mAirplaneMode=");
648        pw.println(mAirplaneMode);
649        pw.print("  mLocale=");
650        pw.println(mLocale);
651        pw.print("  mLastServiceState=");
652        pw.println(mLastServiceState);
653        pw.print("  mIsEmergency=");
654        pw.println(mIsEmergency);
655        pw.print("  mEmergencySource=");
656        pw.println(emergencyToString(mEmergencySource));
657
658        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
659            mobileSignalController.dump(pw);
660        }
661        mWifiSignalController.dump(pw);
662
663        mEthernetSignalController.dump(pw);
664
665        mAccessPoints.dump(pw);
666    }
667
668    private static final String emergencyToString(int emergencySource) {
669        if (emergencySource > EMERGENCY_NO_SUB) {
670            return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")";
671        } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) {
672            return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")";
673        } else if (emergencySource > EMERGENCY_FIRST_CONTROLLER) {
674            return "FIRST_CONTROLLER(" + (emergencySource - EMERGENCY_FIRST_CONTROLLER) + ")";
675        } else if (emergencySource == EMERGENCY_NO_CONTROLLERS) {
676            return "NO_CONTROLLERS";
677        }
678        return "UNKNOWN_SOURCE";
679    }
680
681    private boolean mDemoMode;
682    private boolean mDemoInetCondition;
683    private WifiSignalController.WifiState mDemoWifiState;
684
685    @Override
686    public void dispatchDemoCommand(String command, Bundle args) {
687        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
688            if (DEBUG) Log.d(TAG, "Entering demo mode");
689            unregisterListeners();
690            mDemoMode = true;
691            mDemoInetCondition = mInetCondition;
692            mDemoWifiState = mWifiSignalController.getState();
693        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
694            if (DEBUG) Log.d(TAG, "Exiting demo mode");
695            mDemoMode = false;
696            // Update what MobileSignalControllers, because they may change
697            // to set the number of sim slots.
698            updateMobileControllers();
699            for (MobileSignalController controller : mMobileSignalControllers.values()) {
700                controller.resetLastState();
701            }
702            mWifiSignalController.resetLastState();
703            mReceiverHandler.post(mRegisterListeners);
704            notifyAllListeners();
705        } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
706            String airplane = args.getString("airplane");
707            if (airplane != null) {
708                boolean show = airplane.equals("show");
709                mCallbackHandler.setIsAirplaneMode(new IconState(show,
710                        TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode,
711                        mContext));
712            }
713            String fully = args.getString("fully");
714            if (fully != null) {
715                mDemoInetCondition = Boolean.parseBoolean(fully);
716                BitSet connected = new BitSet();
717
718                if (mDemoInetCondition) {
719                    connected.set(mWifiSignalController.mTransportType);
720                }
721                mWifiSignalController.updateConnectivity(connected, connected);
722                for (MobileSignalController controller : mMobileSignalControllers.values()) {
723                    if (mDemoInetCondition) {
724                        connected.set(controller.mTransportType);
725                    }
726                    controller.updateConnectivity(connected, connected);
727                }
728            }
729            String wifi = args.getString("wifi");
730            if (wifi != null) {
731                boolean show = wifi.equals("show");
732                String level = args.getString("level");
733                if (level != null) {
734                    mDemoWifiState.level = level.equals("null") ? -1
735                            : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
736                    mDemoWifiState.connected = mDemoWifiState.level >= 0;
737                }
738                mDemoWifiState.enabled = show;
739                mWifiSignalController.notifyListeners();
740            }
741            String sims = args.getString("sims");
742            if (sims != null) {
743                int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8);
744                List<SubscriptionInfo> subs = new ArrayList<>();
745                if (num != mMobileSignalControllers.size()) {
746                    mMobileSignalControllers.clear();
747                    int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
748                    for (int i = start /* get out of normal index range */; i < start + num; i++) {
749                        subs.add(addSignalController(i, i));
750                    }
751                    mCallbackHandler.setSubs(subs);
752                }
753            }
754            String nosim = args.getString("nosim");
755            if (nosim != null) {
756                mHasNoSims = nosim.equals("show");
757                mCallbackHandler.setNoSims(mHasNoSims);
758            }
759            String mobile = args.getString("mobile");
760            if (mobile != null) {
761                boolean show = mobile.equals("show");
762                String datatype = args.getString("datatype");
763                String slotString = args.getString("slot");
764                int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
765                slot = MathUtils.constrain(slot, 0, 8);
766                // Ensure we have enough sim slots
767                List<SubscriptionInfo> subs = new ArrayList<>();
768                while (mMobileSignalControllers.size() <= slot) {
769                    int nextSlot = mMobileSignalControllers.size();
770                    subs.add(addSignalController(nextSlot, nextSlot));
771                }
772                if (!subs.isEmpty()) {
773                    mCallbackHandler.setSubs(subs);
774                }
775                // Hack to index linearly for easy use.
776                MobileSignalController controller = mMobileSignalControllers
777                        .values().toArray(new MobileSignalController[0])[slot];
778                controller.getState().dataSim = datatype != null;
779                if (datatype != null) {
780                    controller.getState().iconGroup =
781                            datatype.equals("1x") ? TelephonyIcons.ONE_X :
782                            datatype.equals("3g") ? TelephonyIcons.THREE_G :
783                            datatype.equals("4g") ? TelephonyIcons.FOUR_G :
784                            datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS :
785                            datatype.equals("e") ? TelephonyIcons.E :
786                            datatype.equals("g") ? TelephonyIcons.G :
787                            datatype.equals("h") ? TelephonyIcons.H :
788                            datatype.equals("lte") ? TelephonyIcons.LTE :
789                            datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
790                            datatype.equals("roam") ? TelephonyIcons.ROAMING :
791                            TelephonyIcons.UNKNOWN;
792                }
793                int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
794                String level = args.getString("level");
795                if (level != null) {
796                    controller.getState().level = level.equals("null") ? -1
797                            : Math.min(Integer.parseInt(level), icons[0].length - 1);
798                    controller.getState().connected = controller.getState().level >= 0;
799                }
800                controller.getState().enabled = show;
801                controller.notifyListeners();
802            }
803            String carrierNetworkChange = args.getString("carriernetworkchange");
804            if (carrierNetworkChange != null) {
805                boolean show = carrierNetworkChange.equals("show");
806                for (MobileSignalController controller : mMobileSignalControllers.values()) {
807                    controller.setCarrierNetworkChangeMode(show);
808                }
809            }
810        }
811    }
812
813    private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
814        SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
815                null, 0, 0, "");
816        mMobileSignalControllers.put(id, new MobileSignalController(mContext,
817                mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
818                mSubDefaults, mReceiverHandler.getLooper()));
819        return info;
820    }
821
822    public boolean hasEmergencyCryptKeeperText() {
823        return EncryptionHelper.IS_DATA_ENCRYPTED;
824    }
825
826    public boolean isRadioOn() {
827        return !mAirplaneMode;
828    }
829
830    private class SubListener extends OnSubscriptionsChangedListener {
831        @Override
832        public void onSubscriptionsChanged() {
833            updateMobileControllers();
834        }
835    }
836
837    /**
838     * Used to register listeners from the BG Looper, this way the PhoneStateListeners that
839     * get created will also run on the BG Looper.
840     */
841    private final Runnable mRegisterListeners = new Runnable() {
842        @Override
843        public void run() {
844            registerListeners();
845        }
846    };
847
848    public static class SubscriptionDefaults {
849        public int getDefaultVoiceSubId() {
850            return SubscriptionManager.getDefaultVoiceSubscriptionId();
851        }
852
853        public int getDefaultDataSubId() {
854            return SubscriptionManager.getDefaultDataSubscriptionId();
855        }
856    }
857
858    @VisibleForTesting
859    static class Config {
860        boolean showAtLeast3G = false;
861        boolean alwaysShowCdmaRssi = false;
862        boolean show4gForLte = false;
863        boolean hideLtePlus = false;
864        boolean hspaDataDistinguishable;
865
866        static Config readConfig(Context context) {
867            Config config = new Config();
868            Resources res = context.getResources();
869
870            config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
871            config.alwaysShowCdmaRssi =
872                    res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
873            config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
874            config.hspaDataDistinguishable =
875                    res.getBoolean(R.bool.config_hspa_data_distinguishable);
876            config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
877            return config;
878        }
879    }
880}
881