blob: 69ce13383583d6d8e3d1ac76ebb1e410f4f345b8 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.WirelessUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.policy.FiveGServiceClient;
import com.android.systemui.statusbar.policy.FiveGServiceClient.FiveGServiceState;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
/**
* Controller that generates text including the carrier names and/or the status of all the SIM
* interfaces in the device. Through a callback, the updates can be retrieved either as a list or
* separated by a given separator {@link CharSequence}.
*/
public class CarrierTextController {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "CarrierTextController";
private final boolean mIsEmergencyCallCapable;
private final Handler mMainHandler;
private final Handler mBgHandler;
private boolean mTelephonyCapable;
private boolean mShowMissingSim;
private boolean mShowAirplaneMode;
private final AtomicBoolean mNetworkSupported = new AtomicBoolean();
@VisibleForTesting
protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private WifiManager mWifiManager;
private boolean[] mSimErrorState;
private final int mSimSlotsNumber;
@Nullable // Check for nullability before dispatching
private CarrierTextCallback mCarrierTextCallback;
private Context mContext;
private CharSequence mSeparator;
private WakefulnessLifecycle mWakefulnessLifecycle;
private FiveGServiceClient mFiveGServiceClient;
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedWakingUp() {
final CarrierTextCallback callback = mCarrierTextCallback;
if (callback != null) callback.finishedWakingUp();
}
@Override
public void onStartedGoingToSleep() {
final CarrierTextCallback callback = mCarrierTextCallback;
if (callback != null) callback.startedGoingToSleep();
}
};
@VisibleForTesting
protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshCarrierInfo() {
if (DEBUG) {
Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
+ Boolean.toString(mTelephonyCapable));
}
updateCarrierText();
}
@Override
public void onTelephonyCapable(boolean capable) {
if (DEBUG) {
Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
+ Boolean.toString(capable));
}
mTelephonyCapable = capable;
updateCarrierText();
}
public void onSimStateChanged(int subId, int slotId, int simState) {
if (slotId < 0 || slotId >= mSimSlotsNumber) {
Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
+ " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
return;
}
if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState));
if (getStatusForIccState(simState) == CarrierTextController.StatusMode.SimIoError) {
mSimErrorState[slotId] = true;
updateCarrierText();
} else if (mSimErrorState[slotId]) {
mSimErrorState[slotId] = false;
updateCarrierText();
}
}
};
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
mActiveMobileDataSubscription = subId;
if (mNetworkSupported.get() && mCarrierTextCallback != null) {
updateCarrierText();
}
}
};
/**
* The status of this lock screen. Primarily used for widgets on LockScreen.
*/
private enum StatusMode {
Normal, // Normal case (sim card present, it's not locked)
NetworkLocked, // SIM card is 'network locked'.
SimMissing, // SIM card is missing.
SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
SimLocked, // SIM card is currently locked
SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
SimIoError, // SIM card is faulty
SimUnknown // SIM card is unknown
}
/**
* Controller that provides updates on text with carriers names or SIM status.
* Used by {@link CarrierText}.
*
* @param separator Separator between different parts of the text
*/
public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode,
boolean showMissingSim) {
mContext = context;
mIsEmergencyCallCapable = getTelephonyManager().isVoiceCapable();
mShowAirplaneMode = showAirplaneMode;
mShowMissingSim = showMissingSim;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mSeparator = separator;
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mSimSlotsNumber = getTelephonyManager().getSupportedModemCount();
mSimErrorState = new boolean[mSimSlotsNumber];
mMainHandler = Dependency.get(Dependency.MAIN_HANDLER);
mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mBgHandler.post(() -> {
boolean supported = ConnectivityManager.from(mContext).isNetworkSupported(
ConnectivityManager.TYPE_MOBILE);
if (supported && mNetworkSupported.compareAndSet(false, supported)) {
// This will set/remove the listeners appropriately. Note that it will never double
// add the listeners.
handleSetListening(mCarrierTextCallback);
}
});
}
private TelephonyManager getTelephonyManager() {
return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
/**
* Checks if there are faulty cards. Adds the text depending on the slot of the card
*
* @param text: current carrier text based on the sim state
* @param carrierNames names order by subscription order
* @param subOrderBySlot array containing the sub index for each slot ID
* @param noSims: whether a valid sim card is inserted
* @return text
*/
private CharSequence updateCarrierTextWithSimIoError(CharSequence text,
CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) {
final CharSequence carrier = "";
CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier);
// mSimErrorState has the state of each sim indexed by slotID.
for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) {
if (!mSimErrorState[index]) {
continue;
}
// In the case when no sim cards are detected but a faulty card is inserted
// overwrite the text and only show "Invalid card"
if (noSims) {
return concatenate(carrierTextForSimIOError,
getContext().getText(
com.android.internal.R.string.emergency_calls_only),
mSeparator);
} else if (subOrderBySlot[index] != -1) {
int subIndex = subOrderBySlot[index];
// prepend "Invalid card" when faulty card is inserted in slot 0 or 1
carrierNames[subIndex] = concatenate(carrierTextForSimIOError,
carrierNames[subIndex],
mSeparator);
} else {
// concatenate "Invalid card" when faulty card is inserted in other slot
text = concatenate(text, carrierTextForSimIOError, mSeparator);
}
}
return text;
}
/**
* This may be called internally after retrieving the correct value of {@code mNetworkSupported}
* (assumed false to start). In that case, the following happens:
* <ul>
* <li> If there was a registered callback, and the network is supported, it will register
* listeners.
* <li> If there was not a registered callback, it will try to remove unregistered listeners
* which is a no-op
* </ul>
*
* This call will always be processed in a background thread.
*/
private void handleSetListening(CarrierTextCallback callback) {
TelephonyManager telephonyManager = getTelephonyManager();
if (callback != null) {
mCarrierTextCallback = callback;
if (mNetworkSupported.get()) {
// Keyguard update monitor expects callbacks from main thread
mMainHandler.post(() -> mKeyguardUpdateMonitor.registerCallback(mCallback));
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
telephonyManager.listen(mPhoneStateListener,
LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
} else {
// Don't listen and clear out the text when the device isn't a phone.
mMainHandler.post(() -> callback.updateCarrierInfo(
new CarrierTextCallbackInfo("", null, false, null)
));
}
} else {
mCarrierTextCallback = null;
mMainHandler.post(() -> mKeyguardUpdateMonitor.removeCallback(mCallback));
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
}
}
/**
* Sets the listening status of this controller. If the callback is null, it is set to
* not listening.
*
* @param callback Callback to provide text updates
*/
public void setListening(CarrierTextCallback callback) {
mBgHandler.post(() -> handleSetListening(callback));
}
protected List<SubscriptionInfo> getSubscriptionInfo() {
return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
}
protected void updateCarrierText() {
boolean allSimsMissing = true;
boolean anySimReadyAndInService = false;
boolean missingSimsWithSubs = false;
boolean showCustomizeName = getContext().getResources().getBoolean(
com.android.systemui.R.bool.config_show_customize_carrier_name);
CharSequence displayText = null;
List<SubscriptionInfo> subs = getSubscriptionInfo();
final int numSubs = subs.size();
final int[] subsIds = new int[numSubs];
if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
// This array will contain in position i, the index of subscription in slot ID i.
// -1 if no subscription in that slot
final int[] subOrderBySlot = new int[mSimSlotsNumber];
for (int i = 0; i < mSimSlotsNumber; i++) {
subOrderBySlot[i] = -1;
}
final CharSequence[] carrierNames = new CharSequence[numSubs];
if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
for (int i = 0; i < numSubs; i++) {
int subId = subs.get(i).getSubscriptionId();
carrierNames[i] = "";
subsIds[i] = subId;
subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
int simState = mKeyguardUpdateMonitor.getSimState(subId);
CharSequence carrierName = subs.get(i).getCarrierName();
if ( showCustomizeName ) {
carrierName = getCustomizeCarrierName(carrierName, subs.get(i));
}
CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
if (DEBUG) {
Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
}
if (carrierTextForSimState != null) {
allSimsMissing = false;
carrierNames[i] = carrierTextForSimState;
}
if (simState == TelephonyManager.SIM_STATE_READY) {
ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) {
// hack for WFC (IWLAN) not turning off immediately once
// Wi-Fi is disassociated or disabled
if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
|| (mWifiManager.isWifiEnabled()
&& mWifiManager.getConnectionInfo() != null
&& mWifiManager.getConnectionInfo().getBSSID() != null)) {
if (DEBUG) {
Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
}
anySimReadyAndInService = true;
}
}
}
}
// Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY
// This condition will also be true always when numSubs == 0
if (allSimsMissing && !anySimReadyAndInService) {
if (numSubs != 0) {
// Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
// This depends on mPlmn containing the text "Emergency calls only" when the radio
// has some connectivity. Otherwise, it should be null or empty and just show
// "No SIM card"
// Grab the first subscripton, because they all should contain the emergency text,
// described above.
displayText = makeCarrierStringOnEmergencyCapable(
getMissingSimMessage(), subs.get(0).getCarrierName());
} else {
// We don't have a SubscriptionInfo to get the emergency calls only from.
// Grab it from the old sticky broadcast if possible instead. We can use it
// here because no subscriptions are active, so we don't have
// to worry about MSIM clashing.
CharSequence text =
getContext().getText(com.android.internal.R.string.emergency_calls_only);
Intent i = getContext().registerReceiver(null,
new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
if (i != null) {
String spn = "";
String plmn = "";
if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) {
spn = i.getStringExtra(TelephonyManager.EXTRA_SPN);
}
if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) {
plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
}
if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
if (Objects.equals(plmn, spn)) {
text = plmn;
} else {
text = concatenate(plmn, spn, mSeparator);
}
}
displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text);
}
}
if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames);
displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot,
allSimsMissing);
boolean airplaneMode = false;
// APM (airplane mode) != no carrier state. There are carrier services
// (e.g. WFC = Wi-Fi calling) which may operate in APM.
if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
displayText = getAirplaneModeMessage();
airplaneMode = true;
}
final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
displayText,
carrierNames,
!allSimsMissing,
subsIds,
airplaneMode);
postToCallback(info);
}
@VisibleForTesting
protected void postToCallback(CarrierTextCallbackInfo info) {
final CarrierTextCallback callback = mCarrierTextCallback;
if (callback != null) {
mMainHandler.post(() -> callback.updateCarrierInfo(info));
}
}
private Context getContext() {
return mContext;
}
private String getMissingSimMessage() {
return mShowMissingSim && mTelephonyCapable
? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
}
private String getAirplaneModeMessage() {
return mShowAirplaneMode
? getContext().getString(R.string.airplane_mode) : "";
}
/**
* Top-level function for creating carrier text. Makes text based on simState, PLMN
* and SPN as well as device capabilities, such as being emergency call capable.
*
* @return Carrier text if not in missing state, null otherwise.
*/
private CharSequence getCarrierTextForSimState(int simState, CharSequence text) {
CharSequence carrierText = null;
CarrierTextController.StatusMode status = getStatusForIccState(simState);
switch (status) {
case Normal:
carrierText = text;
break;
case SimNotReady:
// Null is reserved for denoting missing, in this case we have nothing to display.
carrierText = ""; // nothing to display yet.
break;
case NetworkLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
mContext.getText(R.string.keyguard_network_locked_message), text);
break;
case SimMissing:
carrierText = null;
break;
case SimPermDisabled:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(
R.string.keyguard_permanent_disabled_sim_message_short),
text);
break;
case SimMissingLocked:
carrierText = null;
break;
case SimLocked:
carrierText = makeCarrierStringOnLocked(
getContext().getText(R.string.keyguard_sim_locked_message),
text);
break;
case SimPukLocked:
carrierText = makeCarrierStringOnLocked(
getContext().getText(R.string.keyguard_sim_puk_locked_message),
text);
break;
case SimIoError:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.keyguard_sim_error_message_short),
text);
break;
case SimUnknown:
carrierText = null;
break;
}
return carrierText;
}
/*
* Add emergencyCallMessage to carrier string only if phone supports emergency calls.
*/
private CharSequence makeCarrierStringOnEmergencyCapable(
CharSequence simMessage, CharSequence emergencyCallMessage) {
if (mIsEmergencyCallCapable) {
return concatenate(simMessage, emergencyCallMessage, mSeparator);
}
return simMessage;
}
/*
* Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in
* DSDS
*/
private CharSequence makeCarrierStringOnLocked(CharSequence simMessage,
CharSequence carrierName) {
final boolean simMessageValid = !TextUtils.isEmpty(simMessage);
final boolean carrierNameValid = !TextUtils.isEmpty(carrierName);
if (simMessageValid && carrierNameValid) {
return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template,
carrierName, simMessage);
} else if (simMessageValid) {
return simMessage;
} else if (carrierNameValid) {
return carrierName;
} else {
return "";
}
}
/**
* Determine the current status of the lock screen given the SIM state and other stuff.
*/
private CarrierTextController.StatusMode getStatusForIccState(int simState) {
final boolean missingAndNotProvisioned =
!mKeyguardUpdateMonitor.isDeviceProvisioned()
&& (simState == TelephonyManager.SIM_STATE_ABSENT
|| simState == TelephonyManager.SIM_STATE_PERM_DISABLED);
// Assume we're NETWORK_LOCKED if not provisioned
simState = missingAndNotProvisioned ? TelephonyManager.SIM_STATE_NETWORK_LOCKED : simState;
switch (simState) {
case TelephonyManager.SIM_STATE_ABSENT:
return CarrierTextController.StatusMode.SimMissing;
case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
return CarrierTextController.StatusMode.SimMissingLocked;
case TelephonyManager.SIM_STATE_NOT_READY:
return CarrierTextController.StatusMode.SimNotReady;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
return CarrierTextController.StatusMode.SimLocked;
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
return CarrierTextController.StatusMode.SimPukLocked;
case TelephonyManager.SIM_STATE_READY:
return CarrierTextController.StatusMode.Normal;
case TelephonyManager.SIM_STATE_PERM_DISABLED:
return CarrierTextController.StatusMode.SimPermDisabled;
case TelephonyManager.SIM_STATE_UNKNOWN:
return CarrierTextController.StatusMode.SimUnknown;
case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
return CarrierTextController.StatusMode.SimIoError;
}
return CarrierTextController.StatusMode.SimUnknown;
}
private static CharSequence concatenate(CharSequence plmn, CharSequence spn,
CharSequence separator) {
final boolean plmnValid = !TextUtils.isEmpty(plmn);
final boolean spnValid = !TextUtils.isEmpty(spn);
if (plmnValid && spnValid) {
return new StringBuilder().append(plmn).append(separator).append(spn).toString();
} else if (plmnValid) {
return plmn;
} else if (spnValid) {
return spn;
} else {
return "";
}
}
/**
* Joins the strings in a sequence using a separator. Empty strings are discarded with no extra
* separator added so there are no extra separators that are not needed.
*/
private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) {
int length = sequences.length;
if (length == 0) return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
if (!TextUtils.isEmpty(sequences[i])) {
if (!TextUtils.isEmpty(sb)) {
sb.append(separator);
}
sb.append(sequences[i]);
}
}
return sb.toString();
}
private static List<CharSequence> append(List<CharSequence> list, CharSequence string) {
if (!TextUtils.isEmpty(string)) {
list.add(string);
}
return list;
}
private CharSequence getCarrierHelpTextForSimState(int simState,
String plmn, String spn) {
int carrierHelpTextId = 0;
CarrierTextController.StatusMode status = getStatusForIccState(simState);
switch (status) {
case NetworkLocked:
carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
break;
case SimMissing:
carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
break;
case SimPermDisabled:
carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
break;
case SimMissingLocked:
carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
break;
case Normal:
case SimLocked:
case SimPukLocked:
break;
}
return mContext.getText(carrierHelpTextId);
}
public static class Builder {
private final Context mContext;
private final String mSeparator;
private boolean mShowAirplaneMode;
private boolean mShowMissingSim;
@Inject
public Builder(Context context, @Main Resources resources) {
mContext = context;
mSeparator = resources.getString(
com.android.internal.R.string.kg_text_message_separator);
}
public Builder setShowAirplaneMode(boolean showAirplaneMode) {
mShowAirplaneMode = showAirplaneMode;
return this;
}
public Builder setShowMissingSim(boolean showMissingSim) {
mShowMissingSim = showMissingSim;
return this;
}
public CarrierTextController build() {
return new CarrierTextController(
mContext, mSeparator, mShowAirplaneMode, mShowMissingSim);
}
}
/**
* Data structure for passing information to CarrierTextController subscribers
*/
public static final class CarrierTextCallbackInfo {
public final CharSequence carrierText;
public final CharSequence[] listOfCarriers;
public final boolean anySimReady;
public final int[] subscriptionIds;
public boolean airplaneMode;
@VisibleForTesting
public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
boolean anySimReady, int[] subscriptionIds) {
this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
}
@VisibleForTesting
public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
this.carrierText = carrierText;
this.listOfCarriers = listOfCarriers;
this.anySimReady = anySimReady;
this.subscriptionIds = subscriptionIds;
this.airplaneMode = airplaneMode;
}
}
/**
* Callback to communicate to Views
*/
public interface CarrierTextCallback {
/**
* Provides updated carrier information.
*/
default void updateCarrierInfo(CarrierTextCallbackInfo info) {};
/**
* Notifies the View that the device is going to sleep
*/
default void startedGoingToSleep() {};
/**
* Notifies the View that the device finished waking up
*/
default void finishedWakingUp() {};
}
private String getCustomizeCarrierName(CharSequence originCarrierName,
SubscriptionInfo sub) {
StringBuilder newCarrierName = new StringBuilder();
int networkType = getNetworkType(sub.getSubscriptionId());
String networkClass = networkTypeToString(networkType);
String fiveGNetworkClass = get5GNetworkClass(sub, networkType);
if ( fiveGNetworkClass != null ) {
networkClass = fiveGNetworkClass;
}
if (!TextUtils.isEmpty(originCarrierName)) {
String[] names = originCarrierName.toString().split(mSeparator.toString(), 2);
for (int j = 0; j < names.length; j++) {
names[j] = getLocalString(
names[j], com.android.systemui.R.array.origin_carrier_names,
com.android.systemui.R.array.locale_carrier_names);
if (!TextUtils.isEmpty(names[j])) {
if (!TextUtils.isEmpty(networkClass)) {
names[j] = new StringBuilder().append(names[j]).append(" ")
.append(networkClass).toString();
}
if (j > 0 && names[j].equals(names[j - 1])) {
continue;
}
if (j > 0) {
newCarrierName.append(mSeparator);
}
newCarrierName.append(names[j]);
}
}
}
return newCarrierName.toString();
}
private int getNetworkType(int subId) {
int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
if (ss != null && (ss.getDataRegState() == ServiceState.STATE_IN_SERVICE
|| ss.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)) {
networkType = ss.getDataNetworkType();
if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
networkType = ss.getVoiceNetworkType();
}
}
return networkType;
}
private String networkTypeToString(int networkType) {
int classId = com.android.systemui.R.string.config_rat_unknown;
long mask = TelephonyManager.getBitMaskForNetworkType(networkType);
if ((mask & TelephonyManager.NETWORK_CLASS_BITMASK_2G) != 0) {
classId = com.android.systemui.R.string.config_rat_2g;
} else if ((mask & TelephonyManager.NETWORK_CLASS_BITMASK_3G) != 0) {
classId = com.android.systemui.R.string.config_rat_3g;
} else if ((mask & TelephonyManager.NETWORK_CLASS_BITMASK_4G) != 0) {
classId = com.android.systemui.R.string.config_rat_4g;
}
return getContext().getResources().getString(classId);
}
/**
* parse the string to current language.
*
* @param originalString original string
* @param originNamesId the id of the original string array.
* @param localNamesId the id of the local string keys.
* @return local language string
*/
private String getLocalString(String originalString,
int originNamesId, int localNamesId) {
String[] origNames = getContext().getResources().getStringArray(originNamesId);
String[] localNames = getContext().getResources().getStringArray(localNamesId);
for (int i = 0; i < origNames.length; i++) {
if (origNames[i].equalsIgnoreCase(originalString)) {
return localNames[i];
}
}
return originalString;
}
private String get5GNetworkClass(SubscriptionInfo sub, int networkType) {
if ( networkType == TelephonyManager.NETWORK_TYPE_NR ) {
return mContext.getResources().getString(R.string.data_connection_5g);
}
int slotIndex = sub.getSimSlotIndex();
int subId = sub.getSubscriptionId();
if ( mFiveGServiceClient == null ) {
mFiveGServiceClient = FiveGServiceClient.getInstance(mContext);
mFiveGServiceClient.registerCallback(mCallback);
}
FiveGServiceState fiveGServiceState =
mFiveGServiceClient.getCurrentServiceState(slotIndex);
if ( fiveGServiceState.isNrIconTypeValid() && isDataRegisteredOnLte(subId)) {
return mContext.getResources().getString(R.string.data_connection_5g);
}
return null;
}
private boolean isDataRegisteredOnLte(int subId) {
TelephonyManager telephonyManager = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
int dataType = telephonyManager.getDataNetworkType(subId);
if ( dataType == TelephonyManager.NETWORK_TYPE_LTE ||
dataType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
return true;
}else{
return false;
}
}
}