Merge "Fix NPE if default supervisor is not defined"
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 6b29b2e..45c9e16 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -264,6 +264,18 @@
         return 0;
     }
 
+    /**
+     * Whether preference is allowing to be displayed to the user.
+     *
+     * @param preference to check if it can be displayed to the user (not hidding in expand area).
+     * @return {@code true} when preference is allowing to be displayed to the user.
+     * {@code false} when preference is hidden in expand area and not been displayed to the user.
+     */
+    protected boolean isPreferenceExpanded(Preference preference) {
+        return ((mAdapter == null)
+                || (mAdapter.getPreferenceAdapterPosition(preference) != RecyclerView.NO_POSITION));
+    }
+
     protected void onDataSetChanged() {
         highlightPreferenceIfNeeded();
         updateEmptyView();
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index b0c3b65..3cded6a 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -311,11 +311,10 @@
             if (appHasSpaceManagementUI) {
                 mButtonsPref.setButton1Text(R.string.manage_space_text);
             } else {
-                mButtonsPref
-                        .setButton1Text(R.string.clear_user_data_text)
-                        .setButton1Icon(R.drawable.ic_settings_delete);
+                mButtonsPref.setButton1Text(R.string.clear_user_data_text);
             }
-            mButtonsPref.setButton1OnClickListener(v -> handleClearDataClick());
+            mButtonsPref.setButton1Icon(R.drawable.ic_settings_delete)
+                    .setButton1OnClickListener(v -> handleClearDataClick());
         }
 
         if (mAppsControlDisallowedBySystem) {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index f2e3d73..6c8a7d5 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -314,6 +314,13 @@
     }
 
     /**
+     * Get current PreferenceController(s)
+     */
+    protected Collection<List<AbstractPreferenceController>> getPreferenceControllers() {
+        return mPreferenceControllers.values();
+    }
+
+    /**
      * Update state of each preference managed by PreferenceController.
      */
     protected void updatePreferenceStates() {
diff --git a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
new file mode 100644
index 0000000..e92cdfc
--- /dev/null
+++ b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+abstract class AbstractMobileNetworkSettings extends RestrictedDashboardFragment {
+
+    private static final String LOG_TAG = "AbsNetworkSettings";
+
+    private List<AbstractPreferenceController> mHiddenControllerList =
+            new ArrayList<AbstractPreferenceController>();
+
+    /**
+     * @param restrictionKey The restriction key to check before pin protecting
+     *            this settings page. Pass in {@link RESTRICT_IF_OVERRIDABLE} if it should
+     *            be protected whenever a restrictions provider is set. Pass in
+     *            null if it should never be protected.
+     */
+    AbstractMobileNetworkSettings(String restrictionKey) {
+        super(restrictionKey);
+    }
+
+    List<AbstractPreferenceController> getPreferenceControllersAsList() {
+        final List<AbstractPreferenceController> result =
+                new ArrayList<AbstractPreferenceController>();
+        getPreferenceControllers().forEach(controllers -> result.addAll(controllers));
+        return result;
+    }
+
+    TelephonyStatusControlSession setTelephonyAvailabilityStatus(
+            Collection<AbstractPreferenceController> listOfPrefControllers) {
+        return (new TelephonyStatusControlSession.Builder(listOfPrefControllers))
+                .build();
+    }
+
+    @Override
+    public void onExpandButtonClick() {
+        final PreferenceScreen screen = getPreferenceScreen();
+        mHiddenControllerList.stream()
+                .filter(controller -> controller.isAvailable())
+                .forEach(controller -> {
+                    final String key = controller.getPreferenceKey();
+                    final Preference preference = screen.findPreference(key);
+                    controller.updateState(preference);
+                });
+        super.onExpandButtonClick();
+    }
+
+    /*
+     * Replace design within {@link DashboardFragment#updatePreferenceStates()}
+     */
+    @Override
+    protected void updatePreferenceStates() {
+        mHiddenControllerList.clear();
+
+        final PreferenceScreen screen = getPreferenceScreen();
+        getPreferenceControllersAsList().forEach(controller -> {
+            final String key = controller.getPreferenceKey();
+            if (TextUtils.isEmpty(key)) {
+                return;
+            }
+            final Preference preference = screen.findPreference(key);
+            if (preference == null) {
+                return;
+            }
+            if (!isPreferenceExpanded(preference)) {
+                mHiddenControllerList.add(controller);
+                return;
+            }
+            if (!controller.isAvailable()) {
+                return;
+            }
+            controller.updateState(preference);
+        });
+    }
+
+
+}
diff --git a/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java b/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java
index 880c444..6eebe6b 100644
--- a/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java
@@ -98,7 +98,7 @@
     @Override
     public int getAvailabilityStatus(int subId) {
         PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(subId);
-        boolean shouldShowPresence = bundle.getBoolean(
+        boolean shouldShowPresence = bundle != null && bundle.getBoolean(
                 CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*default*/);
         return shouldShowPresence ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 30ea0c0..7c1c8dd 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -32,10 +32,13 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.FeatureFlags;
-import com.android.settings.dashboard.RestrictedDashboardFragment;
 import com.android.settings.datausage.BillingCyclePreferenceController;
 import com.android.settings.datausage.DataUsageSummaryPreferenceController;
 import com.android.settings.development.featureflags.FeatureFlagPersistent;
@@ -48,16 +51,19 @@
 import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class MobileNetworkSettings extends RestrictedDashboardFragment {
+public class MobileNetworkSettings extends AbstractMobileNetworkSettings {
 
     private static final String LOG_TAG = "NetworkSettings";
     public static final int REQUEST_CODE_EXIT_ECM = 17;
@@ -117,14 +123,14 @@
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         mSubId = getArguments().getInt(Settings.EXTRA_SUB_ID,
                 MobileNetworkUtils.getSearchableSubscriptionId(context));
+        Log.i(LOG_TAG, "display subId: " + mSubId);
 
-        if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2) &&
-            mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            return Arrays.asList(
-                    new DataUsageSummaryPreferenceController(getActivity(), getSettingsLifecycle(),
-                            this, mSubId));
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return Arrays.asList();
         }
-        return Arrays.asList();
+        return Arrays.asList(
+                new DataUsageSummaryPreferenceController(getActivity(), getSettingsLifecycle(),
+                        this, mSubId));
     }
 
     @Override
@@ -185,13 +191,18 @@
     @Override
     public void onCreate(Bundle icicle) {
         Log.i(LOG_TAG, "onCreate:+");
+
+        final TelephonyStatusControlSession session =
+                setTelephonyAvailabilityStatus(getPreferenceControllersAsList());
+
         super.onCreate(icicle);
         final Context context = getContext();
-
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mTelephonyManager = context.getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mSubId);
 
+        session.close();
+
         onRestoreInstance(icicle);
     }
 
@@ -250,8 +261,7 @@
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2) &&
-                mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
             final MenuItem item = menu.add(Menu.NONE, R.id.edit_sim_name, Menu.NONE,
                     R.string.mobile_network_sim_name);
             item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
@@ -262,8 +272,7 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem menuItem) {
-        if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2) &&
-                mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
             if (menuItem.getItemId() == R.id.edit_sim_name) {
                 RenameMobileNetworkDialogFragment.newInstance(mSubId).show(
                         getFragmentManager(), RenameMobileNetworkDialogFragment.TAG);
diff --git a/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java b/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
new file mode 100644
index 0000000..c1acd91
--- /dev/null
+++ b/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Interface letting {@link TelephonyTogglePreferenceController and
+ * @link TelephonyBasePreferenceController} can handle availability status.
+ */
+package com.android.settings.network.telephony;
+
+import android.content.Context;
+
+public interface TelephonyAvailabilityHandler {
+
+    /**
+     * Set availability status of preference controller to a fixed value.
+     * @param status is the given status. Which will be reported from
+     * {@link BasePreferenceController#getAvailabilityStatus()}
+     */
+    void setAvailabilityStatus(int status);
+
+    /**
+     * Do not set availability, use
+     * {@link MobileNetworkUtils#getAvailability(Context, int, TelephonyAvailabilityCallback)}
+     * to get the availability.
+     */
+    void unsetAvailabilityStatus();
+}
diff --git a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
index 241dc5d..2bd7de9 100644
--- a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
@@ -23,12 +23,16 @@
 
 import com.android.settings.core.BasePreferenceController;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * {@link BasePreferenceController} that used by all preferences that requires subscription id.
  */
 public abstract class TelephonyBasePreferenceController extends BasePreferenceController
-        implements TelephonyAvailabilityCallback {
+        implements TelephonyAvailabilityCallback, TelephonyAvailabilityHandler {
     protected int mSubId;
+    private AtomicInteger mAvailabilityStatus = new AtomicInteger(0);
+    private AtomicInteger mSetSessionCount = new AtomicInteger(0);
 
     public TelephonyBasePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -37,7 +41,22 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return MobileNetworkUtils.getAvailability(mContext, mSubId, this::getAvailabilityStatus);
+        if (mSetSessionCount.get() <= 0) {
+            mAvailabilityStatus.set(MobileNetworkUtils
+                    .getAvailability(mContext, mSubId, this::getAvailabilityStatus));
+        }
+        return mAvailabilityStatus.get();
+    }
+
+    @Override
+    public void setAvailabilityStatus(int status) {
+        mAvailabilityStatus.set(status);
+        mSetSessionCount.getAndIncrement();
+    }
+
+    @Override
+    public void unsetAvailabilityStatus() {
+        mSetSessionCount.getAndDecrement();
     }
 
     /**
diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
new file mode 100644
index 0000000..12c9bee
--- /dev/null
+++ b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony;
+
+import android.util.Log;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * Session for controlling the status of TelephonyPreferenceController(s).
+ *
+ * Within this session, result of {@link BasePreferenceController#availabilityStatus()}
+ * would be under control.
+ */
+public class TelephonyStatusControlSession implements AutoCloseable {
+
+    private static final String LOG_TAG = "TelephonyStatusControlSS";
+
+    private Collection<AbstractPreferenceController> mControllers;
+    private Future<Boolean> mResult;
+
+    /**
+     * Buider of session
+     */
+    public static class Builder {
+        private Collection<AbstractPreferenceController> mControllers;
+
+        /**
+         * Constructor
+         *
+         * @param controllers is a collection of {@link AbstractPreferenceController}
+         *        which would have {@link BasePreferenceController#availabilityStatus()}
+         *        under control within this session.
+         */
+        public Builder(Collection<AbstractPreferenceController> controllers) {
+            mControllers = controllers;
+        }
+
+        /**
+         * Method to build this session.
+         * @return {@link TelephonyStatusControlSession} session been setup.
+         */
+        public TelephonyStatusControlSession build() {
+            return new TelephonyStatusControlSession(mControllers);
+        }
+    }
+
+    private TelephonyStatusControlSession(Collection<AbstractPreferenceController> controllers) {
+        mControllers = controllers;
+        mResult = ThreadUtils.postOnBackgroundThread(() ->
+            setupAvailabilityStatus(controllers)
+        );
+    }
+
+    /**
+     * Close the session.
+     *
+     * No longer control the status.
+     */
+    public void close() {
+        //check the background thread is finished then unset the status of availability.
+        try {
+            mResult.get();
+        } catch (ExecutionException | InterruptedException exception) {
+            Log.e(LOG_TAG, "setup availability status failed!", exception);
+        }
+        unsetAvailabilityStatus(mControllers);
+    }
+
+    private Boolean setupAvailabilityStatus(
+            Collection<AbstractPreferenceController> controllerLists) {
+        try {
+            controllerLists.stream()
+                    .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
+                    .map(TelephonyAvailabilityHandler.class::cast)
+                    .forEach(controller -> {
+                        int status = ((BasePreferenceController) controller)
+                                .getAvailabilityStatus();
+                        controller.setAvailabilityStatus(status);
+                    });
+            return true;
+        } catch (Exception exception) {
+            Log.e(LOG_TAG, "Setup availability status failed!", exception);
+            return false;
+        }
+    }
+
+    private void unsetAvailabilityStatus(
+            Collection<AbstractPreferenceController> controllerLists) {
+        controllerLists.stream()
+                .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
+                .map(TelephonyAvailabilityHandler.class::cast)
+                .forEach(controller -> {
+                    controller.unsetAvailabilityStatus();
+                });
+    }
+}
diff --git a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java
index fc30030..ecad755 100644
--- a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java
+++ b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java
@@ -23,12 +23,16 @@
 
 import com.android.settings.core.TogglePreferenceController;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * {@link TogglePreferenceController} that used by all preferences that requires subscription id.
  */
 public abstract class TelephonyTogglePreferenceController extends TogglePreferenceController
-        implements TelephonyAvailabilityCallback {
+        implements TelephonyAvailabilityCallback, TelephonyAvailabilityHandler {
     protected int mSubId;
+    private AtomicInteger mAvailabilityStatus = new AtomicInteger(0);
+    private AtomicInteger mSetSessionCount = new AtomicInteger(0);
 
     public TelephonyTogglePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -37,9 +41,25 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return MobileNetworkUtils.getAvailability(mContext, mSubId, this::getAvailabilityStatus);
+        if (mSetSessionCount.get() <= 0) {
+            mAvailabilityStatus.set(MobileNetworkUtils
+                    .getAvailability(mContext, mSubId, this::getAvailabilityStatus));
+        }
+        return mAvailabilityStatus.get();
     }
 
+    @Override
+    public void setAvailabilityStatus(int status) {
+        mAvailabilityStatus.set(status);
+        mSetSessionCount.getAndIncrement();
+    }
+
+    @Override
+    public void unsetAvailabilityStatus() {
+        mSetSessionCount.getAndDecrement();
+    }
+
+
     /**
      * Get carrier config based on specific subscription id.
      *
diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index 378606a6..26c1565 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.pm.PackageManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.os.Bundle;
@@ -43,6 +44,9 @@
 import com.android.settings.R;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Dialog showing information about a VPN configuration. The dialog
@@ -129,6 +133,7 @@
 
         // Second, copy values from the profile.
         mName.setText(mProfile.name);
+        setTypesByFeature(mType);
         mType.setSelection(mProfile.type);
         mServer.setText(mProfile.server);
         if (mProfile.saveLogin) {
@@ -487,6 +492,25 @@
         return true;
     }
 
+    private void setTypesByFeature(Spinner typeSpinner) {
+        String[] types = getContext().getResources().getStringArray(R.array.vpn_types);
+        if (!getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_IPSEC_TUNNELS)) {
+            final List<String> typesList = new ArrayList<>(Arrays.asList(types));
+
+            // This must be removed from back to front in order to ensure index consistency
+            typesList.remove(VpnProfile.TYPE_IKEV2_IPSEC_RSA);
+            typesList.remove(VpnProfile.TYPE_IKEV2_IPSEC_PSK);
+            typesList.remove(VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS);
+
+            types = typesList.toArray(new String[0]);
+        }
+        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
+                getContext(), android.R.layout.simple_spinner_item, types);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        typeSpinner.setAdapter(adapter);
+    }
+
     private void loadCertificates(Spinner spinner, String prefix, int firstId, String selected) {
         Context context = getContext();
         String first = (firstId == 0) ? "" : context.getString(firstId);
diff --git a/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java b/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java
index 15a769c..3097715 100644
--- a/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java
+++ b/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java
@@ -59,7 +59,7 @@
     private static final String SCREEN_TIME_OUT = "7200000";
     private static final String DEFAULT_SCREEN_TIMEOUT = "15000";
     private static final int TIME_OUT = 5000;
-    private static final int TEST_TIME = 100;
+    private static final int TEST_TIME = 10;
     private static final Pattern PATTERN = Pattern.compile("TotalTime:\\s[0-9]*");
     private static final Page[] PAGES;
 
@@ -79,6 +79,7 @@
     private Instrumentation mInstrumentation;
     private Map<String, ArrayList<Integer>> mResult;
     private String mDefaultScreenTimeout;
+    private String mDefaultAirplaneModeStatus;
 
     @Before
     public void setUp() throws Exception {
@@ -88,7 +89,9 @@
         mResult = new LinkedHashMap<>();
         mDefaultScreenTimeout = mDevice.executeShellCommand(
                 "settings get system screen_off_timeout");
+        mDefaultAirplaneModeStatus = getAirplaneModeStatus();
         setScreenTimeOut(SCREEN_TIME_OUT);
+        setAirplaneMode();
         mDevice.pressHome();
         mDevice.waitForIdle(TIME_OUT);
 
@@ -102,6 +105,7 @@
         putResultToBundle();
         mInstrumentation.sendStatus(0, mBundle);
         resetScreenTimeout();
+        resetAirplaneMode();
         closeApp();
     }
 
@@ -123,12 +127,12 @@
         handleLaunchResult(title, mString);
     }
 
-    private void handleLaunchResult(String title, String s) {
-        Matcher mMatcher = PATTERN.matcher(s);
+    private void handleLaunchResult(String title, String shellCommandResult) {
+        Matcher mMatcher = PATTERN.matcher(shellCommandResult);
         if (mMatcher.find()) {
             mResult.get(title).add(Integer.valueOf(mMatcher.group().split("\\s")[1]));
         } else {
-            fail("Some pages can't be found");
+            fail(String.format("Not found %s.\n %s", title, shellCommandResult));
         }
     }
 
@@ -145,6 +149,10 @@
                     getMin(mResult.get(string)));
             mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "avg"),
                     getAvg(mResult.get(string)));
+            mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "all_results"),
+                    mResult.get(string).toString());
+            mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "results_count"),
+                    String.valueOf(mResult.get(string).size()));
         }
     }
 
@@ -171,4 +179,27 @@
         }
         setScreenTimeOut(timeout);
     }
+
+    private void setAirplaneMode() throws Exception {
+        if (mDefaultAirplaneModeStatus.equals("0\n")) {
+            clickAirplaneMode();
+        }
+    }
+
+    private void resetAirplaneMode() throws Exception {
+        if (!getAirplaneModeStatus().equals(mDefaultAirplaneModeStatus)) {
+            clickAirplaneMode();
+        }
+    }
+
+    private void clickAirplaneMode() throws Exception {
+        mDevice.executeShellCommand("am start -W -a android.settings.AIRPLANE_MODE_SETTINGS");
+        mDevice.waitForIdle(TIME_OUT);
+        mDevice.findObject(By.textContains("Airplane")).click();
+        mDevice.waitForIdle(TIME_OUT);
+    }
+
+    private String getAirplaneModeStatus() throws Exception {
+        return mDevice.executeShellCommand("settings get global airplane_mode_on");
+    }
 }
\ No newline at end of file