CarrierText.java revision 47051d8e6dfcede55fa926c38bc06a6314fe6e4f
1/* 2 * Copyright (C) 2012 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 */ 16 17package com.android.keyguard; 18 19import java.util.List; 20import java.util.Locale; 21import java.util.Objects; 22 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.res.Resources; 27import android.content.res.TypedArray; 28import android.net.ConnectivityManager; 29import android.net.wifi.WifiManager; 30import android.telephony.ServiceState; 31import android.telephony.SubscriptionInfo; 32import android.text.TextUtils; 33import android.text.method.SingleLineTransformationMethod; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.view.View; 37import android.widget.TextView; 38 39import com.android.internal.telephony.IccCardConstants; 40import com.android.internal.telephony.IccCardConstants.State; 41import com.android.internal.telephony.TelephonyIntents; 42import com.android.settingslib.WirelessUtils; 43 44public class CarrierText extends TextView { 45 private static final boolean DEBUG = KeyguardConstants.DEBUG; 46 private static final String TAG = "CarrierText"; 47 48 private static CharSequence mSeparator; 49 50 private final boolean mIsEmergencyCallCapable; 51 52 private KeyguardUpdateMonitor mKeyguardUpdateMonitor; 53 54 private WifiManager mWifiManager; 55 56 private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { 57 @Override 58 public void onRefreshCarrierInfo() { 59 updateCarrierText(); 60 } 61 62 public void onScreenTurnedOff(int why) { 63 setSelected(false); 64 }; 65 66 public void onScreenTurnedOn() { 67 setSelected(true); 68 }; 69 }; 70 /** 71 * The status of this lock screen. Primarily used for widgets on LockScreen. 72 */ 73 private static enum StatusMode { 74 Normal, // Normal case (sim card present, it's not locked) 75 NetworkLocked, // SIM card is 'network locked'. 76 SimMissing, // SIM card is missing. 77 SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access 78 SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times 79 SimLocked, // SIM card is currently locked 80 SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure 81 SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM. 82 } 83 84 public CarrierText(Context context) { 85 this(context, null); 86 } 87 88 public CarrierText(Context context, AttributeSet attrs) { 89 super(context, attrs); 90 mIsEmergencyCallCapable = context.getResources().getBoolean( 91 com.android.internal.R.bool.config_voice_capable); 92 boolean useAllCaps; 93 TypedArray a = context.getTheme().obtainStyledAttributes( 94 attrs, R.styleable.CarrierText, 0, 0); 95 try { 96 useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false); 97 } finally { 98 a.recycle(); 99 } 100 setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps)); 101 102 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 103 } 104 105 protected void updateCarrierText() { 106 boolean allSimsMissing = true; 107 boolean anySimReadyAndInService = false; 108 CharSequence displayText = null; 109 110 List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); 111 final int N = subs.size(); 112 if (DEBUG) Log.d(TAG, "updateCarrierText(): " + N); 113 for (int i = 0; i < N; i++) { 114 int subId = subs.get(i).getSubscriptionId(); 115 State simState = mKeyguardUpdateMonitor.getSimState(subId); 116 CharSequence carrierName = subs.get(i).getCarrierName(); 117 CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); 118 if (DEBUG) { 119 Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName); 120 } 121 if (carrierTextForSimState != null) { 122 allSimsMissing = false; 123 displayText = concatenate(displayText, carrierTextForSimState); 124 } 125 if (simState == IccCardConstants.State.READY) { 126 ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId); 127 if (ss != null && ss.getDataRegState() == ServiceState.STATE_IN_SERVICE) { 128 // hack for WFC (IWLAN) not turning off immediately once 129 // Wi-Fi is disassociated or disabled 130 if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN 131 || (mWifiManager.isWifiEnabled() 132 && mWifiManager.getConnectionInfo() != null 133 && mWifiManager.getConnectionInfo().getBSSID() != null)) { 134 if (DEBUG) { 135 Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss); 136 } 137 anySimReadyAndInService = true; 138 } 139 } 140 } 141 } 142 if (allSimsMissing) { 143 if (N != 0) { 144 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable. 145 // This depends on mPlmn containing the text "Emergency calls only" when the radio 146 // has some connectivity. Otherwise, it should be null or empty and just show 147 // "No SIM card" 148 // Grab the first subscripton, because they all should contain the emergency text, 149 // described above. 150 displayText = makeCarrierStringOnEmergencyCapable( 151 getContext().getText(R.string.keyguard_missing_sim_message_short), 152 subs.get(0).getCarrierName()); 153 } else { 154 // We don't have a SubscriptionInfo to get the emergency calls only from. 155 // Grab it from the old sticky broadcast if possible instead. We can use it 156 // here because no subscriptions are active, so we don't have 157 // to worry about MSIM clashing. 158 CharSequence text = 159 getContext().getText(com.android.internal.R.string.emergency_calls_only); 160 Intent i = getContext().registerReceiver(null, 161 new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)); 162 if (i != null) { 163 String spn = ""; 164 String plmn = ""; 165 if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) { 166 spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN); 167 } 168 if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) { 169 plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN); 170 } 171 if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn); 172 if (Objects.equals(plmn, spn)) { 173 text = plmn; 174 } else { 175 text = concatenate(plmn, spn); 176 } 177 } 178 displayText = makeCarrierStringOnEmergencyCapable( 179 getContext().getText(R.string.keyguard_missing_sim_message_short), text); 180 } 181 } 182 183 // APM (airplane mode) != no carrier state. There are carrier services 184 // (e.g. WFC = Wi-Fi calling) which may operate in APM. 185 if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { 186 displayText = getContext().getString(R.string.airplane_mode); 187 } 188 setText(displayText); 189 } 190 191 @Override 192 protected void onFinishInflate() { 193 super.onFinishInflate(); 194 mSeparator = getResources().getString( 195 com.android.internal.R.string.kg_text_message_separator); 196 final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); 197 setSelected(screenOn); // Allow marquee to work. 198 } 199 200 @Override 201 protected void onAttachedToWindow() { 202 super.onAttachedToWindow(); 203 if (ConnectivityManager.from(mContext).isNetworkSupported( 204 ConnectivityManager.TYPE_MOBILE)) { 205 mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 206 mKeyguardUpdateMonitor.registerCallback(mCallback); 207 } else { 208 // Don't listen and clear out the text when the device isn't a phone. 209 mKeyguardUpdateMonitor = null; 210 setText(""); 211 } 212 } 213 214 @Override 215 protected void onDetachedFromWindow() { 216 super.onDetachedFromWindow(); 217 if (mKeyguardUpdateMonitor != null) { 218 mKeyguardUpdateMonitor.removeCallback(mCallback); 219 } 220 } 221 222 /** 223 * Top-level function for creating carrier text. Makes text based on simState, PLMN 224 * and SPN as well as device capabilities, such as being emergency call capable. 225 * 226 * @param simState 227 * @param text 228 * @param spn 229 * @return Carrier text if not in missing state, null otherwise. 230 */ 231 private CharSequence getCarrierTextForSimState(IccCardConstants.State simState, 232 CharSequence text) { 233 CharSequence carrierText = null; 234 StatusMode status = getStatusForIccState(simState); 235 switch (status) { 236 case Normal: 237 carrierText = text; 238 break; 239 240 case SimNotReady: 241 // Null is reserved for denoting missing, in this case we have nothing to display. 242 carrierText = ""; // nothing to display yet. 243 break; 244 245 case NetworkLocked: 246 carrierText = makeCarrierStringOnEmergencyCapable( 247 mContext.getText(R.string.keyguard_network_locked_message), text); 248 break; 249 250 case SimMissing: 251 carrierText = null; 252 break; 253 254 case SimPermDisabled: 255 carrierText = getContext().getText( 256 R.string.keyguard_permanent_disabled_sim_message_short); 257 break; 258 259 case SimMissingLocked: 260 carrierText = null; 261 break; 262 263 case SimLocked: 264 carrierText = makeCarrierStringOnEmergencyCapable( 265 getContext().getText(R.string.keyguard_sim_locked_message), 266 text); 267 break; 268 269 case SimPukLocked: 270 carrierText = makeCarrierStringOnEmergencyCapable( 271 getContext().getText(R.string.keyguard_sim_puk_locked_message), 272 text); 273 break; 274 } 275 276 return carrierText; 277 } 278 279 /* 280 * Add emergencyCallMessage to carrier string only if phone supports emergency calls. 281 */ 282 private CharSequence makeCarrierStringOnEmergencyCapable( 283 CharSequence simMessage, CharSequence emergencyCallMessage) { 284 if (mIsEmergencyCallCapable) { 285 return concatenate(simMessage, emergencyCallMessage); 286 } 287 return simMessage; 288 } 289 290 /** 291 * Determine the current status of the lock screen given the SIM state and other stuff. 292 */ 293 private StatusMode getStatusForIccState(IccCardConstants.State simState) { 294 // Since reading the SIM may take a while, we assume it is present until told otherwise. 295 if (simState == null) { 296 return StatusMode.Normal; 297 } 298 299 final boolean missingAndNotProvisioned = 300 !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned() 301 && (simState == IccCardConstants.State.ABSENT || 302 simState == IccCardConstants.State.PERM_DISABLED); 303 304 // Assume we're NETWORK_LOCKED if not provisioned 305 simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState; 306 switch (simState) { 307 case ABSENT: 308 return StatusMode.SimMissing; 309 case NETWORK_LOCKED: 310 return StatusMode.SimMissingLocked; 311 case NOT_READY: 312 return StatusMode.SimNotReady; 313 case PIN_REQUIRED: 314 return StatusMode.SimLocked; 315 case PUK_REQUIRED: 316 return StatusMode.SimPukLocked; 317 case READY: 318 return StatusMode.Normal; 319 case PERM_DISABLED: 320 return StatusMode.SimPermDisabled; 321 case UNKNOWN: 322 return StatusMode.SimMissing; 323 } 324 return StatusMode.SimMissing; 325 } 326 327 private static CharSequence concatenate(CharSequence plmn, CharSequence spn) { 328 final boolean plmnValid = !TextUtils.isEmpty(plmn); 329 final boolean spnValid = !TextUtils.isEmpty(spn); 330 if (plmnValid && spnValid) { 331 return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString(); 332 } else if (plmnValid) { 333 return plmn; 334 } else if (spnValid) { 335 return spn; 336 } else { 337 return ""; 338 } 339 } 340 341 private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState, 342 String plmn, String spn) { 343 int carrierHelpTextId = 0; 344 StatusMode status = getStatusForIccState(simState); 345 switch (status) { 346 case NetworkLocked: 347 carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled; 348 break; 349 350 case SimMissing: 351 carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long; 352 break; 353 354 case SimPermDisabled: 355 carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions; 356 break; 357 358 case SimMissingLocked: 359 carrierHelpTextId = R.string.keyguard_missing_sim_instructions; 360 break; 361 362 case Normal: 363 case SimLocked: 364 case SimPukLocked: 365 break; 366 } 367 368 return mContext.getText(carrierHelpTextId); 369 } 370 371 private class CarrierTextTransformationMethod extends SingleLineTransformationMethod { 372 private final Locale mLocale; 373 private final boolean mAllCaps; 374 375 public CarrierTextTransformationMethod(Context context, boolean allCaps) { 376 mLocale = context.getResources().getConfiguration().locale; 377 mAllCaps = allCaps; 378 } 379 380 @Override 381 public CharSequence getTransformation(CharSequence source, View view) { 382 source = super.getTransformation(source, view); 383 384 if (mAllCaps && source != null) { 385 source = source.toString().toUpperCase(mLocale); 386 } 387 388 return source; 389 } 390 } 391} 392