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.PersistableBundle; 24 import android.telephony.CarrierConfigManager; 25 import android.telephony.PhoneStateListener; 26 import android.telephony.SubscriptionManager; 27 import android.telephony.TelephonyCallback; 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.internal.telephony.util.ArrayUtils; 38 import com.android.settings.R; 39 import com.android.settings.network.ims.VolteQueryImsState; 40 import com.android.settingslib.core.lifecycle.LifecycleObserver; 41 import com.android.settingslib.core.lifecycle.events.OnStart; 42 import com.android.settingslib.core.lifecycle.events.OnStop; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Preference controller for "Enhanced 4G LTE" 49 */ 50 public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenceController 51 implements LifecycleObserver, OnStart, OnStop { 52 53 private static final String TAG = "Enhanced4g"; 54 55 @VisibleForTesting 56 Preference mPreference; 57 private PhoneCallStateTelephonyCallback mTelephonyCallback; 58 private boolean mShow5gLimitedDialog; 59 boolean mIsNrEnabledFromCarrierConfig; 60 private boolean mHas5gCapability; 61 private 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 (mTelephonyCallback == null) { 77 mTelephonyCallback = new PhoneCallStateTelephonyCallback(); 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 100 int[] nrAvailabilities = carrierConfig.getIntArray( 101 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); 102 mIsNrEnabledFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities); 103 return this; 104 } 105 106 @Override getAvailabilityStatus(int subId)107 public int getAvailabilityStatus(int subId) { 108 init(subId); 109 if (!isModeMatched()) { 110 return CONDITIONALLY_UNAVAILABLE; 111 } 112 final VolteQueryImsState queryState = queryImsState(subId); 113 // Show VoLTE settings if VoIMS opt-in has been enabled irrespective of other VoLTE settings 114 if (queryState.isVoImsOptInEnabled()) { 115 return AVAILABLE; 116 } 117 118 final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId); 119 if ((carrierConfig == null) 120 || carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)) { 121 return CONDITIONALLY_UNAVAILABLE; 122 } 123 124 if (!queryState.isReadyToVoLte()) { 125 return CONDITIONALLY_UNAVAILABLE; 126 } 127 return (isUserControlAllowed(carrierConfig) && queryState.isAllowUserControl()) 128 ? AVAILABLE : AVAILABLE_UNSEARCHABLE; 129 } 130 131 @Override displayPreference(PreferenceScreen screen)132 public void displayPreference(PreferenceScreen screen) { 133 super.displayPreference(screen); 134 mPreference = screen.findPreference(getPreferenceKey()); 135 } 136 137 @Override onStart()138 public void onStart() { 139 if (!isModeMatched() || (mTelephonyCallback == null)) { 140 return; 141 } 142 mTelephonyCallback.register(mContext, mSubId); 143 } 144 145 @Override onStop()146 public void onStop() { 147 if (mTelephonyCallback == null) { 148 return; 149 } 150 mTelephonyCallback.unregister(); 151 } 152 153 @Override updateState(Preference preference)154 public void updateState(Preference preference) { 155 super.updateState(preference); 156 if (preference == null) { 157 return; 158 } 159 final SwitchPreference switchPreference = (SwitchPreference) preference; 160 161 final VolteQueryImsState queryState = queryImsState(mSubId); 162 switchPreference.setEnabled(isUserControlAllowed(getCarrierConfigForSubId(mSubId)) 163 && queryState.isAllowUserControl()); 164 switchPreference.setChecked(queryState.isEnabledByUser() 165 && queryState.isAllowUserControl()); 166 } 167 168 @Override setChecked(boolean isChecked)169 public boolean setChecked(boolean isChecked) { 170 if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { 171 return false; 172 } 173 final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId); 174 if (imsMmTelManager == null) { 175 return false; 176 } 177 178 if (isDialogNeeded() && !isChecked) { 179 show5gLimitedDialog(imsMmTelManager); 180 } else { 181 return setAdvancedCallingSettingEnabled(imsMmTelManager, isChecked); 182 } 183 return false; 184 } 185 186 @Override isChecked()187 public boolean isChecked() { 188 final VolteQueryImsState queryState = queryImsState(mSubId); 189 return queryState.isEnabledByUser(); 190 } 191 addListener(On4gLteUpdateListener lsn)192 public Enhanced4gBasePreferenceController addListener(On4gLteUpdateListener lsn) { 193 m4gLteListeners.add(lsn); 194 return this; 195 } 196 197 @VisibleForTesting getMode()198 protected int getMode() { 199 return MODE_NONE; 200 } 201 isModeMatched()202 private boolean isModeMatched() { 203 return m4gCurrentMode == getMode(); 204 } 205 206 @VisibleForTesting queryImsState(int subId)207 protected VolteQueryImsState queryImsState(int subId) { 208 return new VolteQueryImsState(mContext, subId); 209 } 210 211 @VisibleForTesting isCallStateIdle()212 protected boolean isCallStateIdle() { 213 return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE); 214 } 215 isUserControlAllowed(final PersistableBundle carrierConfig)216 private boolean isUserControlAllowed(final PersistableBundle carrierConfig) { 217 return isCallStateIdle() 218 && (carrierConfig != null) 219 && carrierConfig.getBoolean( 220 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL); 221 } 222 223 private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements 224 TelephonyCallback.CallStateListener { 225 226 private TelephonyManager mTelephonyManager; 227 228 @Override onCallStateChanged(int state)229 public void onCallStateChanged(int state) { 230 mCallState = state; 231 updateState(mPreference); 232 } 233 register(Context context, int subId)234 public void register(Context context, int subId) { 235 mTelephonyManager = context.getSystemService(TelephonyManager.class); 236 if (SubscriptionManager.isValidSubscriptionId(subId)) { 237 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); 238 } 239 // assign current call state so that it helps to show correct preference state even 240 // before first onCallStateChanged() by initial registration. 241 mCallState = mTelephonyManager.getCallState(subId); 242 mTelephonyManager.registerTelephonyCallback( 243 mContext.getMainExecutor(), mTelephonyCallback); 244 245 final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily(); 246 mHas5gCapability = 247 (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0; 248 } 249 unregister()250 public void unregister() { 251 mCallState = null; 252 if (mTelephonyManager != null) { 253 mTelephonyManager.unregisterTelephonyCallback(this); 254 } 255 } 256 } 257 258 /** 259 * Update other preferences when 4gLte state is changed 260 */ 261 public interface On4gLteUpdateListener { on4gLteUpdated()262 void on4gLteUpdated(); 263 } 264 isDialogNeeded()265 private boolean isDialogNeeded() { 266 Log.d(TAG, "Has5gCapability:" + mHas5gCapability); 267 return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig; 268 } 269 show5gLimitedDialog(ImsMmTelManager imsMmTelManager)270 private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) { 271 Log.d(TAG, "show5gLimitedDialog"); 272 AlertDialog.Builder builder = new AlertDialog.Builder(mContext); 273 DialogInterface.OnClickListener networkSettingsClickListener = 274 new Dialog.OnClickListener() { 275 @Override 276 public void onClick(DialogInterface dialog, int which) { 277 Log.d(TAG, "onClick,isChecked:false"); 278 setAdvancedCallingSettingEnabled(imsMmTelManager, false); 279 updateState(mPreference); 280 } 281 }; 282 builder.setTitle(R.string.volte_5G_limited_title) 283 .setMessage(R.string.volte_5G_limited_text) 284 .setNeutralButton(mContext.getResources().getString( 285 R.string.cancel), null) 286 .setPositiveButton(mContext.getResources().getString( 287 R.string.condition_turn_off), 288 networkSettingsClickListener) 289 .create() 290 .show(); 291 } 292 setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager, boolean isChecked)293 private boolean setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager, 294 boolean isChecked) { 295 try { 296 imsMmTelManager.setAdvancedCallingSettingEnabled(isChecked); 297 } catch (IllegalArgumentException exception) { 298 Log.w(TAG, "fail to set VoLTE=" + isChecked + ". subId=" + mSubId, exception); 299 return false; 300 } 301 for (final On4gLteUpdateListener lsn : m4gLteListeners) { 302 lsn.on4gLteUpdated(); 303 } 304 return true; 305 } 306 } 307