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