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