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