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