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