1 /*
2  * Copyright (C) 2014 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.sim;
18 
19 import android.app.Activity;
20 import android.app.settings.SettingsEnums;
21 import android.content.Intent;
22 import android.content.SharedPreferences;
23 import android.os.Bundle;
24 import android.os.PersistableBundle;
25 import android.telecom.PhoneAccountHandle;
26 import android.telecom.TelecomManager;
27 import android.telephony.CarrierConfigManager;
28 import android.telephony.SubscriptionManager;
29 import android.telephony.TelephonyManager;
30 import android.telephony.ims.ImsException;
31 import android.telephony.ims.ImsManager;
32 import android.telephony.ims.ImsMmTelManager;
33 import android.util.Log;
34 import android.view.WindowManager;
35 import android.widget.Toast;
36 
37 import androidx.annotation.NonNull;
38 import androidx.fragment.app.Fragment;
39 import androidx.fragment.app.FragmentActivity;
40 import androidx.fragment.app.FragmentManager;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.settings.R;
44 import com.android.settings.flags.Flags;
45 import com.android.settings.network.CarrierConfigCache;
46 import com.android.settings.network.SubscriptionUtil;
47 import com.android.settings.network.ims.WifiCallingQueryImsState;
48 import com.android.settings.network.telephony.MobileNetworkUtils;
49 import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
50 import com.android.settings.overlay.FeatureFactory;
51 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
52 
53 import java.util.List;
54 
55 /**
56  * This activity provides singleton semantics per dialog type for showing various kinds of
57  * dialogs asking the user to make choices about which SIM to use for various services
58  * (calls, SMS, and data).
59  */
60 public class SimDialogActivity extends FragmentActivity {
61     private static String TAG = "SimDialogActivity";
62 
63     public static String PREFERRED_SIM = "preferred_sim";
64     public static String DIALOG_TYPE_KEY = "dialog_type";
65     // sub ID returned from startActivityForResult
66     public static String RESULT_SUB_ID = "result_sub_id";
67     public static final int INVALID_PICK = -1;
68     public static final int DATA_PICK = 0;
69     public static final int CALLS_PICK = 1;
70     public static final int SMS_PICK = 2;
71     public static final int PREFERRED_PICK = 3;
72     // Show the "select SMS subscription" dialog, but don't save as default, just return a result
73     public static final int SMS_PICK_FOR_MESSAGE = 4;
74     // Dismiss the current dialog and finish the activity.
75     public static final int PICK_DISMISS = 5;
76     // Show auto data switch dialog(when user enables multi-SIM)
77     public static final int ENABLE_AUTO_DATA_SWITCH = 6;
78 
79     private MetricsFeatureProvider mMetricsFeatureProvider;
80     @Override
onCreate(Bundle savedInstanceState)81     protected void onCreate(Bundle savedInstanceState) {
82         super.onCreate(savedInstanceState);
83         if (isUiRestricted()) {
84             finish();
85             return;
86         }
87         if (!SubscriptionUtil.isSimHardwareVisible(this)) {
88             Log.d(TAG, "Not support on device without SIM.");
89             finish();
90             return;
91         }
92         SimDialogProhibitService.supportDismiss(this);
93 
94         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
95         getWindow().addSystemFlags(
96                 WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
97         showOrUpdateDialog();
98     }
99 
100     @VisibleForTesting
isUiRestricted()101     boolean isUiRestricted() {
102         if (MobileNetworkUtils.isMobileNetworkUserRestricted(getApplicationContext())) {
103             Log.e(TAG, "This setting isn't available due to user restriction.");
104             return true;
105         }
106         return false;
107     }
108 
109     @Override
onNewIntent(Intent intent)110     protected void onNewIntent(Intent intent) {
111         super.onNewIntent(intent);
112         setIntent(intent);
113         showOrUpdateDialog();
114     }
115 
getProgressState()116     private int getProgressState() {
117         final SharedPreferences prefs = getSharedPreferences(
118                 SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS, MODE_PRIVATE);
119         return prefs.getInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE,
120                 SubscriptionActionDialogActivity.PROGRESS_IS_NOT_SHOWING);
121     }
122 
showOrUpdateDialog()123     private void showOrUpdateDialog() {
124         final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
125 
126         if (dialogType == PICK_DISMISS) {
127             finishAndRemoveTask();
128             return;
129         }
130 
131         if (dialogType == PREFERRED_PICK
132                 && getProgressState() == SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING) {
133             Log.d(TAG, "Finish the sim dialog since the sim action dialog is showing the progress");
134             finish();
135             return;
136         }
137 
138         if (Flags.isDualSimOnboardingEnabled()
139                 && (dialogType == DATA_PICK
140                 || dialogType == CALLS_PICK
141                 || dialogType == SMS_PICK)) {
142             Log.d(TAG, "Finish the sim dialog since the sim onboarding is shown");
143             finish();
144             return;
145         }
146 
147         final String tag = Integer.toString(dialogType);
148         final FragmentManager fragmentManager = getSupportFragmentManager();
149         SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
150 
151         if (fragment == null) {
152             fragment = createFragment(dialogType);
153             fragment.show(fragmentManager, tag);
154         } else {
155             fragment.updateDialog();
156         }
157     }
158 
createFragment(int dialogType)159     private SimDialogFragment createFragment(int dialogType) {
160         switch (dialogType) {
161             case DATA_PICK:
162                 return getDataPickDialogFragment();
163             case CALLS_PICK:
164                 return CallsSimListDialogFragment.newInstance(dialogType,
165                         R.string.select_sim_for_calls,
166                         true /* includeAskEveryTime */,
167                         false /* isCancelItemShowed */);
168             case SMS_PICK:
169                 return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
170                         true /* includeAskEveryTime */,
171                         false /* isCancelItemShowed */);
172             case PREFERRED_PICK:
173                 if (!getIntent().hasExtra(PREFERRED_SIM)) {
174                     throw new IllegalArgumentException("Missing required extra " + PREFERRED_SIM);
175                 }
176                 return PreferredSimDialogFragment.newInstance();
177             case SMS_PICK_FOR_MESSAGE:
178                 return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
179                         false /* includeAskEveryTime */,
180                         false /* isCancelItemShowed */);
181             case ENABLE_AUTO_DATA_SWITCH:
182                 return EnableAutoDataSwitchDialogFragment.newInstance();
183             default:
184                 throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
185         }
186     }
187 
getDataPickDialogFragment()188     private SimDialogFragment getDataPickDialogFragment() {
189         if (SubscriptionManager.getDefaultDataSubscriptionId()
190                 == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
191             return SimListDialogFragment.newInstance(DATA_PICK, R.string.select_sim_for_data,
192                     false /* includeAskEveryTime */,
193                     true /* isCancelItemShowed */);
194         }
195         return SelectSpecificDataSimDialogFragment.newInstance();
196     }
197 
onSubscriptionSelected(int dialogType, int subId)198     public void onSubscriptionSelected(int dialogType, int subId) {
199         if (getSupportFragmentManager().findFragmentByTag(Integer.toString(dialogType)) == null) {
200             Log.w(TAG, "onSubscriptionSelected ignored because stored fragment was null");
201             return;
202         }
203         switch (dialogType) {
204             case DATA_PICK:
205                 setDefaultDataSubId(subId);
206                 break;
207             case CALLS_PICK:
208                 setDefaultCallsSubId(subId);
209                 break;
210             case SMS_PICK:
211                 setDefaultSmsSubId(subId);
212                 break;
213             case PREFERRED_PICK:
214                 setPreferredSim(subId);
215                 break;
216             case SMS_PICK_FOR_MESSAGE:
217                 // Don't set a default here.
218                 // The caller has created this dialog waiting for a result.
219                 Intent intent = new Intent();
220                 intent.putExtra(RESULT_SUB_ID, subId);
221                 setResult(Activity.RESULT_OK, intent);
222                 break;
223             case ENABLE_AUTO_DATA_SWITCH:
224                 onEnableAutoDataSwitch(subId);
225                 break;
226             default:
227                 throw new IllegalArgumentException(
228                         "Invalid dialog type " + dialogType + " sent.");
229         }
230     }
231 
getCarrierConfigForSubId(int subId)232     private PersistableBundle getCarrierConfigForSubId(int subId) {
233         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
234             return null;
235         }
236         return CarrierConfigCache.getInstance(this).getConfigForSubId(subId);
237     }
238 
isCrossSimCallingAllowedByPlatform(int subId)239     private boolean isCrossSimCallingAllowedByPlatform(int subId) {
240         if ((new WifiCallingQueryImsState(this, subId)).isWifiCallingSupported()) {
241             PersistableBundle bundle = getCarrierConfigForSubId(subId);
242             return (bundle != null) && bundle.getBoolean(
243                     CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
244                     false /*default*/);
245         }
246         return false;
247     }
248 
getImsMmTelManager(int subId)249     private ImsMmTelManager getImsMmTelManager(int subId) {
250         ImsManager imsMgr = getSystemService(ImsManager.class);
251         return (imsMgr == null) ? null : imsMgr.getImsMmTelManager(subId);
252     }
253 
trySetCrossSimCallingPerSub(int subId, boolean enabled)254     private void trySetCrossSimCallingPerSub(int subId, boolean enabled) {
255         try {
256             getImsMmTelManager(subId).setCrossSimCallingEnabled(enabled);
257         } catch (ImsException | IllegalArgumentException | NullPointerException exception) {
258             Log.w(TAG, "failed to change cross SIM calling configuration to " + enabled
259                     + " for subID " + subId + "with exception: ", exception);
260         }
261     }
262 
autoDataSwitchEnabledOnNonDataSub(@onNull int[] subIds, int defaultDataSub)263     private boolean autoDataSwitchEnabledOnNonDataSub(@NonNull int[] subIds, int defaultDataSub) {
264         for (int subId : subIds) {
265             if (subId != defaultDataSub) {
266                 final TelephonyManager telephonyManager = getSystemService(
267                         TelephonyManager.class).createForSubscriptionId(subId);
268                 if (telephonyManager.isMobileDataPolicyEnabled(
269                         TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) {
270                     return true;
271                 }
272             }
273         }
274         return false;
275     }
276 
trySetCrossSimCalling(int[] subIds, boolean enabled)277     private void trySetCrossSimCalling(int[] subIds, boolean enabled) {
278         mMetricsFeatureProvider.action(this,
279                 SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_2ND_SIM_ENABLE, enabled);
280         for (int subId : subIds) {
281             if (isCrossSimCallingAllowedByPlatform(subId)) {
282                 trySetCrossSimCallingPerSub(subId, enabled);
283             }
284         }
285     }
286 
287     /**
288      * Show dialog prompting the user to enable auto data switch
289      */
showEnableAutoDataSwitchDialog()290     public void showEnableAutoDataSwitchDialog() {
291         final FragmentManager fragmentManager = getSupportFragmentManager();
292         SimDialogFragment fragment = createFragment(ENABLE_AUTO_DATA_SWITCH);
293 
294         if (fragmentManager.isStateSaved()) {
295             Log.w(TAG, "Failed to show EnableAutoDataSwitchDialog. The fragmentManager "
296                     + "is StateSaved.");
297             forceClose();
298             return;
299         }
300         try {
301             fragment.show(fragmentManager, Integer.toString(ENABLE_AUTO_DATA_SWITCH));
302         } catch (Exception e) {
303             Log.e(TAG, "Failed to show EnableAutoDataSwitchDialog.", e);
304             forceClose();
305             return;
306         }
307         if (getResources().getBoolean(
308                 R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
309             // If auto data switch is already enabled on the non-DDS, the dialog for enabling it
310             // is suppressed (no onEnableAutoDataSwitch()). so we ensure cross-SIM calling is
311             // enabled.
312 
313             // OTOH, if auto data switch is disabled on the new non-DDS, the user may still not
314             // enable it in the dialog. So we ensure cross-SIM calling is disabled before the
315             // dialog. If the user does enable auto data switch, we will re-enable cross-SIM calling
316             // through onEnableAutoDataSwitch()- a minor redundancy to ensure correctness.
317             final SubscriptionManager subscriptionManager =
318                     getSystemService(SubscriptionManager.class);
319             int[] subIds = subscriptionManager.getActiveSubscriptionIdList();
320             int defaultDataSub = subscriptionManager.getDefaultDataSubscriptionId();
321             if (subIds.length > 1) {
322                 trySetCrossSimCalling(subIds,
323                         autoDataSwitchEnabledOnNonDataSub(subIds, defaultDataSub));
324             }
325         }
326     }
327 
328     /**
329      * @param subId The sub Id to enable auto data switch
330      */
onEnableAutoDataSwitch(int subId)331     public void onEnableAutoDataSwitch(int subId) {
332         Log.d(TAG, "onEnableAutoDataSwitch subId:" + subId);
333         final TelephonyManager telephonyManager = getSystemService(
334                 TelephonyManager.class).createForSubscriptionId(subId);
335         telephonyManager.setMobileDataPolicyEnabled(
336                 TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
337 
338         if (getResources().getBoolean(
339                 R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
340             final SubscriptionManager subscriptionManager =
341                     getSystemService(SubscriptionManager.class);
342             trySetCrossSimCalling(subscriptionManager.getActiveSubscriptionIdList(),
343                     true /* enabled */);
344         }
345     }
346 
onFragmentDismissed(SimDialogFragment simDialogFragment)347     public void onFragmentDismissed(SimDialogFragment simDialogFragment) {
348         final List<Fragment> fragments = getSupportFragmentManager().getFragments();
349         if (fragments.size() == 1 && fragments.get(0) == simDialogFragment
350                 || simDialogFragment.getDialogType() == ENABLE_AUTO_DATA_SWITCH) {
351             Log.d(TAG, "onFragmentDismissed dialogType:" + simDialogFragment.getDialogType());
352             finishAndRemoveTask();
353         }
354     }
355 
setDefaultDataSubId(final int subId)356     private void setDefaultDataSubId(final int subId) {
357         final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
358         final TelephonyManager telephonyManager = getSystemService(
359                 TelephonyManager.class).createForSubscriptionId(subId);
360         subscriptionManager.setDefaultDataSubId(subId);
361         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
362             Log.d(TAG, "setDataEnabledForReason true");
363             telephonyManager.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER,
364                     true);
365             Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
366         }
367     }
368 
setDefaultCallsSubId(final int subId)369     private void setDefaultCallsSubId(final int subId) {
370         final PhoneAccountHandle phoneAccount = subscriptionIdToPhoneAccountHandle(subId);
371         final TelecomManager telecomManager = getSystemService(TelecomManager.class);
372         telecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccount);
373     }
374 
setDefaultSmsSubId(final int subId)375     private void setDefaultSmsSubId(final int subId) {
376         final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
377         subscriptionManager.setDefaultSmsSubId(subId);
378     }
379 
setPreferredSim(final int subId)380     private void setPreferredSim(final int subId) {
381         setDefaultDataSubId(subId);
382         setDefaultSmsSubId(subId);
383         setDefaultCallsSubId(subId);
384     }
385 
subscriptionIdToPhoneAccountHandle(final int subId)386     private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
387         final TelecomManager telecomManager = getSystemService(TelecomManager.class);
388         final TelephonyManager telephonyManager = getSystemService(TelephonyManager.class);
389 
390         for (PhoneAccountHandle handle : telecomManager.getCallCapablePhoneAccounts()) {
391             if (subId == telephonyManager.getSubscriptionId(handle)) {
392                 return handle;
393             }
394         }
395         return null;
396     }
397 
398     /*
399      * Force dismiss this Activity.
400      */
forceClose()401     protected void forceClose() {
402         if (isFinishing() || isDestroyed()) {
403             return;
404         }
405         Log.d(TAG, "Dismissed by Service");
406         finishAndRemoveTask();
407     }
408 }
409