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