MobileSignalController.java revision 5859910463df29324df868f30e0ff66440cf161f
1/*
2 * Copyright (C) 2015 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 */
16package com.android.systemui.statusbar.policy;
17
18import android.content.Context;
19import android.content.Intent;
20import android.net.NetworkCapabilities;
21import android.os.Looper;
22import android.telephony.PhoneStateListener;
23import android.telephony.ServiceState;
24import android.telephony.SignalStrength;
25import android.telephony.SubscriptionInfo;
26import android.telephony.SubscriptionManager;
27import android.telephony.TelephonyManager;
28import android.util.Log;
29import android.util.SparseArray;
30
31import com.android.internal.annotations.VisibleForTesting;
32import com.android.internal.telephony.TelephonyIntents;
33import com.android.internal.telephony.cdma.EriInfo;
34import com.android.systemui.R;
35import com.android.systemui.statusbar.policy.NetworkController.IconState;
36import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
37import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
38
39import java.io.PrintWriter;
40import java.util.BitSet;
41import java.util.Objects;
42
43
44public class MobileSignalController extends SignalController<
45        MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
46    private final TelephonyManager mPhone;
47    private final SubscriptionDefaults mDefaults;
48    private final String mNetworkNameDefault;
49    private final String mNetworkNameSeparator;
50    @VisibleForTesting
51    final PhoneStateListener mPhoneStateListener;
52    // Save entire info for logging, we only use the id.
53    private final SubscriptionInfo mSubscriptionInfo;
54
55    // @VisibleForDemoMode
56    final SparseArray<MobileIconGroup> mNetworkToIconLookup;
57
58    // Since some pieces of the phone state are interdependent we store it locally,
59    // this could potentially become part of MobileState for simplification/complication
60    // of code.
61    private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
62    private int mDataState = TelephonyManager.DATA_DISCONNECTED;
63    private ServiceState mServiceState;
64    private SignalStrength mSignalStrength;
65    private MobileIconGroup mDefaultIcons;
66    private Config mConfig;
67
68    // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
69    // need listener lists anymore.
70    public MobileSignalController(Context context, Config config, boolean hasMobileData,
71            TelephonyManager phone, CallbackHandler callbackHandler,
72            NetworkControllerImpl networkController, SubscriptionInfo info,
73            SubscriptionDefaults defaults, Looper receiverLooper) {
74        super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
75                NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
76                networkController);
77        mNetworkToIconLookup = new SparseArray<>();
78        mConfig = config;
79        mPhone = phone;
80        mDefaults = defaults;
81        mSubscriptionInfo = info;
82        mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(),
83                receiverLooper);
84        mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
85        mNetworkNameDefault = getStringIfExists(
86                com.android.internal.R.string.lockscreen_carrier_default);
87
88        mapIconSets();
89
90        mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault;
91        mLastState.networkNameData = mCurrentState.networkNameData = mNetworkNameDefault;
92        mLastState.enabled = mCurrentState.enabled = hasMobileData;
93        mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
94        // Get initial data sim state.
95        updateDataSim();
96    }
97
98    public void setConfiguration(Config config) {
99        mConfig = config;
100        mapIconSets();
101        updateTelephony();
102    }
103
104    public int getDataContentDescription() {
105        return getIcons().mDataContentDescription;
106    }
107
108    public void setAirplaneMode(boolean airplaneMode) {
109        mCurrentState.airplaneMode = airplaneMode;
110        notifyListenersIfNecessary();
111    }
112
113    @Override
114    public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
115        boolean isValidated = validatedTransports.get(mTransportType);
116        mCurrentState.isDefault = connectedTransports.get(mTransportType);
117        // Only show this as not having connectivity if we are default.
118        mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
119        notifyListenersIfNecessary();
120    }
121
122    public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
123        mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
124        updateTelephony();
125    }
126
127    /**
128     * Start listening for phone state changes.
129     */
130    public void registerListener() {
131        mPhone.listen(mPhoneStateListener,
132                PhoneStateListener.LISTEN_SERVICE_STATE
133                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
134                        | PhoneStateListener.LISTEN_CALL_STATE
135                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
136                        | PhoneStateListener.LISTEN_DATA_ACTIVITY
137                        | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
138    }
139
140    /**
141     * Stop listening for phone state changes.
142     */
143    public void unregisterListener() {
144        mPhone.listen(mPhoneStateListener, 0);
145    }
146
147    /**
148     * Produce a mapping of data network types to icon groups for simple and quick use in
149     * updateTelephony.
150     */
151    private void mapIconSets() {
152        mNetworkToIconLookup.clear();
153
154        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
155        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
156        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
157        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
158        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
159
160        if (!mConfig.showAtLeast3G) {
161            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
162                    TelephonyIcons.UNKNOWN);
163            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
164            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
165            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
166
167            mDefaultIcons = TelephonyIcons.G;
168        } else {
169            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
170                    TelephonyIcons.THREE_G);
171            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
172                    TelephonyIcons.THREE_G);
173            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
174                    TelephonyIcons.THREE_G);
175            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
176                    TelephonyIcons.THREE_G);
177            mDefaultIcons = TelephonyIcons.THREE_G;
178        }
179
180        MobileIconGroup hGroup = TelephonyIcons.THREE_G;
181        if (mConfig.hspaDataDistinguishable) {
182            hGroup = TelephonyIcons.H;
183        }
184        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
185        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
186        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
187        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup);
188
189        if (mConfig.show4gForLte) {
190            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
191        } else {
192            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
193        }
194        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
195    }
196
197    @Override
198    public void notifyListeners() {
199        MobileIconGroup icons = getIcons();
200
201        String contentDescription = getStringIfExists(getContentDescription());
202        String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
203
204        // Show icon in QS when we are connected or need to show roaming.
205        boolean showDataIcon = mCurrentState.dataConnected
206                || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
207        IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
208                getCurrentIconId(), contentDescription);
209
210        int qsTypeIcon = 0;
211        IconState qsIcon = null;
212        String description = null;
213        // Only send data sim callbacks to QS.
214        if (mCurrentState.dataSim) {
215            qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
216            qsIcon = new IconState(mCurrentState.enabled
217                    && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
218            description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
219        }
220        boolean activityIn = mCurrentState.dataConnected
221                        && !mCurrentState.carrierNetworkChangeMode
222                        && mCurrentState.activityIn;
223        boolean activityOut = mCurrentState.dataConnected
224                        && !mCurrentState.carrierNetworkChangeMode
225                        && mCurrentState.activityOut;
226        showDataIcon &= mCurrentState.isDefault
227                || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
228        int typeIcon = showDataIcon ? icons.mDataType : 0;
229        mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
230                activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
231                mSubscriptionInfo.getSubscriptionId());
232    }
233
234    @Override
235    protected MobileState cleanState() {
236        return new MobileState();
237    }
238
239    private boolean hasService() {
240        if (mServiceState != null) {
241            // Consider the device to be in service if either voice or data
242            // service is available. Some SIM cards are marketed as data-only
243            // and do not support voice service, and on these SIM cards, we
244            // want to show signal bars for data service as well as the "no
245            // service" or "emergency calls only" text that indicates that voice
246            // is not available.
247            switch (mServiceState.getVoiceRegState()) {
248                case ServiceState.STATE_POWER_OFF:
249                    return false;
250                case ServiceState.STATE_OUT_OF_SERVICE:
251                case ServiceState.STATE_EMERGENCY_ONLY:
252                    return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
253                default:
254                    return true;
255            }
256        } else {
257            return false;
258        }
259    }
260
261    private boolean isCdma() {
262        return (mSignalStrength != null) && !mSignalStrength.isGsm();
263    }
264
265    public boolean isEmergencyOnly() {
266        return (mServiceState != null && mServiceState.isEmergencyOnly());
267    }
268
269    private boolean isRoaming() {
270        if (isCdma()) {
271            final int iconMode = mServiceState.getCdmaEriIconMode();
272            return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
273                    && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
274                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
275        } else {
276            return mServiceState != null && mServiceState.getRoaming();
277        }
278    }
279
280    private boolean isCarrierNetworkChangeActive() {
281        return mCurrentState.carrierNetworkChangeMode;
282    }
283
284    public void handleBroadcast(Intent intent) {
285        String action = intent.getAction();
286        if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
287            updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
288                    intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
289                    intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN),
290                    intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
291                    intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
292            notifyListenersIfNecessary();
293        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
294            updateDataSim();
295            notifyListenersIfNecessary();
296        }
297    }
298
299    private void updateDataSim() {
300        int defaultDataSub = mDefaults.getDefaultDataSubId();
301        if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) {
302            mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId();
303        } else {
304            // There doesn't seem to be a data sim selected, however if
305            // there isn't a MobileSignalController with dataSim set, then
306            // QS won't get any callbacks and will be blank.  Instead
307            // lets just assume we are the data sim (which will basically
308            // show one at random) in QS until one is selected.  The user
309            // should pick one soon after, so we shouldn't be in this state
310            // for long.
311            mCurrentState.dataSim = true;
312        }
313    }
314
315    /**
316     * Updates the network's name based on incoming spn and plmn.
317     */
318    void updateNetworkName(boolean showSpn, String spn, String dataSpn,
319            boolean showPlmn, String plmn) {
320        if (CHATTY) {
321            Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn
322                    + " spn=" + spn + " dataSpn=" + dataSpn
323                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
324        }
325        StringBuilder str = new StringBuilder();
326        StringBuilder strData = new StringBuilder();
327        if (showPlmn && plmn != null) {
328            str.append(plmn);
329            strData.append(plmn);
330        }
331        if (showSpn && spn != null) {
332            if (str.length() != 0) {
333                str.append(mNetworkNameSeparator);
334            }
335            str.append(spn);
336        }
337        if (str.length() != 0) {
338            mCurrentState.networkName = str.toString();
339        } else {
340            mCurrentState.networkName = mNetworkNameDefault;
341        }
342        if (showSpn && dataSpn != null) {
343            if (strData.length() != 0) {
344                strData.append(mNetworkNameSeparator);
345            }
346            strData.append(dataSpn);
347        }
348        if (strData.length() != 0) {
349            mCurrentState.networkNameData = strData.toString();
350        } else {
351            mCurrentState.networkNameData = mNetworkNameDefault;
352        }
353    }
354
355    /**
356     * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
357     * mDataState, and mSimState.  It should be called any time one of these is updated.
358     * This will call listeners if necessary.
359     */
360    private final void updateTelephony() {
361        if (DEBUG) {
362            Log.d(mTag, "updateTelephonySignalStrength: hasService=" + hasService()
363                    + " ss=" + mSignalStrength);
364        }
365        mCurrentState.connected = hasService() && mSignalStrength != null;
366        if (mCurrentState.connected) {
367            if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
368                mCurrentState.level = mSignalStrength.getCdmaLevel();
369            } else {
370                mCurrentState.level = mSignalStrength.getLevel();
371            }
372        }
373        if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
374            mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
375        } else {
376            mCurrentState.iconGroup = mDefaultIcons;
377        }
378        mCurrentState.dataConnected = mCurrentState.connected
379                && mDataState == TelephonyManager.DATA_CONNECTED;
380
381        if (isCarrierNetworkChangeActive()) {
382            mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
383        } else if (isRoaming()) {
384            mCurrentState.iconGroup = TelephonyIcons.ROAMING;
385        }
386        if (isEmergencyOnly() != mCurrentState.isEmergency) {
387            mCurrentState.isEmergency = isEmergencyOnly();
388            mNetworkController.recalculateEmergency();
389        }
390        // Fill in the network name if we think we have it.
391        if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
392                && mServiceState.getOperatorAlphaShort() != null) {
393            mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
394        }
395
396        notifyListenersIfNecessary();
397    }
398
399    @VisibleForTesting
400    void setActivity(int activity) {
401        mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
402                || activity == TelephonyManager.DATA_ACTIVITY_IN;
403        mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
404                || activity == TelephonyManager.DATA_ACTIVITY_OUT;
405        notifyListenersIfNecessary();
406    }
407
408    @Override
409    public void dump(PrintWriter pw) {
410        super.dump(pw);
411        pw.println("  mSubscription=" + mSubscriptionInfo + ",");
412        pw.println("  mServiceState=" + mServiceState + ",");
413        pw.println("  mSignalStrength=" + mSignalStrength + ",");
414        pw.println("  mDataState=" + mDataState + ",");
415        pw.println("  mDataNetType=" + mDataNetType + ",");
416    }
417
418    class MobilePhoneStateListener extends PhoneStateListener {
419        public MobilePhoneStateListener(int subId, Looper looper) {
420            super(subId, looper);
421        }
422
423        @Override
424        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
425            if (DEBUG) {
426                Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +
427                        ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
428            }
429            mSignalStrength = signalStrength;
430            updateTelephony();
431        }
432
433        @Override
434        public void onServiceStateChanged(ServiceState state) {
435            if (DEBUG) {
436                Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
437                        + " dataState=" + state.getDataRegState());
438            }
439            mServiceState = state;
440            updateTelephony();
441        }
442
443        @Override
444        public void onDataConnectionStateChanged(int state, int networkType) {
445            if (DEBUG) {
446                Log.d(mTag, "onDataConnectionStateChanged: state=" + state
447                        + " type=" + networkType);
448            }
449            mDataState = state;
450            mDataNetType = networkType;
451            updateTelephony();
452        }
453
454        @Override
455        public void onDataActivity(int direction) {
456            if (DEBUG) {
457                Log.d(mTag, "onDataActivity: direction=" + direction);
458            }
459            setActivity(direction);
460        }
461
462        @Override
463        public void onCarrierNetworkChange(boolean active) {
464            if (DEBUG) {
465                Log.d(mTag, "onCarrierNetworkChange: active=" + active);
466            }
467            mCurrentState.carrierNetworkChangeMode = active;
468
469            updateTelephony();
470        }
471    };
472
473    static class MobileIconGroup extends SignalController.IconGroup {
474        final int mDataContentDescription; // mContentDescriptionDataType
475        final int mDataType;
476        final boolean mIsWide;
477        final int mQsDataType;
478
479        public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
480                int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
481                int discContentDesc, int dataContentDesc, int dataType, boolean isWide,
482                int qsDataType) {
483            super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
484                    qsDiscState, discContentDesc);
485            mDataContentDescription = dataContentDesc;
486            mDataType = dataType;
487            mIsWide = isWide;
488            mQsDataType = qsDataType;
489        }
490    }
491
492    static class MobileState extends SignalController.State {
493        String networkName;
494        String networkNameData;
495        boolean dataSim;
496        boolean dataConnected;
497        boolean isEmergency;
498        boolean airplaneMode;
499        boolean carrierNetworkChangeMode;
500        boolean isDefault;
501
502        @Override
503        public void copyFrom(State s) {
504            super.copyFrom(s);
505            MobileState state = (MobileState) s;
506            dataSim = state.dataSim;
507            networkName = state.networkName;
508            networkNameData = state.networkNameData;
509            dataConnected = state.dataConnected;
510            isDefault = state.isDefault;
511            isEmergency = state.isEmergency;
512            airplaneMode = state.airplaneMode;
513            carrierNetworkChangeMode = state.carrierNetworkChangeMode;
514        }
515
516        @Override
517        protected void toString(StringBuilder builder) {
518            super.toString(builder);
519            builder.append(',');
520            builder.append("dataSim=").append(dataSim).append(',');
521            builder.append("networkName=").append(networkName).append(',');
522            builder.append("networkNameData=").append(networkNameData).append(',');
523            builder.append("dataConnected=").append(dataConnected).append(',');
524            builder.append("isDefault=").append(isDefault).append(',');
525            builder.append("isEmergency=").append(isEmergency).append(',');
526            builder.append("airplaneMode=").append(airplaneMode).append(',');
527            builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode);
528        }
529
530        @Override
531        public boolean equals(Object o) {
532            return super.equals(o)
533                    && Objects.equals(((MobileState) o).networkName, networkName)
534                    && Objects.equals(((MobileState) o).networkNameData, networkNameData)
535                    && ((MobileState) o).dataSim == dataSim
536                    && ((MobileState) o).dataConnected == dataConnected
537                    && ((MobileState) o).isEmergency == isEmergency
538                    && ((MobileState) o).airplaneMode == airplaneMode
539                    && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
540                    && ((MobileState) o).isDefault == isDefault;
541        }
542    }
543}
544