1 /* 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 17 package com.android.settings.network.telephony; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.os.Looper; 24 import android.os.PersistableBundle; 25 import android.telephony.CarrierConfigManager; 26 import android.telephony.PhoneStateListener; 27 import android.telephony.SubscriptionManager; 28 import android.telephony.TelephonyManager; 29 import android.telephony.ims.ImsMmTelManager; 30 import android.util.Log; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceScreen; 35 import androidx.preference.SwitchPreference; 36 37 import com.android.settings.R; 38 import com.android.settings.network.ims.VolteQueryImsState; 39 import com.android.settingslib.core.lifecycle.LifecycleObserver; 40 import com.android.settingslib.core.lifecycle.events.OnStart; 41 import com.android.settingslib.core.lifecycle.events.OnStop; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 46 /** 47 * Preference controller for "Enhanced 4G LTE" 48 */ 49 public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenceController 50 implements LifecycleObserver, OnStart, OnStop { 51 52 private static final String TAG = "Enhanced4g"; 53 54 @VisibleForTesting 55 Preference mPreference; 56 private PhoneCallStateListener mPhoneStateListener; 57 private boolean mShow5gLimitedDialog; 58 boolean mIsNrEnabledFromCarrierConfig; 59 private boolean mHas5gCapability; 60 @VisibleForTesting 61 Integer mCallState; 62 private final List<On4gLteUpdateListener> m4gLteListeners; 63 64 protected static final int MODE_NONE = -1; 65 protected static final int MODE_VOLTE = 0; 66 protected static final int MODE_ADVANCED_CALL = 1; 67 protected static final int MODE_4G_CALLING = 2; 68 private int m4gCurrentMode = MODE_NONE; 69 Enhanced4gBasePreferenceController(Context context, String key)70 public Enhanced4gBasePreferenceController(Context context, String key) { 71 super(context, key); 72 m4gLteListeners = new ArrayList<>(); 73 } 74 init(int subId)75 public Enhanced4gBasePreferenceController init(int subId) { 76 if (mPhoneStateListener == null) { 77 mPhoneStateListener = new PhoneCallStateListener(); 78 } 79 80 if (mSubId == subId) { 81 return this; 82 } 83 mSubId = subId; 84 final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId); 85 if (carrierConfig == null) { 86 return this; 87 } 88 89 final boolean show4GForLTE = carrierConfig.getBoolean( 90 CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL); 91 m4gCurrentMode = carrierConfig.getInt( 92 CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT); 93 if (m4gCurrentMode != MODE_ADVANCED_CALL) { 94 m4gCurrentMode = show4GForLTE ? MODE_4G_CALLING : MODE_VOLTE; 95 } 96 97 mShow5gLimitedDialog = carrierConfig.getBoolean( 98 CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL); 99 mIsNrEnabledFromCarrierConfig = carrierConfig.getBoolean( 100 CarrierConfigManager.KEY_NR_ENABLED_BOOL); 101 return this; 102 } 103 104 @Override getAvailabilityStatus(int subId)105 public int getAvailabilityStatus(int subId) { 106 init(subId); 107 if (!isModeMatched()) { 108 return CONDITIONALLY_UNAVAILABLE; 109 } 110 final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId); 111 if ((carrierConfig == null) 112 || carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)) { 113 return CONDITIONALLY_UNAVAILABLE; 114 } 115 final VolteQueryImsState queryState = queryImsState(subId); 116 if (!queryState.isReadyToVoLte()) { 117 return CONDITIONALLY_UNAVAILABLE; 118 } 119 return (isUserControlAllowed(carrierConfig) && queryState.isAllowUserControl()) 120 ? AVAILABLE : AVAILABLE_UNSEARCHABLE; 121 } 122 123 @Override displayPreference(PreferenceScreen screen)124 public void displayPreference(PreferenceScreen screen) { 125 super.displayPreference(screen); 126 mPreference = screen.findPreference(getPreferenceKey()); 127 } 128 129 @Override onStart()130 public void onStart() { 131 if (mPhoneStateListener == null) { 132 return; 133 } 134 mPhoneStateListener.register(mContext, mSubId); 135 } 136 137 @Override onStop()138 public void onStop() { 139 if (mPhoneStateListener == null) { 140 return; 141 } 142 mPhoneStateListener.unregister(); 143 } 144 145 @Override updateState(Preference preference)146 public void updateState(Preference preference) { 147 super.updateState(preference); 148 if (preference == null) { 149 return; 150 } 151 final SwitchPreference switchPreference = (SwitchPreference) preference; 152 153 final VolteQueryImsState queryState = queryImsState(mSubId); 154 switchPreference.setEnabled(isUserControlAllowed(getCarrierConfigForSubId(mSubId)) 155 && queryState.isAllowUserControl()); 156 switchPreference.setChecked(queryState.isEnabledByUser() 157 && queryState.isAllowUserControl()); 158 } 159 160 @Override setChecked(boolean isChecked)161 public boolean setChecked(boolean isChecked) { 162 if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { 163 return false; 164 } 165 final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId); 166 if (imsMmTelManager == null) { 167 return false; 168 } 169 170 if (isDialogNeeded() && !isChecked) { 171 show5gLimitedDialog(imsMmTelManager); 172 } else { 173 return setAdvancedCallingSettingEnabled(imsMmTelManager, isChecked); 174 } 175 return false; 176 } 177 178 @Override isChecked()179 public boolean isChecked() { 180 final VolteQueryImsState queryState = queryImsState(mSubId); 181 return queryState.isEnabledByUser(); 182 } 183 addListener(On4gLteUpdateListener lsn)184 public Enhanced4gBasePreferenceController addListener(On4gLteUpdateListener lsn) { 185 m4gLteListeners.add(lsn); 186 return this; 187 } 188 getMode()189 protected int getMode() { 190 return MODE_NONE; 191 } 192 isModeMatched()193 private boolean isModeMatched() { 194 return m4gCurrentMode == getMode(); 195 } 196 197 @VisibleForTesting queryImsState(int subId)198 VolteQueryImsState queryImsState(int subId) { 199 return new VolteQueryImsState(mContext, subId); 200 } 201 isUserControlAllowed(final PersistableBundle carrierConfig)202 private boolean isUserControlAllowed(final PersistableBundle carrierConfig) { 203 return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE) 204 && (carrierConfig != null) 205 && carrierConfig.getBoolean( 206 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL); 207 } 208 209 private class PhoneCallStateListener extends PhoneStateListener { 210 PhoneCallStateListener()211 PhoneCallStateListener() { 212 super(Looper.getMainLooper()); 213 } 214 215 private TelephonyManager mTelephonyManager; 216 217 @Override onCallStateChanged(int state, String incomingNumber)218 public void onCallStateChanged(int state, String incomingNumber) { 219 mCallState = state; 220 updateState(mPreference); 221 } 222 register(Context context, int subId)223 public void register(Context context, int subId) { 224 mTelephonyManager = context.getSystemService(TelephonyManager.class); 225 if (SubscriptionManager.isValidSubscriptionId(subId)) { 226 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); 227 } 228 mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE); 229 230 final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily(); 231 mHas5gCapability = 232 (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0; 233 } 234 unregister()235 public void unregister() { 236 mCallState = null; 237 mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE); 238 } 239 } 240 241 /** 242 * Update other preferences when 4gLte state is changed 243 */ 244 public interface On4gLteUpdateListener { on4gLteUpdated()245 void on4gLteUpdated(); 246 } 247 isDialogNeeded()248 private boolean isDialogNeeded() { 249 Log.d(TAG, "Has5gCapability:" + mHas5gCapability); 250 return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig; 251 } 252 show5gLimitedDialog(ImsMmTelManager imsMmTelManager)253 private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) { 254 Log.d(TAG, "show5gLimitedDialog"); 255 AlertDialog.Builder builder = new AlertDialog.Builder(mContext); 256 DialogInterface.OnClickListener networkSettingsClickListener = 257 new Dialog.OnClickListener() { 258 @Override 259 public void onClick(DialogInterface dialog, int which) { 260 Log.d(TAG, "onClick,isChecked:false"); 261 setAdvancedCallingSettingEnabled(imsMmTelManager, false); 262 updateState(mPreference); 263 } 264 }; 265 builder.setTitle(R.string.volte_5G_limited_title) 266 .setMessage(R.string.volte_5G_limited_text) 267 .setNeutralButton(mContext.getResources().getString( 268 R.string.cancel), null) 269 .setPositiveButton(mContext.getResources().getString( 270 R.string.condition_turn_off), 271 networkSettingsClickListener) 272 .create() 273 .show(); 274 } 275 setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager, boolean isChecked)276 private boolean setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager, 277 boolean isChecked) { 278 try { 279 imsMmTelManager.setAdvancedCallingSettingEnabled(isChecked); 280 } catch (IllegalArgumentException exception) { 281 Log.w(TAG, "fail to set VoLTE=" + isChecked + ". subId=" + mSubId, exception); 282 return false; 283 } 284 for (final On4gLteUpdateListener lsn : m4gLteListeners) { 285 lsn.on4gLteUpdated(); 286 } 287 return true; 288 } 289 } 290