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