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