blob: 0106609b9271215c0bd17799722956516d7684b9 [file] [log] [blame]
Fabian Kozynski02941af2019-01-17 17:57:37 -05001/*
2 * Copyright (C) 2019 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
Malcolm Chen06ec3572019-04-09 15:55:29 -070019import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
20import static android.telephony.PhoneStateListener.LISTEN_NONE;
21
Lucas Dupin8968f6a2019-08-09 17:41:15 -070022import static com.android.systemui.DejankUtils.whitelistIpcs;
23
Fabian Kozynski02941af2019-01-17 17:57:37 -050024import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Dave Mankoffc4fe6c42019-11-18 17:03:29 -050027import android.content.res.Resources;
Fabian Kozynski02941af2019-01-17 17:57:37 -050028import android.net.ConnectivityManager;
29import android.net.wifi.WifiManager;
Fabian Kozynskibf6fef32019-02-04 09:21:38 -050030import android.os.Handler;
Malcolm Chen06ec3572019-04-09 15:55:29 -070031import android.telephony.PhoneStateListener;
Fabian Kozynski02941af2019-01-17 17:57:37 -050032import android.telephony.ServiceState;
33import android.telephony.SubscriptionInfo;
Malcolm Chen06ec3572019-04-09 15:55:29 -070034import android.telephony.SubscriptionManager;
Fabian Kozynski02941af2019-01-17 17:57:37 -050035import android.telephony.TelephonyManager;
36import android.text.TextUtils;
37import android.util.Log;
38
Fabian Kozynskid4c84af2019-10-07 09:45:50 -040039import androidx.annotation.Nullable;
Lucas Dupin8968f6a2019-08-09 17:41:15 -070040import androidx.annotation.VisibleForTesting;
41
Fabian Kozynski02941af2019-01-17 17:57:37 -050042import com.android.settingslib.WirelessUtils;
43import com.android.systemui.Dependency;
Hyunyoung Song8f9d34c2019-08-30 14:47:43 -070044import com.android.systemui.R;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050045import com.android.systemui.dagger.qualifiers.Main;
Fabian Kozynski02941af2019-01-17 17:57:37 -050046import com.android.systemui.keyguard.WakefulnessLifecycle;
47
48import java.util.List;
49import java.util.Objects;
50
Dave Mankoffc4fe6c42019-11-18 17:03:29 -050051import javax.inject.Inject;
52
Fabian Kozynski02941af2019-01-17 17:57:37 -050053/**
54 * Controller that generates text including the carrier names and/or the status of all the SIM
55 * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
56 * separated by a given separator {@link CharSequence}.
57 */
58public class CarrierTextController {
59 private static final boolean DEBUG = KeyguardConstants.DEBUG;
60 private static final String TAG = "CarrierTextController";
61
62 private final boolean mIsEmergencyCallCapable;
Fabian Kozynskiba46d1c2019-10-03 12:01:38 -040063 private final Handler mMainHandler;
Fabian Kozynski02941af2019-01-17 17:57:37 -050064 private boolean mTelephonyCapable;
Fabian Kozynski02941af2019-01-17 17:57:37 -050065 private boolean mShowMissingSim;
Fabian Kozynski02941af2019-01-17 17:57:37 -050066 private boolean mShowAirplaneMode;
Fabian Kozynskib176f422019-02-05 09:36:59 -050067 @VisibleForTesting
68 protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
Fabian Kozynski02941af2019-01-17 17:57:37 -050069 private WifiManager mWifiManager;
Fabian Kozynskib176f422019-02-05 09:36:59 -050070 private boolean[] mSimErrorState;
71 private final int mSimSlotsNumber;
Fabian Kozynskid4c84af2019-10-07 09:45:50 -040072 @Nullable // Check for nullability before dispatching
Fabian Kozynski02941af2019-01-17 17:57:37 -050073 private CarrierTextCallback mCarrierTextCallback;
74 private Context mContext;
75 private CharSequence mSeparator;
76 private WakefulnessLifecycle mWakefulnessLifecycle;
77 private final WakefulnessLifecycle.Observer mWakefulnessObserver =
78 new WakefulnessLifecycle.Observer() {
79 @Override
80 public void onFinishedWakingUp() {
Fabian Kozynskid4c84af2019-10-07 09:45:50 -040081 if (mCarrierTextCallback != null) mCarrierTextCallback.finishedWakingUp();
Fabian Kozynski02941af2019-01-17 17:57:37 -050082 }
83
84 @Override
85 public void onStartedGoingToSleep() {
Fabian Kozynskid4c84af2019-10-07 09:45:50 -040086 if (mCarrierTextCallback != null) mCarrierTextCallback.startedGoingToSleep();
Fabian Kozynski02941af2019-01-17 17:57:37 -050087 }
88 };
89
Fabian Kozynskib176f422019-02-05 09:36:59 -050090 @VisibleForTesting
91 protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
Fabian Kozynski02941af2019-01-17 17:57:37 -050092 @Override
93 public void onRefreshCarrierInfo() {
94 if (DEBUG) {
95 Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
96 + Boolean.toString(mTelephonyCapable));
97 }
98 updateCarrierText();
99 }
100
101 @Override
102 public void onTelephonyCapable(boolean capable) {
103 if (DEBUG) {
104 Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
105 + Boolean.toString(capable));
106 }
107 mTelephonyCapable = capable;
108 updateCarrierText();
109 }
110
Jayachandran Cf5436a62019-11-08 18:22:45 -0800111 public void onSimStateChanged(int subId, int slotId, int simState) {
Fabian Kozynskib176f422019-02-05 09:36:59 -0500112 if (slotId < 0 || slotId >= mSimSlotsNumber) {
Fabian Kozynski02941af2019-01-17 17:57:37 -0500113 Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
114 + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
115 return;
116 }
117
118 if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState));
119 if (getStatusForIccState(simState) == CarrierTextController.StatusMode.SimIoError) {
120 mSimErrorState[slotId] = true;
121 updateCarrierText();
122 } else if (mSimErrorState[slotId]) {
123 mSimErrorState[slotId] = false;
124 updateCarrierText();
125 }
126 }
127 };
128
Malcolm Chen06ec3572019-04-09 15:55:29 -0700129 private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
130 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
131 @Override
132 public void onActiveDataSubscriptionIdChanged(int subId) {
133 mActiveMobileDataSubscription = subId;
134 if (mKeyguardUpdateMonitor != null) {
135 updateCarrierText();
136 }
137 }
138 };
139
Fabian Kozynski02941af2019-01-17 17:57:37 -0500140 /**
141 * The status of this lock screen. Primarily used for widgets on LockScreen.
142 */
143 private enum StatusMode {
144 Normal, // Normal case (sim card present, it's not locked)
145 NetworkLocked, // SIM card is 'network locked'.
146 SimMissing, // SIM card is missing.
147 SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
148 SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
149 SimLocked, // SIM card is currently locked
150 SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
151 SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
152 SimIoError, // SIM card is faulty
153 SimUnknown // SIM card is unknown
154 }
155
156 /**
157 * Controller that provides updates on text with carriers names or SIM status.
158 * Used by {@link CarrierText}.
Fabian Kozynski1823f112019-01-18 11:43:29 -0500159 *
Fabian Kozynski02941af2019-01-17 17:57:37 -0500160 * @param separator Separator between different parts of the text
Fabian Kozynski02941af2019-01-17 17:57:37 -0500161 */
162 public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode,
163 boolean showMissingSim) {
164 mContext = context;
Michele5dbecd92019-12-11 17:48:29 -0800165 mIsEmergencyCallCapable = getTelephonyManager().isVoiceCapable();
Fabian Kozynski02941af2019-01-17 17:57:37 -0500166
167 mShowAirplaneMode = showAirplaneMode;
168 mShowMissingSim = showMissingSim;
169
170 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
171 mSeparator = separator;
172 mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
Malcolm Chenb908f9c2019-11-04 16:26:28 -0800173 mSimSlotsNumber = getTelephonyManager().getSupportedModemCount();
Fabian Kozynskib176f422019-02-05 09:36:59 -0500174 mSimErrorState = new boolean[mSimSlotsNumber];
Fabian Kozynskiba46d1c2019-10-03 12:01:38 -0400175 mMainHandler = Dependency.get(Dependency.MAIN_HANDLER);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500176 }
177
Malcolm Chenb908f9c2019-11-04 16:26:28 -0800178 private TelephonyManager getTelephonyManager() {
179 return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
180 }
181
Fabian Kozynski02941af2019-01-17 17:57:37 -0500182 /**
183 * Checks if there are faulty cards. Adds the text depending on the slot of the card
184 *
185 * @param text: current carrier text based on the sim state
Fabian Kozynskib176f422019-02-05 09:36:59 -0500186 * @param carrierNames names order by subscription order
187 * @param subOrderBySlot array containing the sub index for each slot ID
Fabian Kozynski02941af2019-01-17 17:57:37 -0500188 * @param noSims: whether a valid sim card is inserted
189 * @return text
190 */
Fabian Kozynskib176f422019-02-05 09:36:59 -0500191 private CharSequence updateCarrierTextWithSimIoError(CharSequence text,
192 CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) {
Fabian Kozynski02941af2019-01-17 17:57:37 -0500193 final CharSequence carrier = "";
194 CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
Jayachandran Cf5436a62019-11-08 18:22:45 -0800195 TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier);
Fabian Kozynskib176f422019-02-05 09:36:59 -0500196 // mSimErrorState has the state of each sim indexed by slotID.
Malcolm Chenb908f9c2019-11-04 16:26:28 -0800197 for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) {
Fabian Kozynskib176f422019-02-05 09:36:59 -0500198 if (!mSimErrorState[index]) {
199 continue;
Fabian Kozynski02941af2019-01-17 17:57:37 -0500200 }
Fabian Kozynskib176f422019-02-05 09:36:59 -0500201 // In the case when no sim cards are detected but a faulty card is inserted
202 // overwrite the text and only show "Invalid card"
203 if (noSims) {
204 return concatenate(carrierTextForSimIOError,
205 getContext().getText(
206 com.android.internal.R.string.emergency_calls_only),
207 mSeparator);
208 } else if (subOrderBySlot[index] != -1) {
209 int subIndex = subOrderBySlot[index];
210 // prepend "Invalid card" when faulty card is inserted in slot 0 or 1
211 carrierNames[subIndex] = concatenate(carrierTextForSimIOError,
212 carrierNames[subIndex],
213 mSeparator);
214 } else {
215 // concatenate "Invalid card" when faulty card is inserted in other slot
216 text = concatenate(text, carrierTextForSimIOError, mSeparator);
217 }
218
Fabian Kozynski02941af2019-01-17 17:57:37 -0500219 }
220 return text;
221 }
222
223 /**
224 * Sets the listening status of this controller. If the callback is null, it is set to
225 * not listening
Fabian Kozynski1823f112019-01-18 11:43:29 -0500226 *
Fabian Kozynski02941af2019-01-17 17:57:37 -0500227 * @param callback Callback to provide text updates
228 */
229 public void setListening(CarrierTextCallback callback) {
Malcolm Chenb908f9c2019-11-04 16:26:28 -0800230 TelephonyManager telephonyManager = getTelephonyManager();
Fabian Kozynski02941af2019-01-17 17:57:37 -0500231 if (callback != null) {
232 mCarrierTextCallback = callback;
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700233 // TODO(b/140034799)
234 if (whitelistIpcs(() -> ConnectivityManager.from(mContext).isNetworkSupported(
235 ConnectivityManager.TYPE_MOBILE))) {
Dave Mankoffe2294692019-08-14 11:53:13 -0400236 mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
Fabian Kozynskiba46d1c2019-10-03 12:01:38 -0400237 // Keyguard update monitor expects callbacks from main thread
238 mMainHandler.post(() -> {
239 if (mKeyguardUpdateMonitor != null) {
240 mKeyguardUpdateMonitor.registerCallback(mCallback);
241 }
242 });
Fabian Kozynski02941af2019-01-17 17:57:37 -0500243 mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
Malcolm Chen06ec3572019-04-09 15:55:29 -0700244 telephonyManager.listen(mPhoneStateListener,
245 LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500246 } else {
247 // Don't listen and clear out the text when the device isn't a phone.
248 mKeyguardUpdateMonitor = null;
Fabian Kozynski1823f112019-01-18 11:43:29 -0500249 callback.updateCarrierInfo(new CarrierTextCallbackInfo("", null, false, null));
Fabian Kozynski02941af2019-01-17 17:57:37 -0500250 }
251 } else {
252 mCarrierTextCallback = null;
253 if (mKeyguardUpdateMonitor != null) {
Fabian Kozynskiba46d1c2019-10-03 12:01:38 -0400254 // Keyguard update monitor expects callbacks from main thread
255 mMainHandler.post(() -> {
256 if (mKeyguardUpdateMonitor != null) {
257 mKeyguardUpdateMonitor.removeCallback(mCallback);
258 }
259 });
Fabian Kozynski02941af2019-01-17 17:57:37 -0500260 mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
261 }
Malcolm Chen06ec3572019-04-09 15:55:29 -0700262 telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
263 }
264 }
265
Sooraj Sasindran0d45da72019-04-25 15:12:21 -0700266 protected List<SubscriptionInfo> getSubscriptionInfo() {
Malcolm Chen5c63b512019-08-13 13:24:07 -0700267 return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
Sooraj Sasindran0d45da72019-04-25 15:12:21 -0700268 }
269
Fabian Kozynski02941af2019-01-17 17:57:37 -0500270 protected void updateCarrierText() {
271 boolean allSimsMissing = true;
272 boolean anySimReadyAndInService = false;
273 CharSequence displayText = null;
Sooraj Sasindran0d45da72019-04-25 15:12:21 -0700274 List<SubscriptionInfo> subs = getSubscriptionInfo();
Malcolm Chen06ec3572019-04-09 15:55:29 -0700275
Fabian Kozynski02941af2019-01-17 17:57:37 -0500276 final int numSubs = subs.size();
Fabian Kozynski1823f112019-01-18 11:43:29 -0500277 final int[] subsIds = new int[numSubs];
Fabian Kozynskib176f422019-02-05 09:36:59 -0500278 // This array will contain in position i, the index of subscription in slot ID i.
279 // -1 if no subscription in that slot
280 final int[] subOrderBySlot = new int[mSimSlotsNumber];
281 for (int i = 0; i < mSimSlotsNumber; i++) {
282 subOrderBySlot[i] = -1;
283 }
284 final CharSequence[] carrierNames = new CharSequence[numSubs];
Fabian Kozynski02941af2019-01-17 17:57:37 -0500285 if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
Fabian Kozynskib176f422019-02-05 09:36:59 -0500286
Fabian Kozynski02941af2019-01-17 17:57:37 -0500287 for (int i = 0; i < numSubs; i++) {
288 int subId = subs.get(i).getSubscriptionId();
Fabian Kozynskib176f422019-02-05 09:36:59 -0500289 carrierNames[i] = "";
Fabian Kozynski1823f112019-01-18 11:43:29 -0500290 subsIds[i] = subId;
Fabian Kozynskib176f422019-02-05 09:36:59 -0500291 subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800292 int simState = mKeyguardUpdateMonitor.getSimState(subId);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500293 CharSequence carrierName = subs.get(i).getCarrierName();
294 CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
295 if (DEBUG) {
296 Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
297 }
298 if (carrierTextForSimState != null) {
299 allSimsMissing = false;
Fabian Kozynskib176f422019-02-05 09:36:59 -0500300 carrierNames[i] = carrierTextForSimState;
Fabian Kozynski02941af2019-01-17 17:57:37 -0500301 }
Jayachandran Cf5436a62019-11-08 18:22:45 -0800302 if (simState == TelephonyManager.SIM_STATE_READY) {
Bonian Chena7e9e8c2019-06-02 23:59:31 +0000303 ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
SongFerngWang2670cba2019-12-23 19:21:14 +0800304 if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) {
Fabian Kozynski02941af2019-01-17 17:57:37 -0500305 // hack for WFC (IWLAN) not turning off immediately once
306 // Wi-Fi is disassociated or disabled
Bonian Chena7e9e8c2019-06-02 23:59:31 +0000307 if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
Fabian Kozynski02941af2019-01-17 17:57:37 -0500308 || (mWifiManager.isWifiEnabled()
309 && mWifiManager.getConnectionInfo() != null
310 && mWifiManager.getConnectionInfo().getBSSID() != null)) {
311 if (DEBUG) {
Bonian Chena7e9e8c2019-06-02 23:59:31 +0000312 Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500313 }
314 anySimReadyAndInService = true;
315 }
316 }
317 }
318 }
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400319 // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY
320 // This condition will also be true always when numSubs == 0
321 if (allSimsMissing && !anySimReadyAndInService) {
Fabian Kozynski02941af2019-01-17 17:57:37 -0500322 if (numSubs != 0) {
323 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
324 // This depends on mPlmn containing the text "Emergency calls only" when the radio
325 // has some connectivity. Otherwise, it should be null or empty and just show
326 // "No SIM card"
327 // Grab the first subscripton, because they all should contain the emergency text,
328 // described above.
329 displayText = makeCarrierStringOnEmergencyCapable(
330 getMissingSimMessage(), subs.get(0).getCarrierName());
331 } else {
332 // We don't have a SubscriptionInfo to get the emergency calls only from.
333 // Grab it from the old sticky broadcast if possible instead. We can use it
334 // here because no subscriptions are active, so we don't have
335 // to worry about MSIM clashing.
336 CharSequence text =
337 getContext().getText(com.android.internal.R.string.emergency_calls_only);
338 Intent i = getContext().registerReceiver(null,
Meng Wang57f56552020-01-17 16:58:40 -0800339 new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
Fabian Kozynski02941af2019-01-17 17:57:37 -0500340 if (i != null) {
341 String spn = "";
342 String plmn = "";
Meng Wang57f56552020-01-17 16:58:40 -0800343 if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) {
344 spn = i.getStringExtra(TelephonyManager.EXTRA_SPN);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500345 }
Meng Wang57f56552020-01-17 16:58:40 -0800346 if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) {
347 plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500348 }
349 if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
350 if (Objects.equals(plmn, spn)) {
351 text = plmn;
352 } else {
353 text = concatenate(plmn, spn, mSeparator);
354 }
355 }
356 displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text);
357 }
358 }
359
Fabian Kozynskic3d06f32019-07-31 14:18:41 -0400360 if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames);
361
Fabian Kozynskib176f422019-02-05 09:36:59 -0500362 displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot,
363 allSimsMissing);
Fabian Kozynskic3d06f32019-07-31 14:18:41 -0400364
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400365 boolean airplaneMode = false;
Fabian Kozynski02941af2019-01-17 17:57:37 -0500366 // APM (airplane mode) != no carrier state. There are carrier services
367 // (e.g. WFC = Wi-Fi calling) which may operate in APM.
368 if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
369 displayText = getAirplaneModeMessage();
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400370 airplaneMode = true;
Fabian Kozynski02941af2019-01-17 17:57:37 -0500371 }
372
Fabian Kozynskibf6fef32019-02-04 09:21:38 -0500373 final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
374 displayText,
Fabian Kozynskib176f422019-02-05 09:36:59 -0500375 carrierNames,
376 !allSimsMissing,
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400377 subsIds,
378 airplaneMode);
Fabian Kozynskib176f422019-02-05 09:36:59 -0500379 postToCallback(info);
380 }
381
382 @VisibleForTesting
383 protected void postToCallback(CarrierTextCallbackInfo info) {
Fabian Kozynski48343c32019-02-07 10:28:50 -0500384 final CarrierTextCallback callback = mCarrierTextCallback;
385 if (callback != null) {
Fabian Kozynskiba46d1c2019-10-03 12:01:38 -0400386 mMainHandler.post(() -> callback.updateCarrierInfo(info));
Fabian Kozynski02941af2019-01-17 17:57:37 -0500387 }
Fabian Kozynski02941af2019-01-17 17:57:37 -0500388 }
389
390 private Context getContext() {
391 return mContext;
392 }
393
394 private String getMissingSimMessage() {
395 return mShowMissingSim && mTelephonyCapable
396 ? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
397 }
398
399 private String getAirplaneModeMessage() {
400 return mShowAirplaneMode
401 ? getContext().getString(R.string.airplane_mode) : "";
402 }
403
404 /**
405 * Top-level function for creating carrier text. Makes text based on simState, PLMN
406 * and SPN as well as device capabilities, such as being emergency call capable.
407 *
408 * @return Carrier text if not in missing state, null otherwise.
409 */
Jayachandran Cf5436a62019-11-08 18:22:45 -0800410 private CharSequence getCarrierTextForSimState(int simState, CharSequence text) {
Fabian Kozynski02941af2019-01-17 17:57:37 -0500411 CharSequence carrierText = null;
412 CarrierTextController.StatusMode status = getStatusForIccState(simState);
413 switch (status) {
414 case Normal:
415 carrierText = text;
416 break;
417
418 case SimNotReady:
419 // Null is reserved for denoting missing, in this case we have nothing to display.
420 carrierText = ""; // nothing to display yet.
421 break;
422
423 case NetworkLocked:
424 carrierText = makeCarrierStringOnEmergencyCapable(
425 mContext.getText(R.string.keyguard_network_locked_message), text);
426 break;
427
428 case SimMissing:
429 carrierText = null;
430 break;
431
432 case SimPermDisabled:
433 carrierText = makeCarrierStringOnEmergencyCapable(
434 getContext().getText(
435 R.string.keyguard_permanent_disabled_sim_message_short),
436 text);
437 break;
438
439 case SimMissingLocked:
440 carrierText = null;
441 break;
442
443 case SimLocked:
Fabian Kozynski2fb343a2019-05-08 16:06:51 -0400444 carrierText = makeCarrierStringOnLocked(
Fabian Kozynski02941af2019-01-17 17:57:37 -0500445 getContext().getText(R.string.keyguard_sim_locked_message),
446 text);
447 break;
448
449 case SimPukLocked:
Fabian Kozynski2fb343a2019-05-08 16:06:51 -0400450 carrierText = makeCarrierStringOnLocked(
Fabian Kozynski02941af2019-01-17 17:57:37 -0500451 getContext().getText(R.string.keyguard_sim_puk_locked_message),
452 text);
453 break;
454 case SimIoError:
455 carrierText = makeCarrierStringOnEmergencyCapable(
456 getContext().getText(R.string.keyguard_sim_error_message_short),
457 text);
458 break;
459 case SimUnknown:
460 carrierText = null;
461 break;
462 }
463
464 return carrierText;
465 }
466
467 /*
468 * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
469 */
470 private CharSequence makeCarrierStringOnEmergencyCapable(
471 CharSequence simMessage, CharSequence emergencyCallMessage) {
472 if (mIsEmergencyCallCapable) {
473 return concatenate(simMessage, emergencyCallMessage, mSeparator);
474 }
475 return simMessage;
476 }
477
Fabian Kozynski2fb343a2019-05-08 16:06:51 -0400478 /*
479 * Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in
480 * DSDS
481 */
482 private CharSequence makeCarrierStringOnLocked(CharSequence simMessage,
483 CharSequence carrierName) {
484 final boolean simMessageValid = !TextUtils.isEmpty(simMessage);
485 final boolean carrierNameValid = !TextUtils.isEmpty(carrierName);
486 if (simMessageValid && carrierNameValid) {
487 return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template,
488 carrierName, simMessage);
489 } else if (simMessageValid) {
490 return simMessage;
491 } else if (carrierNameValid) {
492 return carrierName;
493 } else {
494 return "";
495 }
496 }
497
Fabian Kozynski02941af2019-01-17 17:57:37 -0500498 /**
499 * Determine the current status of the lock screen given the SIM state and other stuff.
500 */
Jayachandran Cf5436a62019-11-08 18:22:45 -0800501 private CarrierTextController.StatusMode getStatusForIccState(int simState) {
Fabian Kozynski02941af2019-01-17 17:57:37 -0500502 final boolean missingAndNotProvisioned =
Dave Mankoffe2294692019-08-14 11:53:13 -0400503 !Dependency.get(KeyguardUpdateMonitor.class).isDeviceProvisioned()
Jayachandran Cf5436a62019-11-08 18:22:45 -0800504 && (simState == TelephonyManager.SIM_STATE_ABSENT
505 || simState == TelephonyManager.SIM_STATE_PERM_DISABLED);
Fabian Kozynski02941af2019-01-17 17:57:37 -0500506
507 // Assume we're NETWORK_LOCKED if not provisioned
Jayachandran Cf5436a62019-11-08 18:22:45 -0800508 simState = missingAndNotProvisioned ? TelephonyManager.SIM_STATE_NETWORK_LOCKED : simState;
Fabian Kozynski02941af2019-01-17 17:57:37 -0500509 switch (simState) {
Jayachandran Cf5436a62019-11-08 18:22:45 -0800510 case TelephonyManager.SIM_STATE_ABSENT:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500511 return CarrierTextController.StatusMode.SimMissing;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800512 case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500513 return CarrierTextController.StatusMode.SimMissingLocked;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800514 case TelephonyManager.SIM_STATE_NOT_READY:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500515 return CarrierTextController.StatusMode.SimNotReady;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800516 case TelephonyManager.SIM_STATE_PIN_REQUIRED:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500517 return CarrierTextController.StatusMode.SimLocked;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800518 case TelephonyManager.SIM_STATE_PUK_REQUIRED:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500519 return CarrierTextController.StatusMode.SimPukLocked;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800520 case TelephonyManager.SIM_STATE_READY:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500521 return CarrierTextController.StatusMode.Normal;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800522 case TelephonyManager.SIM_STATE_PERM_DISABLED:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500523 return CarrierTextController.StatusMode.SimPermDisabled;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800524 case TelephonyManager.SIM_STATE_UNKNOWN:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500525 return CarrierTextController.StatusMode.SimUnknown;
Jayachandran Cf5436a62019-11-08 18:22:45 -0800526 case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
Fabian Kozynski02941af2019-01-17 17:57:37 -0500527 return CarrierTextController.StatusMode.SimIoError;
528 }
529 return CarrierTextController.StatusMode.SimUnknown;
530 }
531
532 private static CharSequence concatenate(CharSequence plmn, CharSequence spn,
533 CharSequence separator) {
534 final boolean plmnValid = !TextUtils.isEmpty(plmn);
535 final boolean spnValid = !TextUtils.isEmpty(spn);
536 if (plmnValid && spnValid) {
537 return new StringBuilder().append(plmn).append(separator).append(spn).toString();
538 } else if (plmnValid) {
539 return plmn;
540 } else if (spnValid) {
541 return spn;
542 } else {
543 return "";
544 }
545 }
546
Fabian Kozynski00d02f12019-04-15 09:48:30 -0400547 /**
548 * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra
549 * separator added so there are no extra separators that are not needed.
550 */
551 private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) {
552 int length = sequences.length;
553 if (length == 0) return "";
554 StringBuilder sb = new StringBuilder();
555 for (int i = 0; i < length; i++) {
556 if (!TextUtils.isEmpty(sequences[i])) {
557 if (!TextUtils.isEmpty(sb)) {
558 sb.append(separator);
559 }
560 sb.append(sequences[i]);
561 }
562 }
563 return sb.toString();
564 }
565
Fabian Kozynski02941af2019-01-17 17:57:37 -0500566 private static List<CharSequence> append(List<CharSequence> list, CharSequence string) {
567 if (!TextUtils.isEmpty(string)) {
568 list.add(string);
569 }
570 return list;
571 }
572
Jayachandran Cf5436a62019-11-08 18:22:45 -0800573 private CharSequence getCarrierHelpTextForSimState(int simState,
Fabian Kozynski02941af2019-01-17 17:57:37 -0500574 String plmn, String spn) {
575 int carrierHelpTextId = 0;
576 CarrierTextController.StatusMode status = getStatusForIccState(simState);
577 switch (status) {
578 case NetworkLocked:
579 carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
580 break;
581
582 case SimMissing:
583 carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
584 break;
585
586 case SimPermDisabled:
587 carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
588 break;
589
590 case SimMissingLocked:
591 carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
592 break;
593
594 case Normal:
595 case SimLocked:
596 case SimPukLocked:
597 break;
598 }
599
600 return mContext.getText(carrierHelpTextId);
601 }
602
Dave Mankoffc4fe6c42019-11-18 17:03:29 -0500603 public static class Builder {
604 private final Context mContext;
605 private final String mSeparator;
606 private boolean mShowAirplaneMode;
607 private boolean mShowMissingSim;
608
609 @Inject
Dave Mankoff00e8a2f2019-12-18 16:59:49 -0500610 public Builder(Context context, @Main Resources resources) {
Dave Mankoffc4fe6c42019-11-18 17:03:29 -0500611 mContext = context;
612 mSeparator = resources.getString(
613 com.android.internal.R.string.kg_text_message_separator);
614 }
615
616
617 public Builder setShowAirplaneMode(boolean showAirplaneMode) {
618 mShowAirplaneMode = showAirplaneMode;
619 return this;
620 }
621
622 public Builder setShowMissingSim(boolean showMissingSim) {
623 mShowMissingSim = showMissingSim;
624 return this;
625 }
626
627 public CarrierTextController build() {
628 return new CarrierTextController(
629 mContext, mSeparator, mShowAirplaneMode, mShowMissingSim);
630 }
631 }
Fabian Kozynski02941af2019-01-17 17:57:37 -0500632 /**
Fabian Kozynski1823f112019-01-18 11:43:29 -0500633 * Data structure for passing information to CarrierTextController subscribers
634 */
635 public static final class CarrierTextCallbackInfo {
636 public final CharSequence carrierText;
637 public final CharSequence[] listOfCarriers;
638 public final boolean anySimReady;
639 public final int[] subscriptionIds;
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400640 public boolean airplaneMode;
Fabian Kozynski1823f112019-01-18 11:43:29 -0500641
Fabian Kozynskibf6fef32019-02-04 09:21:38 -0500642 @VisibleForTesting
643 public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
Fabian Kozynski1823f112019-01-18 11:43:29 -0500644 boolean anySimReady, int[] subscriptionIds) {
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400645 this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
646 }
647
648 @VisibleForTesting
649 public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
650 boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
Fabian Kozynski1823f112019-01-18 11:43:29 -0500651 this.carrierText = carrierText;
652 this.listOfCarriers = listOfCarriers;
653 this.anySimReady = anySimReady;
654 this.subscriptionIds = subscriptionIds;
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400655 this.airplaneMode = airplaneMode;
Fabian Kozynski1823f112019-01-18 11:43:29 -0500656 }
657 }
658
659 /**
Fabian Kozynski02941af2019-01-17 17:57:37 -0500660 * Callback to communicate to Views
661 */
662 public interface CarrierTextCallback {
663 /**
Fabian Kozynski1823f112019-01-18 11:43:29 -0500664 * Provides updated carrier information.
Fabian Kozynski02941af2019-01-17 17:57:37 -0500665 */
Fabian Kozynski1823f112019-01-18 11:43:29 -0500666 default void updateCarrierInfo(CarrierTextCallbackInfo info) {};
Fabian Kozynski02941af2019-01-17 17:57:37 -0500667
668 /**
669 * Notifies the View that the device is going to sleep
670 */
671 default void startedGoingToSleep() {};
672
673 /**
674 * Notifies the View that the device finished waking up
675 */
676 default void finishedWakingUp() {};
677 }
678}