• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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