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