1 /* 2 * Copyright (C) 2015 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; 18 19 import android.app.Activity; 20 import android.app.settings.SettingsEnums; 21 import android.content.BroadcastReceiver; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.res.Resources; 27 import android.os.Bundle; 28 import android.provider.Settings; 29 import android.provider.Settings.Global; 30 import android.telephony.SubscriptionInfo; 31 import android.telephony.SubscriptionManager; 32 import android.telephony.euicc.EuiccManager; 33 import android.text.TextUtils; 34 import android.util.Log; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.View.OnClickListener; 38 import android.view.ViewGroup; 39 import android.widget.ArrayAdapter; 40 import android.widget.Button; 41 import android.widget.CheckBox; 42 import android.widget.Spinner; 43 44 import androidx.activity.result.ActivityResult; 45 import androidx.activity.result.ActivityResultLauncher; 46 import androidx.activity.result.contract.ActivityResultContracts; 47 import androidx.annotation.Nullable; 48 import androidx.annotation.VisibleForTesting; 49 50 import com.android.settings.core.InstrumentedFragment; 51 import com.android.settings.core.SubSettingLauncher; 52 import com.android.settings.network.ResetNetworkRestrictionViewBuilder; 53 import com.android.settings.network.SubscriptionUtil; 54 import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity; 55 import com.android.settings.password.ChooseLockSettingsHelper; 56 import com.android.settings.password.ConfirmLockPattern; 57 import com.android.settings.system.reset.ResetNetworkConfirm; 58 import com.android.settingslib.development.DevelopmentSettingsEnabler; 59 60 import java.util.ArrayList; 61 import java.util.Collections; 62 import java.util.List; 63 import java.util.Optional; 64 65 /** 66 * Confirm and execute a reset of the device's network settings to a clean "just out of the box" 67 * state. Multiple confirmations are required: first, a general "are you sure you want to do this?" 68 * prompt, followed by a keyguard pattern trace if the user has defined one, followed by a final 69 * strongly-worded "THIS WILL RESET EVERYTHING" prompt. If at any time the phone is allowed to go 70 * to sleep, is locked, et cetera, then the confirmation sequence is abandoned. 71 * <p> 72 * This is the initial screen. 73 */ 74 public class ResetNetwork extends InstrumentedFragment { 75 private static final String TAG = "ResetNetwork"; 76 77 // Arbitrary to avoid conficts 78 private static final int KEYGUARD_REQUEST = 55; 79 80 private ActivityResultLauncher mActivityResultLauncher; 81 private List<SubscriptionInfo> mSubscriptions; 82 83 private View mContentView; 84 private Spinner mSubscriptionSpinner; 85 private Button mInitiateButton; 86 @VisibleForTesting 87 View mEsimContainer; 88 @VisibleForTesting 89 CheckBox mEsimCheckbox; 90 91 private BroadcastReceiver mDefaultSubChangeReceiver = new BroadcastReceiver() { 92 @Override 93 public void onReceive(Context context, Intent intent) { 94 if (intent.getAction() != SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED) { 95 return; 96 } 97 establishInitialState(getActiveSubscriptionInfoList()); 98 } 99 }; 100 101 @Override onCreate(@ullable Bundle savedInstanceState)102 public void onCreate(@Nullable Bundle savedInstanceState) { 103 super.onCreate(savedInstanceState); 104 getActivity().setTitle(R.string.reset_mobile_network_settings_title); 105 106 mActivityResultLauncher = registerForActivityResult( 107 new ActivityResultContracts.StartActivityForResult(), 108 result -> onActivityLauncherResult(result)); 109 } 110 111 /** 112 * Keyguard validation is run using the standard {@link ConfirmLockPattern} 113 * component as a subactivity 114 * 115 * @param request the request code to be returned once confirmation finishes 116 * @return true if confirmation launched 117 */ runKeyguardConfirmation(int request)118 private boolean runKeyguardConfirmation(int request) { 119 Resources res = getActivity().getResources(); 120 final ChooseLockSettingsHelper.Builder builder = 121 new ChooseLockSettingsHelper.Builder(getActivity(), this); 122 return builder.setRequestCode(request) 123 .setTitle(res.getText(R.string.reset_mobile_network_settings_title)) 124 .setActivityResultLauncher(mActivityResultLauncher) 125 .show(); 126 } 127 onActivityLauncherResult(ActivityResult result)128 public void onActivityLauncherResult(ActivityResult result) { 129 // If the user entered a valid keyguard trace, present the final 130 // confirmation prompt; otherwise, go back to the initial state. 131 if (result.getResultCode() == Activity.RESULT_OK) { 132 showFinalConfirmation(); 133 } else if (mContentView != null) { 134 establishInitialState(getActiveSubscriptionInfoList()); 135 } 136 } 137 138 @VisibleForTesting showFinalConfirmation()139 void showFinalConfirmation() { 140 Bundle args = new Bundle(); 141 Context context = getContext(); 142 boolean resetSims = false; 143 144 int resetOptions = ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER 145 | ResetNetworkRequest.RESET_VPN_MANAGER 146 | ResetNetworkRequest.RESET_IMS_STACK 147 | ResetNetworkRequest.RESET_PHONE_PROCESS; 148 ResetNetworkRequest request = new ResetNetworkRequest(resetOptions); 149 if (mSubscriptions != null && mSubscriptions.size() > 0) { 150 int selectedIndex = mSubscriptionSpinner.getSelectedItemPosition(); 151 SubscriptionInfo subscription = mSubscriptions.get(selectedIndex); 152 int subId = subscription.getSubscriptionId(); 153 request.setResetTelephonyAndNetworkPolicyManager(subId) 154 .setResetApn(subId); 155 request.setResetImsSubId(subId); 156 } 157 if (mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked()) { 158 resetSims = true; 159 request.setResetEsim(context.getPackageName()).writeIntoBundle(args); 160 } else { 161 request.writeIntoBundle(args); 162 } 163 164 SubSettingLauncher launcher = 165 new SubSettingLauncher(context) 166 .setDestination(ResetNetworkConfirm.class.getName()) 167 .setArguments(args) 168 .setTitleRes(R.string.reset_mobile_network_settings_confirm_title) 169 .setSourceMetricsCategory(getMetricsCategory()); 170 171 if (resetSims && SubscriptionUtil.shouldShowRacDialogWhenErasingAllEsims(context)) { 172 context.startActivity( 173 EuiccRacConnectivityDialogActivity.getIntent(context, launcher.toIntent())); 174 } else { 175 launcher.launch(); 176 } 177 } 178 179 /** 180 * If the user clicks to begin the reset sequence, we next require a 181 * keyguard confirmation if the user has currently enabled one. If there 182 * is no keyguard available, we simply go to the final confirmation prompt. 183 */ 184 private final Button.OnClickListener mInitiateListener = new Button.OnClickListener() { 185 186 @Override 187 public void onClick(View v) { 188 if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) { 189 showFinalConfirmation(); 190 } 191 } 192 }; 193 194 /** 195 * In its initial state, the activity presents a button for the user to 196 * click in order to initiate a confirmation sequence. This method is 197 * called from various other points in the code to reset the activity to 198 * this base state. 199 * 200 * <p>Reinflating views from resources is expensive and prevents us from 201 * caching widget pointers, so we use a single-inflate pattern: we lazy- 202 * inflate each view, caching all of the widget pointers we'll need at the 203 * time, then simply reuse the inflated views directly whenever we need 204 * to change contents. 205 * 206 * @param subscriptionsList is a list of SubscriptionInfo(s) which allow user to select from 207 */ establishInitialState(List<SubscriptionInfo> subscriptionsList)208 private void establishInitialState(List<SubscriptionInfo> subscriptionsList) { 209 mSubscriptionSpinner = (Spinner) mContentView.findViewById(R.id.reset_network_subscription); 210 mEsimContainer = mContentView.findViewById(R.id.erase_esim_container); 211 mEsimCheckbox = mContentView.findViewById(R.id.erase_esim); 212 213 mSubscriptions = subscriptionsList; 214 if (mSubscriptions != null && mSubscriptions.size() > 0) { 215 // Get the default subscription in the order of data, voice, sms, first up. 216 int defaultSubscription = SubscriptionManager.getDefaultDataSubscriptionId(); 217 if (!SubscriptionManager.isUsableSubscriptionId(defaultSubscription)) { 218 defaultSubscription = SubscriptionManager.getDefaultVoiceSubscriptionId(); 219 } 220 if (!SubscriptionManager.isUsableSubscriptionId(defaultSubscription)) { 221 defaultSubscription = SubscriptionManager.getDefaultSmsSubscriptionId(); 222 } 223 if (!SubscriptionManager.isUsableSubscriptionId(defaultSubscription)) { 224 defaultSubscription = SubscriptionManager.getDefaultSubscriptionId(); 225 } 226 227 int selectedIndex = 0; 228 List<String> subscriptionNames = new ArrayList<>(); 229 for (SubscriptionInfo record : mSubscriptions) { 230 if (record.getSubscriptionId() == defaultSubscription) { 231 // Set the first selected value to the default 232 selectedIndex = subscriptionNames.size(); 233 } 234 String name = SubscriptionUtil.getUniqueSubscriptionDisplayName( 235 record, getContext()).toString(); 236 if (TextUtils.isEmpty(name)) { 237 name = record.getNumber(); 238 } 239 if (TextUtils.isEmpty(name)) { 240 CharSequence carrierName = record.getCarrierName(); 241 name = TextUtils.isEmpty(carrierName) ? "" : carrierName.toString(); 242 } 243 if (TextUtils.isEmpty(name)) { 244 name = String.format("MCC:%s MNC:%s Slot:%s Id:%s", record.getMcc(), 245 record.getMnc(), record.getSimSlotIndex(), record.getSubscriptionId()); 246 } 247 subscriptionNames.add(name); 248 } 249 ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), 250 android.R.layout.simple_spinner_item, subscriptionNames); 251 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 252 mSubscriptionSpinner.setAdapter(adapter); 253 mSubscriptionSpinner.setSelection(selectedIndex); 254 if (mSubscriptions.size() > 1) { 255 mSubscriptionSpinner.setVisibility(View.VISIBLE); 256 } else { 257 mSubscriptionSpinner.setVisibility(View.INVISIBLE); 258 } 259 } else { 260 mSubscriptionSpinner.setVisibility(View.INVISIBLE); 261 } 262 mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_reset_network); 263 mInitiateButton.setOnClickListener(mInitiateListener); 264 if (showEuiccSettings(getContext())) { 265 mEsimContainer.setVisibility(View.VISIBLE); 266 mEsimContainer.setOnClickListener(new OnClickListener() { 267 @Override 268 public void onClick(View v) { 269 mEsimCheckbox.toggle(); 270 } 271 }); 272 } else { 273 mEsimCheckbox.setChecked(false /* checked */); 274 } 275 } 276 getActiveSubscriptionInfoList()277 private List<SubscriptionInfo> getActiveSubscriptionInfoList() { 278 if (!SubscriptionUtil.isSimHardwareVisible(getActivity())) { 279 return Collections.emptyList(); 280 } 281 SubscriptionManager mgr = getActivity().getSystemService(SubscriptionManager.class); 282 if (mgr == null) { 283 Log.w(TAG, "No SubscriptionManager"); 284 return Collections.emptyList(); 285 } 286 return Optional.ofNullable(mgr.getActiveSubscriptionInfoList()) 287 .orElse(Collections.emptyList()); 288 } 289 290 @Override onResume()291 public void onResume() { 292 super.onResume(); 293 getContext().registerReceiver(mDefaultSubChangeReceiver, 294 new IntentFilter(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)); 295 296 if (mContentView == null) { 297 return; 298 } 299 300 // update options if subcription has been changed 301 List<SubscriptionInfo> updatedSubscriptions = getActiveSubscriptionInfoList(); 302 if ((mSubscriptions != null) 303 && (mSubscriptions.size() == updatedSubscriptions.size()) 304 && mSubscriptions.containsAll(updatedSubscriptions)) { 305 return; 306 } 307 Log.d(TAG, "subcription list changed"); 308 establishInitialState(updatedSubscriptions); 309 } 310 311 @Override onPause()312 public void onPause() { 313 super.onPause(); 314 getContext().unregisterReceiver(mDefaultSubChangeReceiver); 315 } 316 showEuiccSettings(Context context)317 private boolean showEuiccSettings(Context context) { 318 if (!SubscriptionUtil.isSimHardwareVisible(context)) { 319 return false; 320 } 321 EuiccManager euiccManager = 322 (EuiccManager) context.getSystemService(Context.EUICC_SERVICE); 323 if (euiccManager == null || !euiccManager.isEnabled()) { 324 return false; 325 } 326 ContentResolver resolver = context.getContentResolver(); 327 return Settings.Global.getInt(resolver, Global.EUICC_PROVISIONED, 0) != 0 328 || DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context); 329 } 330 331 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)332 public View onCreateView(LayoutInflater inflater, ViewGroup container, 333 Bundle savedInstanceState) { 334 View view = (new ResetNetworkRestrictionViewBuilder(getActivity())).build(); 335 if (view != null) { 336 Log.w(TAG, "Access deny."); 337 return view; 338 } 339 340 mContentView = inflater.inflate(R.layout.reset_mobile_network_settings, null); 341 342 establishInitialState(getActiveSubscriptionInfoList()); 343 return mContentView; 344 } 345 346 @Override getMetricsCategory()347 public int getMetricsCategory() { 348 return SettingsEnums.RESET_NETWORK; 349 } 350 } 351