1 /* 2 * Copyright (C) 2017 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.wifi.calling; 18 19 import android.app.Activity; 20 import android.app.settings.SettingsEnums; 21 import android.content.BroadcastReceiver; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.res.Resources; 27 import android.os.Bundle; 28 import android.os.PersistableBundle; 29 import android.telephony.CarrierConfigManager; 30 import android.telephony.ServiceState; 31 import android.telephony.SubscriptionManager; 32 import android.telephony.TelephonyCallback; 33 import android.telephony.TelephonyManager; 34 import android.telephony.ims.ImsManager; 35 import android.telephony.ims.ImsMmTelManager; 36 import android.text.TextUtils; 37 import android.util.Log; 38 import android.view.LayoutInflater; 39 import android.view.View; 40 import android.view.ViewGroup; 41 import android.widget.CompoundButton; 42 import android.widget.CompoundButton.OnCheckedChangeListener; 43 44 import androidx.annotation.NonNull; 45 import androidx.annotation.Nullable; 46 import androidx.appcompat.app.AlertDialog; 47 import androidx.lifecycle.LifecycleOwner; 48 import androidx.preference.Preference; 49 import androidx.preference.Preference.OnPreferenceClickListener; 50 import androidx.preference.PreferenceScreen; 51 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.internal.telephony.Phone; 54 import com.android.settings.R; 55 import com.android.settings.SettingsActivity; 56 import com.android.settings.Utils; 57 import com.android.settings.core.SubSettingLauncher; 58 import com.android.settings.dashboard.DashboardFragment; 59 import com.android.settings.network.ims.WifiCallingQueryImsState; 60 import com.android.settings.network.telephony.AbstractSubscriptionPreferenceController; 61 import com.android.settings.network.telephony.wificalling.IWifiCallingRepository; 62 import com.android.settings.network.telephony.wificalling.WifiCallingRepository; 63 import com.android.settings.widget.SettingsMainSwitchPreference; 64 import com.android.settingslib.core.AbstractPreferenceController; 65 66 import kotlin.Unit; 67 68 import java.util.List; 69 70 /** 71 * This is the inner class of {@link WifiCallingSettings} fragment. 72 * The preference screen lets you enable/disable Wi-Fi Calling and change Wi-Fi Calling mode. 73 */ 74 public class WifiCallingSettingsForSub extends DashboardFragment 75 implements OnCheckedChangeListener, 76 Preference.OnPreferenceChangeListener { 77 private static final String TAG = "WifiCallingForSub"; 78 79 //String keys for preference lookup 80 private static final String SWITCH_BAR = "wifi_calling_switch_bar"; 81 private static final String BUTTON_WFC_MODE = "wifi_calling_mode"; 82 private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode"; 83 private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key"; 84 private static final String PREFERENCE_NO_OPTIONS_DESC = "no_options_description"; 85 86 @VisibleForTesting 87 static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1; 88 @VisibleForTesting 89 static final int REQUEST_CHECK_WFC_DISCLAIMER = 2; 90 91 public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP"; 92 public static final String EXTRA_SUB_ID = "EXTRA_SUB_ID"; 93 94 protected static final String FRAGMENT_BUNDLE_SUBID = "subId"; 95 96 public static final int LAUNCH_APP_ACTIVATE = 0; 97 public static final int LAUNCH_APP_UPDATE = 1; 98 99 //UI objects 100 private SettingsMainSwitchPreference mSwitchBar; 101 private ListWithEntrySummaryPreference mButtonWfcMode; 102 private ListWithEntrySummaryPreference mButtonWfcRoamingMode; 103 private Preference mUpdateAddress; 104 105 private boolean mEditableWfcMode = true; 106 private boolean mEditableWfcRoamingMode = true; 107 private boolean mUseWfcHomeModeForRoaming = false; 108 private boolean mOverrideWfcRoamingModeWhileUsingNtn = false; 109 110 private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 111 private ImsMmTelManager mImsMmTelManager; 112 private TelephonyManager mTelephonyManager; 113 114 private PhoneTelephonyCallback mTelephonyCallback; 115 116 private class PhoneTelephonyCallback extends TelephonyCallback implements 117 TelephonyCallback.CallStateListener { 118 /* 119 * Enable/disable controls when in/out of a call and depending on 120 * TTY mode and TTY support over VoLTE. 121 * @see android.telephony.PhoneStateListener#onCallStateChanged(int, 122 * java.lang.String) 123 */ 124 @Override onCallStateChanged(int state)125 public void onCallStateChanged(int state) { 126 final SettingsActivity activity = (SettingsActivity) getActivity(); 127 128 boolean isWfcEnabled = false; 129 boolean isCallStateIdle = false; 130 131 final SettingsMainSwitchPreference prefSwitch = (SettingsMainSwitchPreference) 132 getPreferenceScreen().findPreference(SWITCH_BAR); 133 if (prefSwitch != null) { 134 isWfcEnabled = prefSwitch.isChecked(); 135 isCallStateIdle = getTelephonyManagerForSub( 136 WifiCallingSettingsForSub.this.mSubId).getCallStateForSubscription() 137 == TelephonyManager.CALL_STATE_IDLE; 138 139 boolean isNonTtyOrTtyOnVolteEnabled = true; 140 if (isWfcEnabled || isCallStateIdle) { 141 isNonTtyOrTtyOnVolteEnabled = 142 queryImsState(WifiCallingSettingsForSub.this.mSubId) 143 .isAllowUserControl(); 144 } 145 146 isWfcEnabled = isWfcEnabled && isNonTtyOrTtyOnVolteEnabled; 147 prefSwitch.setEnabled(isCallStateIdle && isNonTtyOrTtyOnVolteEnabled); 148 } 149 150 boolean isWfcModeEditable = true; 151 boolean isWfcRoamingModeEditable = false; 152 if (isWfcEnabled && isCallStateIdle) { 153 final CarrierConfigManager configManager = (CarrierConfigManager) 154 activity.getSystemService(Context.CARRIER_CONFIG_SERVICE); 155 if (configManager != null) { 156 PersistableBundle b = configManager.getConfigForSubId( 157 WifiCallingSettingsForSub.this.mSubId); 158 if (b != null) { 159 isWfcModeEditable = b.getBoolean( 160 CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); 161 isWfcRoamingModeEditable = b.getBoolean( 162 CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); 163 } 164 } 165 } else { 166 isWfcModeEditable = false; 167 isWfcRoamingModeEditable = false; 168 } 169 170 final Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE); 171 if (pref != null) { 172 pref.setEnabled(isWfcModeEditable); 173 } 174 final Preference pref_roam = 175 getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE); 176 if (pref_roam != null) { 177 pref_roam.setEnabled(isWfcRoamingModeEditable 178 && !overrideWfcRoamingModeWhileUsingNtn()); 179 } 180 } 181 } 182 183 /* 184 * Launch carrier emergency address management activity 185 */ 186 private final OnPreferenceClickListener mUpdateAddressListener = 187 preference -> { 188 final Intent carrierAppIntent = getCarrierActivityIntent(); 189 if (carrierAppIntent != null) { 190 carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUNCH_APP_UPDATE); 191 startActivity(carrierAppIntent); 192 } 193 return true; 194 }; 195 196 @VisibleForTesting showAlert(Intent intent)197 void showAlert(Intent intent) { 198 final Context context = getActivity(); 199 200 final CharSequence title = 201 intent.getCharSequenceExtra(ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE); 202 final CharSequence message = 203 intent.getCharSequenceExtra(ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE); 204 205 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 206 builder.setMessage(message) 207 .setTitle(title) 208 .setIcon(android.R.drawable.ic_dialog_alert) 209 .setPositiveButton(android.R.string.ok, null); 210 final AlertDialog dialog = builder.create(); 211 dialog.show(); 212 } 213 214 private IntentFilter mIntentFilter; 215 216 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 217 @Override 218 public void onReceive(Context context, Intent intent) { 219 final String action = intent.getAction(); 220 if (action.equals(ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR)) { 221 // If this fragment is active then we are immediately 222 // showing alert on screen. There is no need to add 223 // notification in this case. 224 // 225 // In order to communicate to ImsPhone that it should 226 // not show notification, we are changing result code here. 227 setResultCode(Activity.RESULT_CANCELED); 228 229 showAlert(intent); 230 } 231 } 232 }; 233 234 @Override getMetricsCategory()235 public int getMetricsCategory() { 236 return SettingsEnums.WIFI_CALLING_FOR_SUB; 237 } 238 239 @Override getHelpResource()240 public int getHelpResource() { 241 // Return 0 to suppress help icon. The help will be populated by parent page. 242 return 0; 243 } 244 245 @VisibleForTesting getTelephonyManagerForSub(int subId)246 TelephonyManager getTelephonyManagerForSub(int subId) { 247 if (mTelephonyManager == null) { 248 mTelephonyManager = getContext().getSystemService(TelephonyManager.class); 249 } 250 return mTelephonyManager.createForSubscriptionId(subId); 251 } 252 253 @VisibleForTesting queryImsState(int subId)254 WifiCallingQueryImsState queryImsState(int subId) { 255 return new WifiCallingQueryImsState(getContext(), subId); 256 } 257 258 @VisibleForTesting getImsMmTelManager()259 ImsMmTelManager getImsMmTelManager() { 260 if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { 261 return null; 262 } 263 return ImsMmTelManager.createForSubscriptionId(mSubId); 264 } 265 266 @Override onCreate(Bundle savedInstanceState)267 public void onCreate(Bundle savedInstanceState) { 268 super.onCreate(savedInstanceState); 269 270 // SubId should always be specified when creating this fragment. Either through 271 // fragment.setArguments() or through savedInstanceState. 272 if (getArguments() != null && getArguments().containsKey(FRAGMENT_BUNDLE_SUBID)) { 273 mSubId = getArguments().getInt(FRAGMENT_BUNDLE_SUBID); 274 } else if (savedInstanceState != null) { 275 mSubId = savedInstanceState.getInt( 276 FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 277 } 278 279 mImsMmTelManager = getImsMmTelManager(); 280 281 mSwitchBar = (SettingsMainSwitchPreference) findPreference(SWITCH_BAR); 282 283 mButtonWfcMode = findPreference(BUTTON_WFC_MODE); 284 mButtonWfcMode.setOnPreferenceChangeListener(this); 285 286 mButtonWfcRoamingMode = findPreference(BUTTON_WFC_ROAMING_MODE); 287 mButtonWfcRoamingMode.setOnPreferenceChangeListener(this); 288 289 mUpdateAddress = findPreference(PREFERENCE_EMERGENCY_ADDRESS); 290 mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener); 291 292 mIntentFilter = new IntentFilter(); 293 mIntentFilter.addAction(ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR); 294 295 updateDescriptionForOptions( 296 List.of(mButtonWfcMode, mButtonWfcRoamingMode, mUpdateAddress)); 297 298 List<AbstractPreferenceController> subscriptionPreferenceControllers = 299 useGroup(AbstractSubscriptionPreferenceController.class); 300 subscriptionPreferenceControllers.forEach( 301 controller -> ((AbstractSubscriptionPreferenceController) controller).init(mSubId)); 302 } 303 304 @Override onSaveInstanceState(Bundle outState)305 public void onSaveInstanceState(Bundle outState) { 306 outState.putInt(FRAGMENT_BUNDLE_SUBID, mSubId); 307 super.onSaveInstanceState(outState); 308 } 309 310 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)311 public View onCreateView(LayoutInflater inflater, ViewGroup container, 312 Bundle savedInstanceState) { 313 314 final View view = inflater.inflate( 315 R.layout.wifi_calling_settings_preferences, container, false); 316 317 final ViewGroup prefs_container = view.findViewById(android.R.id.tabcontent); 318 Utils.prepareCustomPreferencesList(container, view, prefs_container, false); 319 final View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState); 320 prefs_container.addView(prefs); 321 322 return view; 323 } 324 325 @Override onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)326 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 327 super.onViewCreated(view, savedInstanceState); 328 getWifiCallingRepository().collectIsWifiCallingReadyFlow( 329 getLifecycleOwner(), (isWifiWifiCallingReadyFlow) -> { 330 if (!isWifiWifiCallingReadyFlow) { 331 // This screen is not allowed to be shown due to provisioning policy and 332 // should therefore be closed. 333 finish(); 334 } 335 return Unit.INSTANCE; 336 }); 337 } 338 339 @VisibleForTesting 340 @NonNull getWifiCallingRepository()341 IWifiCallingRepository getWifiCallingRepository() { 342 return new WifiCallingRepository(requireContext(), mSubId); 343 } 344 345 @VisibleForTesting 346 @NonNull getLifecycleOwner()347 LifecycleOwner getLifecycleOwner() { 348 return getViewLifecycleOwner(); 349 } 350 updateBody()351 private void updateBody() { 352 final CarrierConfigManager configManager = (CarrierConfigManager) 353 getSystemService(Context.CARRIER_CONFIG_SERVICE); 354 boolean isWifiOnlySupported = true; 355 356 if (configManager != null) { 357 final PersistableBundle b = configManager.getConfigForSubId(mSubId); 358 if (b != null) { 359 mEditableWfcMode = b.getBoolean( 360 CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); 361 mEditableWfcRoamingMode = b.getBoolean( 362 CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); 363 mUseWfcHomeModeForRoaming = b.getBoolean( 364 CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, 365 false); 366 isWifiOnlySupported = b.getBoolean( 367 CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true); 368 mOverrideWfcRoamingModeWhileUsingNtn = b.getBoolean( 369 CarrierConfigManager.KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, 370 true); 371 } 372 } 373 374 final Resources res = getResourcesForSubId(); 375 mButtonWfcMode.setTitle(res.getString(R.string.wifi_calling_mode_title)); 376 mButtonWfcMode.setDialogTitle(res.getString(R.string.wifi_calling_mode_dialog_title)); 377 mButtonWfcRoamingMode.setTitle(res.getString(R.string.wifi_calling_roaming_mode_title)); 378 mButtonWfcRoamingMode.setDialogTitle( 379 res.getString(R.string.wifi_calling_roaming_mode_dialog_title)); 380 381 if (isWifiOnlySupported) { 382 // Set string resources WITH option wifi only in mButtonWfcMode. 383 mButtonWfcMode.setEntries( 384 res.getStringArray(R.array.wifi_calling_mode_choices)); 385 mButtonWfcMode.setEntryValues(res.getStringArray(R.array.wifi_calling_mode_values)); 386 mButtonWfcMode.setEntrySummaries( 387 res.getStringArray(R.array.wifi_calling_mode_summaries)); 388 389 // Set string resources WITH option wifi only in mButtonWfcRoamingMode. 390 mButtonWfcRoamingMode.setEntries( 391 res.getStringArray(R.array.wifi_calling_mode_choices_v2)); 392 mButtonWfcRoamingMode.setEntryValues( 393 res.getStringArray(R.array.wifi_calling_mode_values)); 394 mButtonWfcRoamingMode.setEntrySummaries( 395 res.getStringArray(R.array.wifi_calling_mode_summaries)); 396 } else { 397 // Set string resources WITHOUT option wifi only in mButtonWfcMode. 398 mButtonWfcMode.setEntries( 399 res.getStringArray(R.array.wifi_calling_mode_choices_without_wifi_only)); 400 mButtonWfcMode.setEntryValues( 401 res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only)); 402 mButtonWfcMode.setEntrySummaries( 403 res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only)); 404 405 // Set string resources WITHOUT option wifi only in mButtonWfcRoamingMode. 406 mButtonWfcRoamingMode.setEntries( 407 res.getStringArray(R.array.wifi_calling_mode_choices_v2_without_wifi_only)); 408 mButtonWfcRoamingMode.setEntryValues( 409 res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only)); 410 mButtonWfcRoamingMode.setEntrySummaries( 411 res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only)); 412 } 413 414 // NOTE: Buttons will be enabled/disabled in mTelephonyCallback 415 final WifiCallingQueryImsState queryIms = queryImsState(mSubId); 416 final boolean wfcEnabled = queryIms.isEnabledByUser() 417 && queryIms.isAllowUserControl(); 418 mSwitchBar.setChecked(wfcEnabled); 419 int wfcMode = ImsMmTelManager.WIFI_MODE_UNKNOWN; 420 int wfcRoamingMode = ImsMmTelManager.WIFI_MODE_UNKNOWN; 421 boolean hasException = false; 422 try { 423 wfcMode = mImsMmTelManager.getVoWiFiModeSetting(); 424 wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting(); 425 } catch (IllegalArgumentException e) { 426 hasException = true; 427 Log.e(TAG, "getResourceIdForWfcMode: Exception", e); 428 } 429 mButtonWfcMode.setValue(Integer.toString(wfcMode)); 430 mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode)); 431 updateButtonWfcMode(wfcEnabled && !hasException, wfcMode, wfcRoamingMode); 432 } 433 434 @Override onResume()435 public void onResume() { 436 super.onResume(); 437 updateBody(); 438 Context context = getActivity(); 439 if (mTelephonyCallback == null && queryImsState(mSubId).isWifiCallingSupported()) { 440 mTelephonyCallback = new PhoneTelephonyCallback(); 441 getTelephonyManagerForSub(mSubId).registerTelephonyCallback( 442 context.getMainExecutor(), mTelephonyCallback); 443 mSwitchBar.addOnSwitchChangeListener(this); 444 } 445 context.registerReceiver(mIntentReceiver, mIntentFilter, 446 Context.RECEIVER_EXPORTED_UNAUDITED); 447 final Intent intent = getActivity().getIntent(); 448 if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) { 449 showAlert(intent); 450 } 451 } 452 453 @Override getPreferenceScreenResId()454 protected int getPreferenceScreenResId() { 455 return R.xml.wifi_calling_settings; 456 } 457 458 @Override onPause()459 public void onPause() { 460 super.onPause(); 461 Context context = getActivity(); 462 if (mTelephonyCallback != null) { 463 getTelephonyManagerForSub(mSubId).unregisterTelephonyCallback(mTelephonyCallback); 464 mTelephonyCallback = null; 465 mSwitchBar.removeOnSwitchChangeListener(this); 466 } 467 context.unregisterReceiver(mIntentReceiver); 468 } 469 470 /** 471 * Listens to the state change of the switch. 472 */ 473 @Override onCheckedChanged(CompoundButton buttonView, boolean isChecked)474 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 475 Log.d(TAG, "onSwitchChanged(" + isChecked + ")"); 476 477 if (!isChecked) { 478 updateWfcMode(false); 479 return; 480 } 481 482 // Launch disclaimer fragment before turning on WFC 483 final Context context = getActivity(); 484 final Bundle args = new Bundle(); 485 args.putInt(EXTRA_SUB_ID, mSubId); 486 new SubSettingLauncher(context) 487 .setDestination(WifiCallingDisclaimerFragment.class.getName()) 488 .setArguments(args) 489 .setTitleRes(R.string.wifi_calling_settings_title) 490 .setSourceMetricsCategory(getMetricsCategory()) 491 .setResultListener(this, REQUEST_CHECK_WFC_DISCLAIMER) 492 .launch(); 493 } 494 getCarrierActivityIntent()495 private @Nullable Intent getCarrierActivityIntent() { 496 return getCarrierActivityIntent(getActivity(), mSubId); 497 } 498 499 /* 500 * Get the Intent to launch carrier emergency address management activity. 501 * Return null when no activity found. 502 */ getCarrierActivityIntent(Context context, int subId)503 static @Nullable Intent getCarrierActivityIntent(Context context, int subId) { 504 // Retrieve component name from carrier config 505 final CarrierConfigManager configManager = 506 context.getSystemService(CarrierConfigManager.class); 507 if (configManager == null) return null; 508 509 final PersistableBundle bundle = configManager.getConfigForSubId(subId); 510 if (bundle == null) return null; 511 512 final String carrierApp = bundle.getString( 513 CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); 514 if (TextUtils.isEmpty(carrierApp)) return null; 515 516 final ComponentName componentName = ComponentName.unflattenFromString(carrierApp); 517 if (componentName == null) return null; 518 519 // Build and return intent 520 final Intent intent = new Intent(); 521 intent.setComponent(componentName); 522 intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); 523 return intent; 524 } 525 526 /* 527 * Turn on/off WFC mode with ImsManager and update UI accordingly 528 */ updateWfcMode(boolean wfcEnabled)529 private void updateWfcMode(boolean wfcEnabled) { 530 Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")"); 531 boolean hasException = false; 532 try { 533 mImsMmTelManager.setVoWiFiSettingEnabled(wfcEnabled); 534 } catch (IllegalArgumentException e) { 535 Log.e(TAG, "updateWfcMode: Exception", e); 536 hasException = true; 537 } 538 539 int wfcMode = ImsMmTelManager.WIFI_MODE_UNKNOWN; 540 int wfcRoamingMode = ImsMmTelManager.WIFI_MODE_UNKNOWN; 541 if (!hasException) { 542 try { 543 wfcMode = mImsMmTelManager.getVoWiFiModeSetting(); 544 wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting(); 545 } catch (IllegalArgumentException e) { 546 hasException = true; 547 Log.e(TAG, "updateWfcMode: Exception", e); 548 } 549 } 550 updateButtonWfcMode(wfcEnabled && !hasException, wfcMode, wfcRoamingMode); 551 if (wfcEnabled) { 552 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode); 553 } else { 554 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1); 555 } 556 } 557 558 @Override onActivityResult(int requestCode, int resultCode, Intent data)559 public void onActivityResult(int requestCode, int resultCode, Intent data) { 560 super.onActivityResult(requestCode, resultCode, data); 561 Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode); 562 switch (requestCode) { 563 case REQUEST_CHECK_WFC_EMERGENCY_ADDRESS: 564 if (resultCode == Activity.RESULT_OK) { 565 updateWfcMode(true); 566 } 567 break; 568 case REQUEST_CHECK_WFC_DISCLAIMER: 569 if (resultCode == Activity.RESULT_OK) { 570 // Call address management activity before turning on WFC 571 final Intent carrierAppIntent = getCarrierActivityIntent(); 572 if (carrierAppIntent != null) { 573 carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUNCH_APP_ACTIVATE); 574 startActivityForResult(carrierAppIntent, 575 REQUEST_CHECK_WFC_EMERGENCY_ADDRESS); 576 } else { 577 updateWfcMode(true); 578 } 579 } 580 break; 581 default: 582 Log.e(TAG, "Unexpected request: " + requestCode); 583 break; 584 } 585 } 586 587 @Override getLogTag()588 protected String getLogTag() { 589 return TAG; 590 } 591 updateButtonWfcMode(boolean wfcEnabled, int wfcMode, int wfcRoamingMode)592 private void updateButtonWfcMode(boolean wfcEnabled, 593 int wfcMode, int wfcRoamingMode) { 594 mButtonWfcMode.setSummary(getWfcModeSummary(wfcMode)); 595 mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode); 596 // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. 597 mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode 598 && !overrideWfcRoamingModeWhileUsingNtn()); 599 600 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 601 final boolean updateAddressEnabled = (getCarrierActivityIntent() != null); 602 if (wfcEnabled) { 603 // Don't show WFC (home) preference if it's not editable. 604 mButtonWfcMode.setVisible(mEditableWfcMode); 605 // Don't show WFC roaming preference if it's not editable. 606 mButtonWfcRoamingMode.setVisible( 607 mEditableWfcRoamingMode && !mUseWfcHomeModeForRoaming); 608 mUpdateAddress.setVisible(updateAddressEnabled); 609 } else { 610 mButtonWfcMode.setVisible(false); 611 mButtonWfcRoamingMode.setVisible(false); 612 mUpdateAddress.setVisible(false); 613 } 614 updateDescriptionForOptions( 615 List.of(mButtonWfcMode, mButtonWfcRoamingMode, mUpdateAddress)); 616 } 617 updateDescriptionForOptions(List<Preference> visibleOptions)618 private void updateDescriptionForOptions(List<Preference> visibleOptions) { 619 LinkifyDescriptionPreference pref = findPreference(PREFERENCE_NO_OPTIONS_DESC); 620 if (pref == null) { 621 return; 622 } 623 624 boolean optionsAvailable = visibleOptions.stream().anyMatch(Preference::isVisible); 625 if (!optionsAvailable) { 626 final Resources res = getResourcesForSubId(); 627 String emptyViewText = res.getString(R.string.wifi_calling_off_explanation, 628 res.getString(R.string.wifi_calling_off_explanation_2)); 629 pref.setSummary(emptyViewText); 630 } 631 pref.setVisible(!optionsAvailable); 632 } 633 634 @Override onPreferenceChange(Preference preference, Object newValue)635 public boolean onPreferenceChange(Preference preference, Object newValue) { 636 boolean hasException = false; 637 638 if (preference == mButtonWfcMode) { 639 Log.d(TAG, "onPreferenceChange mButtonWfcMode " + newValue); 640 mButtonWfcMode.setValue((String) newValue); 641 final int buttonMode = Integer.valueOf((String) newValue); 642 int currentWfcMode = ImsMmTelManager.WIFI_MODE_UNKNOWN; 643 try { 644 currentWfcMode = mImsMmTelManager.getVoWiFiModeSetting(); 645 } catch (IllegalArgumentException e) { 646 hasException = true; 647 Log.e(TAG, "onPreferenceChange: Exception", e); 648 } 649 if (buttonMode != currentWfcMode && !hasException) { 650 try { 651 mImsMmTelManager.setVoWiFiModeSetting(buttonMode); 652 mButtonWfcMode.setSummary(getWfcModeSummary(buttonMode)); 653 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); 654 655 if (mUseWfcHomeModeForRoaming) { 656 mImsMmTelManager.setVoWiFiRoamingModeSetting(buttonMode); 657 // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value 658 } 659 } catch (IllegalArgumentException e) { 660 Log.e(TAG, "onPreferenceChange: Exception", e); 661 } 662 } 663 } else if (preference == mButtonWfcRoamingMode) { 664 mButtonWfcRoamingMode.setValue((String) newValue); 665 final int buttonMode = Integer.valueOf((String) newValue); 666 int currentMode = ImsMmTelManager.WIFI_MODE_UNKNOWN; 667 try { 668 currentMode = mImsMmTelManager.getVoWiFiRoamingModeSetting(); 669 } catch (IllegalArgumentException e) { 670 hasException = true; 671 Log.e(TAG, "updateWfcMode: Exception", e); 672 } 673 if (buttonMode != currentMode && !hasException) { 674 try { 675 mImsMmTelManager.setVoWiFiRoamingModeSetting(buttonMode); 676 // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected 677 // value. 678 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); 679 } catch (IllegalArgumentException e) { 680 Log.e(TAG, "onPreferenceChange: Exception", e); 681 } 682 } 683 } 684 return true; 685 } 686 getWfcModeSummary(int wfcMode)687 private CharSequence getWfcModeSummary(int wfcMode) { 688 int resId = com.android.internal.R.string.wifi_calling_off_summary; 689 if (queryImsState(mSubId).isEnabledByUser()) { 690 switch (wfcMode) { 691 case ImsMmTelManager.WIFI_MODE_WIFI_ONLY: 692 resId = com.android.internal.R.string.wfc_mode_wifi_only_summary; 693 break; 694 case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED: 695 resId = com.android.internal.R.string.wfc_mode_cellular_preferred_summary; 696 break; 697 case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: 698 resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary; 699 break; 700 default: 701 Log.e(TAG, "Unexpected WFC mode value: " + wfcMode); 702 } 703 } 704 return getResourcesForSubId().getString(resId); 705 } 706 707 @VisibleForTesting getResourcesForSubId()708 Resources getResourcesForSubId() { 709 return SubscriptionManager.getResourcesForSubId(getContext(), mSubId); 710 } 711 712 /** 713 * Determine whether to override roaming Wi-Fi calling preference when device is connected to 714 * non-terrestrial network. 715 * 716 * @return {@code true} if phone is connected to non-terrestrial network and if 717 * {@link CarrierConfigManager#KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL} is true, 718 * {@code false} otherwise. 719 */ overrideWfcRoamingModeWhileUsingNtn()720 private boolean overrideWfcRoamingModeWhileUsingNtn() { 721 TelephonyManager tm = getTelephonyManagerForSub(mSubId); 722 ServiceState serviceState = tm.getServiceState(); 723 if (serviceState == null) { 724 return false; 725 } 726 727 if (!serviceState.isUsingNonTerrestrialNetwork()) { 728 return false; 729 } 730 731 return mOverrideWfcRoamingModeWhileUsingNtn; 732 } 733 734 @Override getPreferenceScreenBindingKey(@onNull Context context)735 public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) { 736 return WifiCallingScreen.KEY; 737 } 738 } 739