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