Merge "Add since_visible_millis to Settings action logs."
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
index 94435bd..38b9d28 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
@@ -220,6 +220,9 @@
 
     public Tile getSuggestion(int position) {
         final long itemId = getItemId(position);
+        if (mSuggestions == null) {
+            return null;
+        }
         for (Tile tile : mSuggestions) {
             if (Objects.hash(tile.title) == itemId) {
                 return tile;
@@ -230,6 +233,9 @@
 
     public Suggestion getSuggestionsV2(int position) {
         final long itemId = getItemId(position);
+        if (mSuggestionsV2 == null) {
+            return null;
+        }
         for (Suggestion suggestion : mSuggestionsV2) {
             if (Objects.hash(suggestion.getId()) == itemId) {
                 return suggestion;
diff --git a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java
index a7d45eb..7715c25 100644
--- a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java
+++ b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java
@@ -17,6 +17,7 @@
 package com.android.settings.development;
 
 import android.content.Context;
+import android.content.Intent;
 
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -35,12 +36,28 @@
     }
 
     /**
+     * Called when an activity returns to the DeveloperSettingsDashboardFragment.
+     *
+     * @param requestCode The integer request code originally supplied to
+     *                    startActivityForResult(), allowing you to identify who this
+     *                    result came from.
+     * @param resultCode  The integer result code returned by the child activity
+     *                    through its setResult().
+     * @param data        An Intent, which can return result data to the caller
+     *                    (various data can be attached to Intent "extras").
+     * @return true if the controller handled the activity result
+     */
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        return false;
+    }
+
+    /**
      * Called when developer options is enabled
      */
     public abstract void onDeveloperOptionsEnabled();
 
     /**
-     *Called when developer options is disabled
+     * Called when developer options is disabled
      */
     public abstract void onDeveloperOptionsDisabled();
 }
diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
new file mode 100644
index 0000000..54d1fa3
--- /dev/null
+++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.development;
+
+/**
+ * Interface for storing Activity request codes in development options
+ */
+public interface DevelopmentOptionsActivityRequestCodes {
+    int REQUEST_CODE_ENABLE_OEM_UNLOCK = 0;
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 8df25c2..0662308 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -16,10 +16,13 @@
 
 package com.android.settings.development;
 
+import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.SearchIndexableResource;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 import android.widget.Switch;
 
@@ -40,7 +43,7 @@
 import java.util.List;
 
 public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment
-        implements SwitchBar.OnSwitchChangeListener {
+        implements SwitchBar.OnSwitchChangeListener, OemUnlockDialogHost {
 
     private static final String TAG = "DevSettingsDashboard";
 
@@ -104,6 +107,33 @@
     }
 
     @Override
+    public void onOemUnlockDialogConfirmed() {
+        final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
+                OemUnlockPreferenceController.class);
+        controller.onOemUnlockConfirmed();
+    }
+
+    @Override
+    public void onOemUnlockDialogDismissed() {
+        final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
+                OemUnlockPreferenceController.class);
+        controller.onOemUnlockDismissed();
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        for (AbstractPreferenceController controller : mPreferenceControllers) {
+            if (controller instanceof DeveloperOptionsPreferenceController) {
+                if (((DeveloperOptionsPreferenceController) controller).onActivityResult(
+                        requestCode, resultCode, data)) {
+                    return;
+                }
+            }
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -121,7 +151,8 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        mPreferenceControllers = buildPreferenceControllers(context, getLifecycle());
+        mPreferenceControllers = buildPreferenceControllers(context, getActivity(), getLifecycle(),
+                this /* devOptionsDashboardFragment */);
         return mPreferenceControllers;
     }
 
@@ -140,14 +171,92 @@
     }
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
-            Lifecycle lifecycle) {
+            Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        // take bug report
+        // desktop backup password
         controllers.add(new StayAwakePreferenceController(context, lifecycle));
+        // hdcp checking
         controllers.add(new BluetoothSnoopLogPreferenceController(context));
-
+        controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
+        // running services
+        // convert to file encryption
+        // picture color mode
+        // webview implementation
+        // cool color temperature
+        // automatic system updates
+        // system ui demo mode
+        // quick settings developer tiles
+        // usb debugging
+        // revoke usb debugging authorizations
+        // local terminal
+        // bug report shortcut
+        // select mock location app
+        // enable view attribute inspection
+        // select debug app
+        // wait for debugger
+        // verify apps over usb
+        // logger buffer sizes
+        // store logger data persistently on device
+        // telephony monitor
+        // camera laser sensor
+        // camera HAL HDR+
+        // feature flags
+        // wireless display certification
+        // enable wi-fi verbose logging
+        // aggressive wifi to mobile handover
+        // always allow wifi roam scans
+        // mobile always active
+        // tethering hardware acceleration
+        // select usb configuration
+        // show bluetooth devices without names
+        // disable absolute volume
+        // enable in-band ringing
+        // bluetooth avrcp version
+        // bluetooth audio codec
+        // bluetooth audio sample rate
+        // bluetooth audio bits per sample
+        // bluetooth audio channel mode
+        // bluetooth audio ldac codec: playback quality
+        // show taps
+        // pointer location
+        // show surface updates
+        // show layout bounds
+        // force rtl layout direction
+        // window animation scale
+        // transition animation scale
+        // animator duration scale
+        // simulate secondary displays
+        // smallest width
+        // force gpu rendering
+        // show gpu view updates
+        // show hardware layers updates
+        // debug gpu overdraw
+        // debug non-rectangular clip operations
+        // force 4x msaa
+        // disable hw overlays
+        // simulate color space
+        // set gpu renderer
+        // disable usb audio routing
+        // strict mode enabled
+        // profile gpu rendering
+        // don't keep activities
+        // background process limit
+        // background check
+        // show all anrs
+        // show notification channel warnings
+        // inactive apps
+        // force allow apps on external
+        // force activities to be resizable
+        // reset shortcutmanager rate-limiting
         return controllers;
     }
 
+    @VisibleForTesting
+    <T extends AbstractPreferenceController> T getDevelopmentOptionsController(Class<T> clazz) {
+        return getPreferenceController(clazz);
+    }
+
     /**
      * For Search.
      */
@@ -171,7 +280,8 @@
                 @Override
                 public List<AbstractPreferenceController> getPreferenceControllers(Context
                         context) {
-                    return buildPreferenceControllers(context, null /* lifecycle */);
+                    return buildPreferenceControllers(context, null /* activity */,
+                            null /* lifecycle */, null /* devOptionsDashboardFragment */);
                 }
             };
 }
diff --git a/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java b/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java
new file mode 100644
index 0000000..2486ef5
--- /dev/null
+++ b/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.development;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class EnableOemUnlockSettingWarningDialog extends InstrumentedDialogFragment implements
+        DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+
+    public static final String TAG = "EnableOemUnlockDlg";
+
+    public static void show(Fragment host) {
+        final FragmentManager manager = host.getActivity().getFragmentManager();
+        if (manager.findFragmentByTag(TAG) == null) {
+            final EnableOemUnlockSettingWarningDialog dialog =
+                    new EnableOemUnlockSettingWarningDialog();
+            dialog.setTargetFragment(host, 0 /* requestCode */);
+            dialog.show(manager, TAG);
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.DIALOG_ENABLE_OEM_UNLOCKING;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.confirm_enable_oem_unlock_title)
+                .setMessage(R.string.confirm_enable_oem_unlock_text)
+                .setPositiveButton(R.string.enable_text, this /* onClickListener */)
+                .setNegativeButton(android.R.string.cancel, this /* onClickListener */)
+                .create();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment();
+        if (host == null) {
+            return;
+        }
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            host.onOemUnlockDialogConfirmed();
+        } else {
+            host.onOemUnlockDialogDismissed();
+        }
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        super.onDismiss(dialog);
+        final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment();
+        if (host == null) {
+            return;
+        }
+        host.onOemUnlockDialogDismissed();
+    }
+}
diff --git a/src/com/android/settings/development/OemUnlockDialogHost.java b/src/com/android/settings/development/OemUnlockDialogHost.java
new file mode 100644
index 0000000..c134e9c
--- /dev/null
+++ b/src/com/android/settings/development/OemUnlockDialogHost.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.development;
+
+/**
+ * Interface for OemUnlockDialogFragment callbacks.
+ */
+public interface OemUnlockDialogHost {
+
+    /**
+     * Called when the user presses enable on the warning dialog.
+     */
+    void onOemUnlockDialogConfirmed();
+
+    /**
+     * Called when the user dismisses or cancels the warning dialog.
+     */
+    void onOemUnlockDialogDismissed();
+}
diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java
new file mode 100644
index 0000000..7d85d2e
--- /dev/null
+++ b/src/com/android/settings/development/OemUnlockPreferenceController.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2017 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.development;
+
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+        .REQUEST_CODE_ENABLE_OEM_UNLOCK;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.oemlock.OemLockManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.R;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class OemUnlockPreferenceController extends DeveloperOptionsPreferenceController implements
+        Preference.OnPreferenceChangeListener {
+
+    private static final String PREFERENCE_KEY = "oem_unlock_enable";
+
+    private final OemLockManager mOemLockManager;
+    private final UserManager mUserManager;
+    private final TelephonyManager mTelephonyManager;
+    private final DevelopmentSettingsDashboardFragment mFragment;
+    private final ChooseLockSettingsHelper mChooseLockSettingsHelper;
+    private RestrictedSwitchPreference mPreference;
+
+    public OemUnlockPreferenceController(Context context, Activity activity,
+            DevelopmentSettingsDashboardFragment fragment) {
+        super(context);
+        mOemLockManager = (OemLockManager) context.getSystemService(Context.OEM_LOCK_SERVICE);
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mFragment = fragment;
+        if (activity != null || mFragment != null) {
+            mChooseLockSettingsHelper = new ChooseLockSettingsHelper(activity, mFragment);
+        } else {
+            mChooseLockSettingsHelper = null;
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mOemLockManager != null;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREFERENCE_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = (RestrictedSwitchPreference) screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        boolean isUnlocked = (Boolean) newValue;
+        if (isUnlocked) {
+            if (!showKeyguardConfirmation(mContext.getResources(),
+                    REQUEST_CODE_ENABLE_OEM_UNLOCK)) {
+                confirmEnableOemUnlock();
+            }
+        } else {
+            mOemLockManager.setOemUnlockAllowedByUser(false);
+        }
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        mPreference.setChecked(mOemLockManager.isOemUnlockAllowed());
+        updateOemUnlockSettingDescription();
+        // Showing mEnableOemUnlock preference as device has persistent data block.
+        mPreference.setDisabledByAdmin(null);
+        mPreference.setEnabled(enableOemUnlockPreference());
+        if (mPreference.isEnabled()) {
+            // Check restriction, disable mEnableOemUnlock and apply policy transparency.
+            mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+        }
+    }
+
+    @Override
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE_ENABLE_OEM_UNLOCK) {
+            if (resultCode == Activity.RESULT_OK) {
+                if (mPreference.isChecked()) {
+                    confirmEnableOemUnlock();
+                } else {
+                    mOemLockManager.setOemUnlockAllowedByUser(false);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onDeveloperOptionsEnabled() {
+        handleDeveloperOptionsToggled();
+    }
+
+    @Override
+    public void onDeveloperOptionsDisabled() {
+        handleDeveloperOptionsToggled();
+    }
+
+    public void onOemUnlockConfirmed() {
+        mOemLockManager.setOemUnlockAllowedByUser(true);
+    }
+
+    public void onOemUnlockDismissed() {
+        if (mPreference == null) {
+            return;
+        }
+        updateState(mPreference);
+    }
+
+    private void handleDeveloperOptionsToggled() {
+        if (mPreference == null) {
+            return;
+        }
+
+        mPreference.setEnabled(enableOemUnlockPreference());
+        if (mPreference.isEnabled()) {
+            // Check restriction, disable mEnableOemUnlock and apply policy transparency.
+            mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+        }
+    }
+
+    private void updateOemUnlockSettingDescription() {
+        int oemUnlockSummary = R.string.oem_unlock_enable_summary;
+        if (isBootloaderUnlocked()) {
+            oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked;
+        } else if (isSimLockedDevice()) {
+            oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device;
+        } else if (!isOemUnlockAllowedByUserAndCarrier()) {
+            // If the device isn't SIM-locked but OEM unlock is disallowed by some party, this
+            // means either some other carrier restriction is in place or the device hasn't been
+            // able to confirm which restrictions (SIM-lock or otherwise) apply.
+            oemUnlockSummary =
+                    R.string.oem_unlock_enable_disabled_summary_connectivity_or_locked;
+        }
+        mPreference.setSummary(mContext.getResources().getString(oemUnlockSummary));
+    }
+
+    /** Returns {@code true} if the device is SIM-locked. Otherwise, returns {@code false}. */
+    private boolean isSimLockedDevice() {
+        int phoneCount = mTelephonyManager.getPhoneCount();
+        for (int i = 0; i < phoneCount; i++) {
+            if (mTelephonyManager.getAllowedCarriers(i).size() > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns {@code true} if the bootloader has been unlocked. Otherwise, returns {code false}.
+     */
+    private boolean isBootloaderUnlocked() {
+        return mOemLockManager.isDeviceOemUnlocked();
+    }
+
+    private boolean enableOemUnlockPreference() {
+        return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();
+    }
+
+
+    @VisibleForTesting
+    boolean showKeyguardConfirmation(Resources resources, int requestCode) {
+        return mChooseLockSettingsHelper.launchConfirmationActivity(
+                requestCode, resources.getString(R.string.oem_unlock_enable));
+    }
+
+    @VisibleForTesting
+    void confirmEnableOemUnlock() {
+        EnableOemUnlockSettingWarningDialog.show(mFragment);
+    }
+
+    /**
+     * Returns whether OEM unlock is allowed by the user and carrier.
+     *
+     * This does not take into account any restrictions imposed by the device policy.
+     */
+    @VisibleForTesting
+    boolean isOemUnlockAllowedByUserAndCarrier() {
+        final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
+        return mOemLockManager.isOemUnlockAllowedByCarrier()
+                && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET,
+                userHandle);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
index 1e4ad79..6b80465 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
@@ -203,7 +203,7 @@
     public void onBindViewHolder_v2_itemViewShouldHandleClick()
             throws PendingIntent.CanceledException {
         final List<Suggestion> packages = makeSuggestionsV2("pkg1");
-        setupSuggestions(mActivity, null /* suggestionV1 */ , packages);
+        setupSuggestions(mActivity, null /* suggestionV1 */, packages);
 
         mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
         mSuggestionHolder.itemView.performClick();
@@ -233,6 +233,22 @@
         assertThat(itemView.getChildCount()).isEqualTo(1);
     }
 
+    @Test
+    public void getSuggestionsV2_shouldReturnSuggestionWhenMatch() {
+        final List<Suggestion> suggestionsV2 = makeSuggestionsV2("pkg1");
+        setupSuggestions(mActivity, null /* suggestionV1 */, suggestionsV2);
+
+        assertThat(mSuggestionAdapter.getSuggestion(0)).isNull();
+        assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNotNull();
+
+        List<Tile> suggestionsV1 = makeSuggestions("pkg1");
+        setupSuggestions(mActivity, suggestionsV1, null /* suggestionV2 */);
+
+        assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNull();
+        assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
+
+    }
+
     private void setupSuggestions(Context context, List<Tile> suggestions,
             List<Suggestion> suggestionsV2) {
         mSuggestionAdapter = new SuggestionAdapter(context, suggestions, suggestionsV2,
diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
index a001aaf..c8748de 100644
--- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
@@ -17,7 +17,11 @@
 package com.android.settings.development;
 
 import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -161,6 +165,24 @@
                 .isFalse();
     }
 
+    @Test
+    public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() {
+        final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
+        doReturn(controller).when(mDashboard).getDevelopmentOptionsController(
+                OemUnlockPreferenceController.class);
+        mDashboard.onOemUnlockDialogConfirmed();
+        verify(controller).onOemUnlockConfirmed();
+    }
+
+    @Test
+    public void onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed() {
+        final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
+        doReturn(controller).when(mDashboard).getDevelopmentOptionsController(
+                OemUnlockPreferenceController.class);
+        mDashboard.onOemUnlockDialogDismissed();
+        verify(controller).onOemUnlockDismissed();
+    }
+
     @Implements(EnableDevelopmentSettingWarningDialog.class)
     public static class ShadowEnableDevelopmentSettingWarningDialog {
 
diff --git a/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
new file mode 100644
index 0000000..1367870
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 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.development;
+
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+        .REQUEST_CODE_ENABLE_OEM_UNLOCK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.UserManager;
+import android.service.oemlock.OemLockManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class OemUnlockPreferenceControllerTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private DevelopmentSettingsDashboardFragment mFragment;
+    @Mock
+    private RestrictedSwitchPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private OemLockManager mOemLockManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private Resources mResources;
+    private OemUnlockPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.OEM_LOCK_SERVICE)).thenReturn(mOemLockManager);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mContext.getResources()).thenReturn(mResources);
+        mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void isAvailable_shouldReturnTrueWhenOemLockManagerIsNotNull() {
+        boolean returnValue = mController.isAvailable();
+
+        assertThat(returnValue).isTrue();
+    }
+
+    @Test
+    public void isAvailable_shouldReturnFalseWhenOemLockManagerIsNull() {
+        when(mContext.getSystemService(Context.OEM_LOCK_SERVICE)).thenReturn(null);
+        mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment);
+        boolean returnValue = mController.isAvailable();
+
+        assertThat(returnValue).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_turnOnUnlock() {
+        mController = spy(mController);
+        doReturn(false).when(mController).showKeyguardConfirmation(mResources,
+                REQUEST_CODE_ENABLE_OEM_UNLOCK);
+        doNothing().when(mController).confirmEnableOemUnlock();
+        mController.onPreferenceChange(null, true);
+
+        verify(mController).confirmEnableOemUnlock();
+    }
+
+    @Test
+    public void onPreferenceChanged_turnOffUnlock() {
+        mController.onPreferenceChange(null, false);
+
+        verify(mOemLockManager).setOemUnlockAllowedByUser(false);
+    }
+
+    @Test
+    public void updateState_preferenceShouldBeCheckedAndShouldBeDisabled() {
+        mController = spy(mController);
+        when(mOemLockManager.isOemUnlockAllowed()).thenReturn(true);
+        doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier();
+        when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(true);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+        verify(mPreference).setEnabled(false);
+    }
+
+    @Test
+    public void updateState_preferenceShouldBeUncheckedAndShouldBeDisabled() {
+        mController = spy(mController);
+        when(mOemLockManager.isOemUnlockAllowed()).thenReturn(false);
+        doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier();
+        when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(true);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+        verify(mPreference).setEnabled(false);
+    }
+
+    @Test
+    public void updateState_preferenceShouldBeCheckedAndShouldBeEnabled() {
+        mController = spy(mController);
+        when(mOemLockManager.isOemUnlockAllowed()).thenReturn(true);
+        doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier();
+        when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(false);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+        verify(mPreference).setEnabled(true);
+    }
+
+    @Test
+    public void onActivityResult_shouldReturnTrue() {
+        final boolean result = mController.onActivityResult(REQUEST_CODE_ENABLE_OEM_UNLOCK,
+                Activity.RESULT_OK, null);
+
+        assertThat(result).isTrue();
+    }
+
+    @Test
+    public void onActivityResult_shouldReturnFalse() {
+        final boolean result = mController.onActivityResult(123454,
+                1434, null);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void onDeveloperOptionsEnabled_preferenceShouldCheckRestriction() {
+        mController = spy(mController);
+        doReturn(false).when(mController).isOemUnlockAllowedByUserAndCarrier();
+        when(mPreference.isEnabled()).thenReturn(true);
+        mController.onDeveloperOptionsEnabled();
+
+        verify(mPreference).checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+
+    }
+
+    @Test
+    public void onDeveloperOptionsDisabled_preferenceShouldCheckRestriction() {
+        mController = spy(mController);
+        doReturn(false).when(mController).isOemUnlockAllowedByUserAndCarrier();
+        when(mPreference.isEnabled()).thenReturn(true);
+        mController.onDeveloperOptionsDisabled();
+
+        verify(mPreference).checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
+
+    }
+
+    @Test
+    public void onOemUnlockConfirmed_oemManagerShouldSetUnlockAllowedByUser() {
+        mController.onOemUnlockConfirmed();
+
+        verify(mOemLockManager).setOemUnlockAllowedByUser(true);
+    }
+}