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