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