[go: nahoru, domu]

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