• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         Log.d(TAG, "dialogType:" + dialogType);
126 
127         if (dialogType == PICK_DISMISS) {
128             finishAndRemoveTask();
129             return;
130         }
131 
132         if (dialogType == PREFERRED_PICK
133                 && getProgressState() == SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING) {
134             Log.d(TAG, "Finish the sim dialog since the sim action dialog is showing the progress");
135             finish();
136             return;
137         }
138 
139         if (Flags.isDualSimOnboardingEnabled()
140                 && (dialogType == DATA_PICK
141                 || dialogType == CALLS_PICK
142                 || dialogType == SMS_PICK)) {
143             Log.d(TAG, "Finish the sim dialog since the sim onboarding is shown");
144             finish();
145             return;
146         }
147 
148         final String tag = Integer.toString(dialogType);
149         final FragmentManager fragmentManager = getSupportFragmentManager();
150         SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
151 
152         if (fragment == null) {
153             fragment = createFragment(dialogType);
154             fragment.show(fragmentManager, tag);
155         } else {
156             fragment.updateDialog();
157         }
158     }
159 
createFragment(int dialogType)160     private SimDialogFragment createFragment(int dialogType) {
161         switch (dialogType) {
162             case DATA_PICK:
163                 return getDataPickDialogFragment();
164             case CALLS_PICK:
165                 return CallsSimListDialogFragment.newInstance(dialogType,
166                         R.string.select_sim_for_calls,
167                         true /* includeAskEveryTime */,
168                         false /* isCancelItemShowed */);
169             case SMS_PICK:
170                 return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
171                         true /* includeAskEveryTime */,
172                         false /* isCancelItemShowed */);
173             case PREFERRED_PICK:
174                 if (!getIntent().hasExtra(PREFERRED_SIM)) {
175                     throw new IllegalArgumentException("Missing required extra " + PREFERRED_SIM);
176                 }
177                 return PreferredSimDialogFragment.newInstance();
178             case SMS_PICK_FOR_MESSAGE:
179                 return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
180                         false /* includeAskEveryTime */,
181                         false /* isCancelItemShowed */);
182             case ENABLE_AUTO_DATA_SWITCH:
183                 return EnableAutoDataSwitchDialogFragment.newInstance();
184             default:
185                 throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
186         }
187     }
188 
getDataPickDialogFragment()189     private SimDialogFragment getDataPickDialogFragment() {
190         if (SubscriptionManager.getDefaultDataSubscriptionId()
191                 == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
192             return SimListDialogFragment.newInstance(DATA_PICK, R.string.select_sim_for_data,
193                     false /* includeAskEveryTime */,
194                     true /* isCancelItemShowed */);
195         }
196         return SelectSpecificDataSimDialogFragment.newInstance();
197     }
198 
onSubscriptionSelected(int dialogType, int subId)199     public void onSubscriptionSelected(int dialogType, int subId) {
200         if (getSupportFragmentManager().findFragmentByTag(Integer.toString(dialogType)) == null) {
201             Log.w(TAG, "onSubscriptionSelected ignored because stored fragment was null");
202             return;
203         }
204         switch (dialogType) {
205             case DATA_PICK:
206                 setDefaultDataSubId(subId);
207                 break;
208             case CALLS_PICK:
209                 setDefaultCallsSubId(subId);
210                 break;
211             case SMS_PICK:
212                 setDefaultSmsSubId(subId);
213                 break;
214             case PREFERRED_PICK:
215                 setPreferredSim(subId);
216                 break;
217             case SMS_PICK_FOR_MESSAGE:
218                 // Don't set a default here.
219                 // The caller has created this dialog waiting for a result.
220                 Intent intent = new Intent();
221                 intent.putExtra(RESULT_SUB_ID, subId);
222                 setResult(Activity.RESULT_OK, intent);
223                 break;
224             case ENABLE_AUTO_DATA_SWITCH:
225                 onEnableAutoDataSwitch(subId);
226                 break;
227             default:
228                 throw new IllegalArgumentException(
229                         "Invalid dialog type " + dialogType + " sent.");
230         }
231     }
232 
getCarrierConfigForSubId(int subId)233     private PersistableBundle getCarrierConfigForSubId(int subId) {
234         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
235             return null;
236         }
237         return CarrierConfigCache.getInstance(this).getConfigForSubId(subId);
238     }
239 
isCrossSimCallingAllowedByPlatform(int subId)240     private boolean isCrossSimCallingAllowedByPlatform(int subId) {
241         if ((new WifiCallingQueryImsState(this, subId)).isWifiCallingSupported()) {
242             PersistableBundle bundle = getCarrierConfigForSubId(subId);
243             return (bundle != null) && bundle.getBoolean(
244                     CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
245                     false /*default*/);
246         }
247         return false;
248     }
249 
getImsMmTelManager(int subId)250     private ImsMmTelManager getImsMmTelManager(int subId) {
251         ImsManager imsMgr = getSystemService(ImsManager.class);
252         return (imsMgr == null) ? null : imsMgr.getImsMmTelManager(subId);
253     }
254 
trySetCrossSimCallingPerSub(int subId, boolean enabled)255     private void trySetCrossSimCallingPerSub(int subId, boolean enabled) {
256         try {
257             getImsMmTelManager(subId).setCrossSimCallingEnabled(enabled);
258         } catch (ImsException | IllegalArgumentException | NullPointerException exception) {
259             Log.w(TAG, "failed to change cross SIM calling configuration to " + enabled
260                     + " for subID " + subId + "with exception: ", exception);
261         }
262     }
263 
autoDataSwitchEnabledOnNonDataSub(@onNull int[] subIds, int defaultDataSub)264     private boolean autoDataSwitchEnabledOnNonDataSub(@NonNull int[] subIds, int defaultDataSub) {
265         for (int subId : subIds) {
266             if (subId != defaultDataSub) {
267                 final TelephonyManager telephonyManager = getSystemService(
268                         TelephonyManager.class).createForSubscriptionId(subId);
269                 if (telephonyManager.isMobileDataPolicyEnabled(
270                         TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) {
271                     return true;
272                 }
273             }
274         }
275         return false;
276     }
277 
trySetCrossSimCalling(int[] subIds, boolean enabled)278     private void trySetCrossSimCalling(int[] subIds, boolean enabled) {
279         mMetricsFeatureProvider.action(this,
280                 SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_2ND_SIM_ENABLE, enabled);
281         for (int subId : subIds) {
282             if (isCrossSimCallingAllowedByPlatform(subId)) {
283                 trySetCrossSimCallingPerSub(subId, enabled);
284             }
285         }
286     }
287 
288     /**
289      * Show dialog prompting the user to enable auto data switch
290      */
showEnableAutoDataSwitchDialog()291     public void showEnableAutoDataSwitchDialog() {
292         final FragmentManager fragmentManager = getSupportFragmentManager();
293         SimDialogFragment fragment = createFragment(ENABLE_AUTO_DATA_SWITCH);
294 
295         if (fragmentManager.isStateSaved()) {
296             Log.w(TAG, "Failed to show EnableAutoDataSwitchDialog. The fragmentManager "
297                     + "is StateSaved.");
298             forceClose();
299             return;
300         }
301         try {
302             fragment.show(fragmentManager, Integer.toString(ENABLE_AUTO_DATA_SWITCH));
303         } catch (Exception e) {
304             Log.e(TAG, "Failed to show EnableAutoDataSwitchDialog.", e);
305             forceClose();
306             return;
307         }
308         if (getResources().getBoolean(
309                 R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
310             // If auto data switch is already enabled on the non-DDS, the dialog for enabling it
311             // is suppressed (no onEnableAutoDataSwitch()). so we ensure cross-SIM calling is
312             // enabled.
313 
314             // OTOH, if auto data switch is disabled on the new non-DDS, the user may still not
315             // enable it in the dialog. So we ensure cross-SIM calling is disabled before the
316             // dialog. If the user does enable auto data switch, we will re-enable cross-SIM calling
317             // through onEnableAutoDataSwitch()- a minor redundancy to ensure correctness.
318             final SubscriptionManager subscriptionManager =
319                     getSystemService(SubscriptionManager.class);
320             int[] subIds = subscriptionManager.getActiveSubscriptionIdList();
321             int defaultDataSub = subscriptionManager.getDefaultDataSubscriptionId();
322             if (subIds.length > 1) {
323                 trySetCrossSimCalling(subIds,
324                         autoDataSwitchEnabledOnNonDataSub(subIds, defaultDataSub));
325             }
326         }
327     }
328 
329     /**
330      * @param subId The sub Id to enable auto data switch
331      */
onEnableAutoDataSwitch(int subId)332     public void onEnableAutoDataSwitch(int subId) {
333         Log.d(TAG, "onEnableAutoDataSwitch subId:" + subId);
334         final TelephonyManager telephonyManager = getSystemService(
335                 TelephonyManager.class).createForSubscriptionId(subId);
336         telephonyManager.setMobileDataPolicyEnabled(
337                 TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
338 
339         if (getResources().getBoolean(
340                 R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
341             final SubscriptionManager subscriptionManager =
342                     getSystemService(SubscriptionManager.class);
343             trySetCrossSimCalling(subscriptionManager.getActiveSubscriptionIdList(),
344                     true /* enabled */);
345         }
346     }
347 
onFragmentDismissed(SimDialogFragment simDialogFragment)348     public void onFragmentDismissed(SimDialogFragment simDialogFragment) {
349         final List<Fragment> fragments = getSupportFragmentManager().getFragments();
350         if (fragments.size() == 1 && fragments.get(0) == simDialogFragment
351                 || simDialogFragment.getDialogType() == ENABLE_AUTO_DATA_SWITCH) {
352             Log.d(TAG, "onFragmentDismissed dialogType:" + simDialogFragment.getDialogType());
353             finishAndRemoveTask();
354         }
355     }
356 
setDefaultDataSubId(final int subId)357     private void setDefaultDataSubId(final int subId) {
358         final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
359         final TelephonyManager telephonyManager = getSystemService(
360                 TelephonyManager.class).createForSubscriptionId(subId);
361         subscriptionManager.setDefaultDataSubId(subId);
362         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
363             Log.d(TAG, "setDataEnabledForReason true");
364             telephonyManager.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER,
365                     true);
366             Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
367         }
368     }
369 
setDefaultCallsSubId(final int subId)370     private void setDefaultCallsSubId(final int subId) {
371         final PhoneAccountHandle phoneAccount = subscriptionIdToPhoneAccountHandle(subId);
372         final TelecomManager telecomManager = getSystemService(TelecomManager.class);
373         telecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccount);
374     }
375 
setDefaultSmsSubId(final int subId)376     private void setDefaultSmsSubId(final int subId) {
377         final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
378         subscriptionManager.setDefaultSmsSubId(subId);
379     }
380 
setPreferredSim(final int subId)381     private void setPreferredSim(final int subId) {
382         setDefaultDataSubId(subId);
383     }
384 
subscriptionIdToPhoneAccountHandle(final int subId)385     private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
386         final TelecomManager telecomManager = getSystemService(TelecomManager.class);
387         final TelephonyManager telephonyManager = getSystemService(TelephonyManager.class);
388 
389         for (PhoneAccountHandle handle : telecomManager.getCallCapablePhoneAccounts()) {
390             if (subId == telephonyManager.getSubscriptionId(handle)) {
391                 return handle;
392             }
393         }
394         return null;
395     }
396 
397     /*
398      * Force dismiss this Activity.
399      */
forceClose()400     protected void forceClose() {
401         if (isFinishing() || isDestroyed()) {
402             return;
403         }
404         Log.d(TAG, "Dismissed by Service");
405         finishAndRemoveTask();
406     }
407 }
408