1be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott/*
2be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * Copyright (C) 2015 The Android Open Source Project
3be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott *
4be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * Licensed under the Apache License, Version 2.0 (the "License");
5be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * you may not use this file except in compliance with the License.
6be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * You may obtain a copy of the License at
7be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott *
8be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott *      http://www.apache.org/licenses/LICENSE-2.0
9be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott *
10be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * Unless required by applicable law or agreed to in writing, software
11be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * distributed under the License is distributed on an "AS IS" BASIS,
12be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * See the License for the specific language governing permissions and
14be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * limitations under the License.
15be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott */
16be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
17be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottpackage com.android.settings;
18be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
19be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.app.Activity;
20be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.content.Intent;
21be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.content.res.Resources;
22be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.os.Bundle;
237dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shankaimport android.os.UserHandle;
24be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.os.UserManager;
25be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.telephony.SubscriptionInfo;
26be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.telephony.SubscriptionManager;
27be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.text.TextUtils;
28be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.view.LayoutInflater;
29be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.view.View;
30be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.view.ViewGroup;
31be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.widget.ArrayAdapter;
32be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.widget.Button;
33be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport android.widget.Spinner;
34be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
35265d3c2a0c36251bf8a9f571d7239b6dd404d942Tamas Berghammerimport com.android.internal.logging.nano.MetricsProto.MetricsEvent;
36be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport com.android.internal.telephony.PhoneConstants;
372eb170cd6ff43db01dc0ff3c1fcac5ebba4489deMaurice Lamimport com.android.settings.password.ChooseLockSettingsHelper;
382eb170cd6ff43db01dc0ff3c1fcac5ebba4489deMaurice Lamimport com.android.settings.password.ConfirmLockPattern;
397dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shankaimport com.android.settingslib.RestrictedLockUtils;
402eb170cd6ff43db01dc0ff3c1fcac5ebba4489deMaurice Lamimport com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
41be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
42be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport java.util.ArrayList;
43be90341c808ac5b17149eb42eac966906c6f2041Stuart Scottimport java.util.List;
44be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
45be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott/**
46be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * Confirm and execute a reset of the device's network settings to a clean "just out of the box"
47be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * state.  Multiple confirmations are required: first, a general "are you sure you want to do this?"
48be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * prompt, followed by a keyguard pattern trace if the user has defined one, followed by a final
49be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * strongly-worded "THIS WILL RESET EVERYTHING" prompt.  If at any time the phone is allowed to go
50be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * to sleep, is locked, et cetera, then the confirmation sequence is abandoned.
51be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott *
52be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott * This is the initial screen.
53be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott */
540708d9e119da4c4d9424c0bc54fa458d01856bd7Udam Sainipublic class ResetNetwork extends OptionsMenuFragment {
55be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private static final String TAG = "ResetNetwork";
56be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
57be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    // Arbitrary to avoid conficts
58be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private static final int KEYGUARD_REQUEST = 55;
59be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
60be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private List<SubscriptionInfo> mSubscriptions;
61be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
62be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private View mContentView;
63be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private Spinner mSubscriptionSpinner;
64be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private Button mInitiateButton;
65be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
66be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    /**
67be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * Keyguard validation is run using the standard {@link ConfirmLockPattern}
68be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * component as a subactivity
69be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * @param request the request code to be returned once confirmation finishes
70be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * @return true if confirmation launched
71be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     */
72be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private boolean runKeyguardConfirmation(int request) {
73be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        Resources res = getActivity().getResources();
748a09b619aeb233e2aab1919301f162d8acf1f0f0Jorim Jaggi        return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
758a09b619aeb233e2aab1919301f162d8acf1f0f0Jorim Jaggi                request, res.getText(R.string.reset_network_title));
76be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    }
77be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
78be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    @Override
79be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    public void onActivityResult(int requestCode, int resultCode, Intent data) {
80be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        super.onActivityResult(requestCode, resultCode, data);
81be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
82be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        if (requestCode != KEYGUARD_REQUEST) {
83be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            return;
84be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        }
85be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
86be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        // If the user entered a valid keyguard trace, present the final
87be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        // confirmation prompt; otherwise, go back to the initial state.
88be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        if (resultCode == Activity.RESULT_OK) {
89be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            showFinalConfirmation();
90be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        } else {
91be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            establishInitialState();
92be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        }
93be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    }
94be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
95be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private void showFinalConfirmation() {
96be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        Bundle args = new Bundle();
97754b56690be41fe38fcb502da2eb0d8ec4d19200Stuart Scott        if (mSubscriptions != null && mSubscriptions.size() > 0) {
98be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            int selectedIndex = mSubscriptionSpinner.getSelectedItemPosition();
99be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            SubscriptionInfo subscription = mSubscriptions.get(selectedIndex);
100be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            args.putInt(PhoneConstants.SUBSCRIPTION_KEY, subscription.getSubscriptionId());
101be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        }
102c6ca314c0b872f75926807ca7d6eb24ebe7cb684Fan Zhang        ((SettingsActivity) getActivity()).startPreferencePanel(
103c6ca314c0b872f75926807ca7d6eb24ebe7cb684Fan Zhang                this, ResetNetworkConfirm.class.getName(),
104be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                args, R.string.reset_network_confirm_title, null, null, 0);
105be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    }
106be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
107be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    /**
108be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * If the user clicks to begin the reset sequence, we next require a
109be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * keyguard confirmation if the user has currently enabled one.  If there
110be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * is no keyguard available, we simply go to the final confirmation prompt.
111be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     */
112be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private final Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
113be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
114be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        @Override
115be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        public void onClick(View v) {
116be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {
117be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                showFinalConfirmation();
118be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            }
119be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        }
120be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    };
121be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
122be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    /**
123be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * In its initial state, the activity presents a button for the user to
124be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * click in order to initiate a confirmation sequence.  This method is
125be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * called from various other points in the code to reset the activity to
126be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * this base state.
127be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     *
128be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * <p>Reinflating views from resources is expensive and prevents us from
129be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * caching widget pointers, so we use a single-inflate pattern:  we lazy-
130be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * inflate each view, caching all of the widget pointers we'll need at the
131be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * time, then simply reuse the inflated views directly whenever we need
132be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     * to change contents.
133be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott     */
134be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    private void establishInitialState() {
135be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        mSubscriptionSpinner = (Spinner) mContentView.findViewById(R.id.reset_network_subscription);
136be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
137be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        mSubscriptions = SubscriptionManager.from(getActivity()).getActiveSubscriptionInfoList();
1389302830fd0d7e1a87cbb6ef74358b3ad01e229f3Stuart Scott        if (mSubscriptions != null && mSubscriptions.size() > 0) {
139be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            // Get the default subscription in the order of data, voice, sms, first up.
140d2b0fc066c9feedb988db59abd410203777a5f38Shishir Agrawal            int defaultSubscription = SubscriptionManager.getDefaultDataSubscriptionId();
141be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            if (!SubscriptionManager.isUsableSubIdValue(defaultSubscription)) {
142d2b0fc066c9feedb988db59abd410203777a5f38Shishir Agrawal                defaultSubscription = SubscriptionManager.getDefaultVoiceSubscriptionId();
143be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            }
144be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            if (!SubscriptionManager.isUsableSubIdValue(defaultSubscription)) {
145d2b0fc066c9feedb988db59abd410203777a5f38Shishir Agrawal                defaultSubscription = SubscriptionManager.getDefaultSmsSubscriptionId();
146be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            }
147be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            if (!SubscriptionManager.isUsableSubIdValue(defaultSubscription)) {
148d2b0fc066c9feedb988db59abd410203777a5f38Shishir Agrawal                defaultSubscription = SubscriptionManager.getDefaultSubscriptionId();
149be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            }
150be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
151be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            int selectedIndex = 0;
152be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            int size = mSubscriptions.size();
153be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            List<String> subscriptionNames = new ArrayList<>();
154be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            for (SubscriptionInfo record : mSubscriptions) {
155be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                if (record.getSubscriptionId() == defaultSubscription) {
156be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                    // Set the first selected value to the default
157be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                    selectedIndex = subscriptionNames.size();
158be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                }
159be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                String name = record.getDisplayName().toString();
160be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                if (TextUtils.isEmpty(name)) {
161be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                    name = record.getNumber();
162be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                }
163be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                if (TextUtils.isEmpty(name)) {
164be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                    name = record.getCarrierName().toString();
165be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                }
166be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                if (TextUtils.isEmpty(name)) {
167be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                    name = String.format("MCC:%s MNC:%s Slot:%s Id:%s", record.getMcc(),
168be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                            record.getMnc(), record.getSimSlotIndex(), record.getSubscriptionId());
169be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                }
170be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                subscriptionNames.add(name);
171be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            }
172be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
173be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott                    android.R.layout.simple_spinner_item, subscriptionNames);
174be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
175be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            mSubscriptionSpinner.setAdapter(adapter);
176be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            mSubscriptionSpinner.setSelection(selectedIndex);
177dfba92346874650e20669a98a2850d6b96839a76Meng Wang            if (mSubscriptions.size() > 1) {
178dfba92346874650e20669a98a2850d6b96839a76Meng Wang                mSubscriptionSpinner.setVisibility(View.VISIBLE);
179dfba92346874650e20669a98a2850d6b96839a76Meng Wang            } else {
180dfba92346874650e20669a98a2850d6b96839a76Meng Wang                mSubscriptionSpinner.setVisibility(View.INVISIBLE);
181dfba92346874650e20669a98a2850d6b96839a76Meng Wang            }
182be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        } else {
183be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            mSubscriptionSpinner.setVisibility(View.INVISIBLE);
184be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        }
185be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_reset_network);
186be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        mInitiateButton.setOnClickListener(mInitiateListener);
187be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    }
188be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
189be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    @Override
190be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    public View onCreateView(LayoutInflater inflater, ViewGroup container,
191be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott            Bundle savedInstanceState) {
19244879a387a6be6b93cdcc8894227d68af7dbbb1aXiaohui Chen        final UserManager um = UserManager.get(getActivity());
1937dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka        final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
1947dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka                getActivity(), UserManager.DISALLOW_NETWORK_RESET, UserHandle.myUserId());
1957dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka        if (!um.isAdminUser() || RestrictedLockUtils.hasBaseUserRestriction(getActivity(),
1967dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka                UserManager.DISALLOW_NETWORK_RESET, UserHandle.myUserId())) {
197176f512c5ad8d8478db88fad4c14ffca52102adeStuart Scott            return inflater.inflate(R.layout.network_reset_disallowed_screen, null);
1987dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka        } else if (admin != null) {
1997dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            View view = inflater.inflate(R.layout.admin_support_details_empty_view, null);
2007dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), view, admin, false);
2017dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            view.setVisibility(View.VISIBLE);
2027dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            return view;
203176f512c5ad8d8478db88fad4c14ffca52102adeStuart Scott        }
204176f512c5ad8d8478db88fad4c14ffca52102adeStuart Scott
205be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        mContentView = inflater.inflate(R.layout.reset_network, null);
206be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott
207be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        establishInitialState();
208be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott        return mContentView;
209be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott    }
2108a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren
2118a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren    @Override
2126507613ebcd22e4691c2af92a5c161bd327db336Fan Zhang    public int getMetricsCategory() {
2139d1bfd1e8de6e46137a9571507c03526880d6a46Chris Wren        return MetricsEvent.RESET_NETWORK;
2148a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren    }
215be90341c808ac5b17149eb42eac966906c6f2041Stuart Scott}
216