[go: nahoru, domu]

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