1 /*
2  * Copyright (C) 2018 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 static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
20 
21 import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
22 import static com.android.settings.slices.CustomSliceRegistry.WIFI_CALLING_PREFERENCE_URI;
23 import static com.android.settings.slices.CustomSliceRegistry.WIFI_CALLING_URI;
24 
25 import android.app.PendingIntent;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.res.Resources;
30 import android.net.Uri;
31 import android.os.PersistableBundle;
32 import android.provider.Settings;
33 import android.telephony.CarrierConfigManager;
34 import android.telephony.SubscriptionManager;
35 import android.telephony.ims.ImsMmTelManager;
36 import android.text.TextUtils;
37 import android.util.Log;
38 
39 import androidx.annotation.VisibleForTesting;
40 import androidx.core.graphics.drawable.IconCompat;
41 import androidx.slice.Slice;
42 import androidx.slice.builders.ListBuilder;
43 import androidx.slice.builders.ListBuilder.RowBuilder;
44 import androidx.slice.builders.SliceAction;
45 
46 import com.android.settings.R;
47 import com.android.settings.Utils;
48 import com.android.settings.network.ims.WifiCallingQueryImsState;
49 import com.android.settings.slices.SliceBroadcastReceiver;
50 
51 import java.util.concurrent.Callable;
52 import java.util.concurrent.ExecutionException;
53 import java.util.concurrent.ExecutorService;
54 import java.util.concurrent.Executors;
55 import java.util.concurrent.FutureTask;
56 import java.util.concurrent.TimeUnit;
57 import java.util.concurrent.TimeoutException;
58 
59 /**
60  * Helper class to control slices for wifi calling settings.
61  */
62 public class WifiCallingSliceHelper {
63 
64     private static final String TAG = "WifiCallingSliceHelper";
65 
66     /**
67      * Settings slice path to wifi calling setting.
68      */
69     public static final String PATH_WIFI_CALLING = "wifi_calling";
70 
71     /**
72      * Settings slice path to wifi calling preference setting.
73      */
74     public static final String PATH_WIFI_CALLING_PREFERENCE =
75             "wifi_calling_preference";
76 
77     /**
78      * Action passed for changes to wifi calling slice (toggle).
79      */
80     public static final String ACTION_WIFI_CALLING_CHANGED =
81             "com.android.settings.wifi.calling.action.WIFI_CALLING_CHANGED";
82 
83     /**
84      * Action passed when user selects wifi only preference.
85      */
86     public static final String ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY =
87             "com.android.settings.slice.action.WIFI_CALLING_PREFERENCE_WIFI_ONLY";
88     /**
89      * Action passed when user selects wifi preferred preference.
90      */
91     public static final String ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED =
92             "com.android.settings.slice.action.WIFI_CALLING_PREFERENCE_WIFI_PREFERRED";
93     /**
94      * Action passed when user selects cellular preferred preference.
95      */
96     public static final String ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED =
97             "com.android.settings.slice.action.WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED";
98 
99     /**
100      * Action for Wifi calling Settings activity which
101      * allows setting configuration for Wifi calling
102      * related settings
103      */
104     public static final String ACTION_WIFI_CALLING_SETTINGS_ACTIVITY =
105             "android.settings.WIFI_CALLING_SETTINGS";
106 
107     /**
108      * Timeout for querying wifi calling setting from ims manager.
109      */
110     private static final int TIMEOUT_MILLIS = 2000;
111 
112     private final Context mContext;
113 
114     @VisibleForTesting
WifiCallingSliceHelper(Context context)115     public WifiCallingSliceHelper(Context context) {
116         mContext = context;
117     }
118 
119     /**
120      * Returns Slice object for wifi calling settings.
121      *
122      * If wifi calling is being turned on and if wifi calling activation is needed for the current
123      * carrier, this method will return Slice with instructions to go to Settings App.
124      *
125      * If wifi calling is not supported for the current carrier, this method will return slice with
126      * not supported message.
127      *
128      * If wifi calling setting can be changed, this method will return the slice to toggle wifi
129      * calling option with ACTION_WIFI_CALLING_CHANGED as endItem.
130      */
createWifiCallingSlice(Uri sliceUri)131     public Slice createWifiCallingSlice(Uri sliceUri) {
132         final int subId = getDefaultVoiceSubId();
133 
134         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
135             Log.d(TAG, "Invalid subscription Id");
136             return null;
137         }
138 
139         if (!queryImsState(subId).isWifiCallingProvisioned()) {
140             Log.d(TAG, "Wifi calling is either not provisioned or not enabled by Platform");
141             return null;
142         }
143 
144         final boolean isWifiCallingEnabled = isWifiCallingEnabled();
145         final Intent activationAppIntent =
146                 getWifiCallingCarrierActivityIntent(subId);
147 
148         // Send this actionable wifi calling slice to toggle the setting
149         // only when there is no need for wifi calling activation with the server
150         if (activationAppIntent != null && !isWifiCallingEnabled) {
151             Log.d(TAG, "Needs Activation");
152             // Activation needed for the next action of the user
153             // Give instructions to go to settings app
154             final Resources res = getResourcesForSubId(subId);
155             return getNonActionableWifiCallingSlice(
156                     res.getText(R.string.wifi_calling_settings_title),
157                     res.getText(R.string.wifi_calling_settings_activation_instructions),
158                     sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY));
159         }
160         return getWifiCallingSlice(sliceUri, isWifiCallingEnabled, subId);
161     }
162 
isWifiCallingEnabled()163     private boolean isWifiCallingEnabled() {
164         final WifiCallingQueryImsState queryState = queryImsState(getDefaultVoiceSubId());
165         return queryState.isEnabledByUser() && queryState.isAllowUserControl();
166     }
167 
168     /**
169      * Builds a toggle slice where the intent takes you to the wifi calling page and the toggle
170      * enables/disables wifi calling.
171      */
getWifiCallingSlice(Uri sliceUri, boolean isWifiCallingEnabled, int subId)172     private Slice getWifiCallingSlice(Uri sliceUri, boolean isWifiCallingEnabled, int subId) {
173         final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
174         final Resources res = getResourcesForSubId(subId);
175 
176         return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
177                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
178                 .addRow(new RowBuilder()
179                         .setTitle(res.getText(R.string.wifi_calling_settings_title))
180                         .addEndItem(
181                                 SliceAction.createToggle(
182                                         getBroadcastIntent(ACTION_WIFI_CALLING_CHANGED,
183                                                 isWifiCallingEnabled),
184                                         null /* actionTitle */, isWifiCallingEnabled))
185                         .setPrimaryAction(SliceAction.createDeeplink(
186                                 getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY),
187                                 icon,
188                                 ListBuilder.ICON_IMAGE,
189                                 res.getText(R.string.wifi_calling_settings_title))))
190                 .build();
191     }
192 
193     /**
194      * Returns Slice object for wifi calling preference.
195      *
196      * If wifi calling is not turned on, this method will return a slice to turn on wifi calling.
197      *
198      * If wifi calling preference is not user editable, this method will return a slice to display
199      * appropriate message.
200      *
201      * If wifi calling preference can be changed, this method will return a slice with 3 or 4 rows:
202      * Header Row: current preference settings
203      * Row 1: wifi only option with ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY, if wifi only option
204      * is editable
205      * Row 2: wifi preferred option with ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED
206      * Row 3: cellular preferred option with ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED
207      */
createWifiCallingPreferenceSlice(Uri sliceUri)208     public Slice createWifiCallingPreferenceSlice(Uri sliceUri) {
209         final int subId = getDefaultVoiceSubId();
210 
211         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
212             Log.d(TAG, "Invalid Subscription Id");
213             return null;
214         }
215 
216         final boolean isWifiCallingPrefEditable = isCarrierConfigManagerKeyEnabled(
217                 CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, subId, false);
218         final boolean isWifiOnlySupported = isCarrierConfigManagerKeyEnabled(
219                 CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, subId, true);
220 
221         if (!isWifiCallingPrefEditable) {
222             Log.d(TAG, "Wifi calling preference is not editable");
223             return null;
224         }
225 
226         if (!queryImsState(subId).isWifiCallingProvisioned()) {
227             Log.d(TAG, "Wifi calling is either not provisioned or not enabled by platform");
228             return null;
229         }
230 
231         boolean isWifiCallingEnabled = false;
232         int wfcMode = -1;
233         try {
234             final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
235             isWifiCallingEnabled = isWifiCallingEnabled();
236             wfcMode = getWfcMode(imsMmTelManager);
237         } catch (InterruptedException | ExecutionException | TimeoutException e) {
238             Log.e(TAG, "Unable to get wifi calling preferred mode", e);
239             return null;
240         }
241         if (!isWifiCallingEnabled) {
242             // wifi calling is not enabled. Ask user to enable wifi calling
243             final Resources res = getResourcesForSubId(subId);
244             return getNonActionableWifiCallingSlice(
245                     res.getText(R.string.wifi_calling_mode_title),
246                     res.getText(R.string.wifi_calling_turn_on),
247                     sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY));
248         }
249         // Return the slice to change wifi calling preference
250         return getWifiCallingPreferenceSlice(
251                 isWifiOnlySupported, wfcMode, sliceUri, subId);
252     }
253 
254     /**
255      * Returns actionable wifi calling preference slice.
256      *
257      * @param isWifiOnlySupported adds row for wifi only if this is true
258      * @param currentWfcPref      current Preference {@link ImsConfig}
259      * @param sliceUri            sliceUri
260      * @param subId               subscription id
261      * @return Slice for actionable wifi calling preference settings
262      */
getWifiCallingPreferenceSlice(boolean isWifiOnlySupported, int currentWfcPref, Uri sliceUri, int subId)263     private Slice getWifiCallingPreferenceSlice(boolean isWifiOnlySupported,
264             int currentWfcPref,
265             Uri sliceUri,
266             int subId) {
267         final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
268         final Resources res = getResourcesForSubId(subId);
269         // Top row shows information on current preference state
270         final ListBuilder listBuilder = new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
271                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext));
272         final ListBuilder.HeaderBuilder headerBuilder = new ListBuilder.HeaderBuilder()
273                 .setTitle(res.getText(R.string.wifi_calling_mode_title))
274                 .setPrimaryAction(SliceAction.createDeeplink(
275                         getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY),
276                         icon,
277                         ListBuilder.ICON_IMAGE,
278                         res.getText(R.string.wifi_calling_mode_title)));
279         if (!Utils.isSettingsIntelligence(mContext)) {
280             headerBuilder.setSubtitle(getWifiCallingPreferenceSummary(currentWfcPref, subId));
281         }
282         listBuilder.setHeader(headerBuilder);
283 
284         if (isWifiOnlySupported) {
285             listBuilder.addRow(wifiPreferenceRowBuilder(listBuilder,
286                     com.android.internal.R.string.wfc_mode_wifi_only_summary,
287                     ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY,
288                     currentWfcPref == ImsMmTelManager.WIFI_MODE_WIFI_ONLY, subId));
289         }
290 
291         listBuilder.addRow(wifiPreferenceRowBuilder(listBuilder,
292                 com.android.internal.R.string.wfc_mode_wifi_preferred_summary,
293                 ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED,
294                 currentWfcPref == ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED, subId));
295 
296         listBuilder.addRow(wifiPreferenceRowBuilder(listBuilder,
297                 com.android.internal.R.string.wfc_mode_cellular_preferred_summary,
298                 ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED,
299                 currentWfcPref == ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, subId));
300 
301         return listBuilder.build();
302     }
303 
304     /**
305      * Returns RowBuilder for a new row containing specific wifi calling preference.
306      *
307      * @param listBuilder          ListBuilder that will be the parent for this RowBuilder
308      * @param preferenceTitleResId resource Id for the preference row title
309      * @param action               action to be added for the row
310      * @param subId                subscription id
311      * @return RowBuilder for the row
312      */
wifiPreferenceRowBuilder(ListBuilder listBuilder, int preferenceTitleResId, String action, boolean checked, int subId)313     private RowBuilder wifiPreferenceRowBuilder(ListBuilder listBuilder,
314             int preferenceTitleResId, String action, boolean checked, int subId) {
315         final IconCompat icon =
316                 IconCompat.createWithResource(mContext, R.drawable.radio_button_check);
317         final Resources res = getResourcesForSubId(subId);
318         return new RowBuilder()
319                 .setTitle(res.getText(preferenceTitleResId))
320                 .setTitleItem(SliceAction.createToggle(getBroadcastIntent(action, checked),
321                         icon, res.getText(preferenceTitleResId), checked));
322     }
323 
324 
325     /**
326      * Returns the String describing wifi calling preference mentioned in wfcMode
327      *
328      * @param wfcMode ImsConfig constant for the preference {@link ImsConfig}
329      * @return summary/name of the wifi calling preference
330      */
getWifiCallingPreferenceSummary(int wfcMode, int subId)331     private CharSequence getWifiCallingPreferenceSummary(int wfcMode, int subId) {
332         final Resources res = getResourcesForSubId(subId);
333         switch (wfcMode) {
334             case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
335                 return res.getText(
336                         com.android.internal.R.string.wfc_mode_wifi_only_summary);
337             case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
338                 return res.getText(
339                         com.android.internal.R.string.wfc_mode_wifi_preferred_summary);
340             case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
341                 return res.getText(
342                         com.android.internal.R.string.wfc_mode_cellular_preferred_summary);
343             default:
344                 return null;
345         }
346     }
347 
getImsMmTelManager(int subId)348     protected ImsMmTelManager getImsMmTelManager(int subId) {
349         return ImsMmTelManager.createForSubscriptionId(subId);
350     }
351 
getWfcMode(ImsMmTelManager imsMmTelManager)352     private int getWfcMode(ImsMmTelManager imsMmTelManager)
353             throws InterruptedException, ExecutionException, TimeoutException {
354         final FutureTask<Integer> wfcModeTask = new FutureTask<>(new Callable<Integer>() {
355             @Override
356             public Integer call() {
357                 return imsMmTelManager.getVoWiFiModeSetting();
358             }
359         });
360         final ExecutorService executor = Executors.newSingleThreadExecutor();
361         executor.execute(wfcModeTask);
362         return wfcModeTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
363     }
364 
365     /**
366      * Handles wifi calling setting change from wifi calling slice and posts notification. Should be
367      * called when intent action is ACTION_WIFI_CALLING_CHANGED. Executed in @WorkerThread
368      *
369      * @param intent action performed
370      */
handleWifiCallingChanged(Intent intent)371     public void handleWifiCallingChanged(Intent intent) {
372         final int subId = getDefaultVoiceSubId();
373 
374         if (SubscriptionManager.isValidSubscriptionId(subId)
375                 && intent.hasExtra(EXTRA_TOGGLE_STATE)) {
376             final WifiCallingQueryImsState queryState = queryImsState(subId);
377             if (queryState.isWifiCallingProvisioned()) {
378                 final boolean currentValue = isWifiCallingEnabled();
379                 final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
380                         currentValue);
381                 final Intent activationAppIntent =
382                         getWifiCallingCarrierActivityIntent(subId);
383                 if ((newValue == currentValue) && activationAppIntent == null) {
384                     // If either the action is to turn off wifi calling setting
385                     // or there is no activation involved - Update the setting
386                     final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
387                     imsMmTelManager.setVoWiFiSettingEnabled(!newValue);
388                 } else {
389                     Log.w(TAG, "action not taken: subId " + subId
390                             + " from " + currentValue + " to " + newValue);
391                 }
392             } else {
393                 Log.w(TAG, "action not taken: subId " + subId + " needs provision");
394             }
395         } else {
396             Log.w(TAG, "action not taken: subId " + subId);
397         }
398 
399         // notify change in slice in any case to get re-queried. This would result in displaying
400         // appropriate message with the updated setting.
401         mContext.getContentResolver().notifyChange(WIFI_CALLING_URI, null);
402     }
403 
404     /**
405      * Handles wifi calling preference Setting change from wifi calling preference Slice and posts
406      * notification for the change. Should be called when intent action is one of the below
407      * ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY
408      * ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED
409      * ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED
410      *
411      * @param intent intent
412      */
handleWifiCallingPreferenceChanged(Intent intent)413     public void handleWifiCallingPreferenceChanged(Intent intent) {
414         final int subId = getDefaultVoiceSubId();
415         final int errorValue = -1;
416 
417         if (SubscriptionManager.isValidSubscriptionId(subId)) {
418             final boolean isWifiCallingPrefEditable = isCarrierConfigManagerKeyEnabled(
419                     CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, subId, false);
420             final boolean isWifiOnlySupported = isCarrierConfigManagerKeyEnabled(
421                     CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, subId, true);
422 
423             final WifiCallingQueryImsState queryState = queryImsState(subId);
424             if (isWifiCallingPrefEditable
425                     && queryState.isWifiCallingProvisioned()
426                     && queryState.isEnabledByUser()
427                     && queryState.isAllowUserControl()) {
428                 // Change the preference only when wifi calling is enabled
429                 // And when wifi calling preference is editable for the current carrier
430                 final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
431                 final int currentValue = imsMmTelManager.getVoWiFiModeSetting();
432                 int newValue = errorValue;
433                 switch (intent.getAction()) {
434                     case ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY:
435                         if (isWifiOnlySupported) {
436                             // change to wifi_only when wifi_only is enabled.
437                             newValue = ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
438                         }
439                         break;
440                     case ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED:
441                         newValue = ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED;
442                         break;
443                     case ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED:
444                         newValue = ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED;
445                         break;
446                 }
447                 if (newValue != errorValue && newValue != currentValue) {
448                     // Update the setting only when there is a valid update
449                     imsMmTelManager.setVoWiFiModeSetting(newValue);
450                 }
451             }
452         }
453 
454         // notify change in slice in any case to get re-queried. This would result in displaying
455         // appropriate message.
456         mContext.getContentResolver().notifyChange(WIFI_CALLING_PREFERENCE_URI, null);
457     }
458 
459     /**
460      * Returns Slice with the title and subtitle provided as arguments with wifi signal Icon.
461      *
462      * @param title    Title of the slice
463      * @param subtitle Subtitle of the slice
464      * @param sliceUri slice uri
465      * @return Slice with title and subtitle
466      */
getNonActionableWifiCallingSlice(CharSequence title, CharSequence subtitle, Uri sliceUri, PendingIntent primaryActionIntent)467     private Slice getNonActionableWifiCallingSlice(CharSequence title, CharSequence subtitle,
468             Uri sliceUri, PendingIntent primaryActionIntent) {
469         final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
470         final RowBuilder rowBuilder = new RowBuilder()
471                 .setTitle(title)
472                 .setPrimaryAction(SliceAction.createDeeplink(
473                         primaryActionIntent, icon, ListBuilder.SMALL_IMAGE,
474                         title));
475         if (!Utils.isSettingsIntelligence(mContext)) {
476             rowBuilder.setSubtitle(subtitle);
477         }
478         return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
479                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
480                 .addRow(rowBuilder)
481                 .build();
482     }
483 
484     /**
485      * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise.
486      */
isCarrierConfigManagerKeyEnabled(String key, int subId, boolean defaultValue)487     protected boolean isCarrierConfigManagerKeyEnabled(String key, int subId,
488             boolean defaultValue) {
489         final CarrierConfigManager configManager = getCarrierConfigManager(mContext);
490         boolean ret = false;
491         if (configManager != null) {
492             final PersistableBundle bundle = configManager.getConfigForSubId(subId);
493             if (bundle != null) {
494                 ret = bundle.getBoolean(key, defaultValue);
495             }
496         }
497         return ret;
498     }
499 
getCarrierConfigManager(Context mContext)500     protected CarrierConfigManager getCarrierConfigManager(Context mContext) {
501         return mContext.getSystemService(CarrierConfigManager.class);
502     }
503 
504     /**
505      * Returns the current default voice subId obtained from SubscriptionManager
506      */
getDefaultVoiceSubId()507     protected int getDefaultVoiceSubId() {
508         return SubscriptionManager.getDefaultVoiceSubscriptionId();
509     }
510 
511     /**
512      * Returns Intent of the activation app required to activate wifi calling or null if there is no
513      * need for activation.
514      */
getWifiCallingCarrierActivityIntent(int subId)515     protected Intent getWifiCallingCarrierActivityIntent(int subId) {
516         final CarrierConfigManager configManager = getCarrierConfigManager(mContext);
517         if (configManager == null) {
518             return null;
519         }
520 
521         final PersistableBundle bundle = configManager.getConfigForSubId(subId);
522         if (bundle == null) {
523             return null;
524         }
525 
526         final String carrierApp = bundle.getString(
527                 CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING);
528         if (TextUtils.isEmpty(carrierApp)) {
529             return null;
530         }
531 
532         final ComponentName componentName = ComponentName.unflattenFromString(carrierApp);
533         if (componentName == null) {
534             return null;
535         }
536 
537         final Intent intent = new Intent();
538         intent.setComponent(componentName);
539         return intent;
540     }
541 
542     /**
543      * @return {@link PendingIntent} to the Settings home page.
544      */
getSettingsIntent(Context context)545     public static PendingIntent getSettingsIntent(Context context) {
546         final Intent intent = new Intent(Settings.ACTION_SETTINGS);
547         return PendingIntent.getActivity(context, 0 /* requestCode */, intent,
548                 PendingIntent.FLAG_IMMUTABLE);
549     }
550 
551     /**
552      * Create PendingIntent for Slice.
553      * Note: SliceAction#createDeeplink() didn't support toggle status so far,
554      *       therefore, embedding toggle status within PendingIntent.
555      *
556      * @param action Slice action
557      * @param isChecked Status when Slice created.
558      * @return PendingIntent
559      */
getBroadcastIntent(String action, boolean isChecked)560     private PendingIntent getBroadcastIntent(String action, boolean isChecked) {
561         final Intent intent = new Intent(action);
562         intent.setClass(mContext, SliceBroadcastReceiver.class);
563         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
564         intent.putExtra(EXTRA_TOGGLE_STATE, isChecked);
565         return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
566                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
567     }
568 
569     /**
570      * Returns PendingIntent to start activity specified by action
571      */
getActivityIntent(String action)572     private PendingIntent getActivityIntent(String action) {
573         final Intent intent = new Intent(action);
574         intent.setPackage(SETTINGS_PACKAGE_NAME);
575         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
576         return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent,
577                 PendingIntent.FLAG_IMMUTABLE);
578     }
579 
getResourcesForSubId(int subId)580     private Resources getResourcesForSubId(int subId) {
581         return SubscriptionManager.getResourcesForSubId(mContext, subId);
582     }
583 
584     @VisibleForTesting
queryImsState(int subId)585     WifiCallingQueryImsState queryImsState(int subId) {
586         return new WifiCallingQueryImsState(mContext, subId);
587     }
588 }
589