Check for user restrictions disallowing BT in BluetoothEnabler.

Test: make RunSettingsRoboTests -j40
Test: manual

BUG: 35596982
BUG: 32895313

Change-Id: Ie8f53b665a4aad4e6b17b20602cbe5502998d7a2
(cherry picked from commit 479d1971962ea79985535f00a323cd9a1597a553)
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index 22b21e4..6c41f83 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -21,13 +21,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.widget.Switch;
 import android.widget.Toast;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.SwitchWidgetController;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.WirelessUtils;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -45,6 +48,7 @@
     private boolean mValidListener;
     private final LocalBluetoothAdapter mLocalAdapter;
     private final IntentFilter mIntentFilter;
+    private final RestrictionUtils mRestrictionUtils;
 
     private static final String EVENT_DATA_IS_BT_ON = "is_bluetooth_on";
     private static final int EVENT_UPDATE_INDEX = 0;
@@ -63,6 +67,13 @@
     public BluetoothEnabler(Context context, SwitchWidgetController switchWidget,
             MetricsFeatureProvider metricsFeatureProvider, LocalBluetoothManager manager,
             int metricsEvent) {
+        this(context, switchWidget, metricsFeatureProvider, manager, metricsEvent,
+                new RestrictionUtils());
+    }
+
+    public BluetoothEnabler(Context context, SwitchWidgetController switchWidget,
+            MetricsFeatureProvider metricsFeatureProvider, LocalBluetoothManager manager,
+            int metricsEvent, RestrictionUtils restrictionUtils) {
         mContext = context;
         mMetricsFeatureProvider = metricsFeatureProvider;
         mSwitchWidget = switchWidget;
@@ -79,6 +90,7 @@
             mLocalAdapter = manager.getBluetoothAdapter();
         }
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mRestrictionUtils = restrictionUtils;
     }
 
     public void setupSwitchController() {
@@ -90,15 +102,17 @@
     }
 
     public void resume(Context context) {
+        if (mContext != context) {
+            mContext = context;
+        }
+
+        maybeEnforceRestrictions();
+
         if (mLocalAdapter == null) {
             mSwitchWidget.setEnabled(false);
             return;
         }
 
-        if (mContext != context) {
-            mContext = context;
-        }
-
         // Bluetooth state is not sticky, so set it manually
         handleStateChanged(mLocalAdapter.getBluetoothState());
 
@@ -156,6 +170,10 @@
 
     @Override
     public boolean onSwitchToggled(boolean isChecked) {
+        if (maybeEnforceRestrictions()) {
+            return true;
+        }
+
         // Show toast message if Bluetooth is not allowed in airplane mode
         if (isChecked &&
                 !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
@@ -182,4 +200,29 @@
         mSwitchWidget.setEnabled(false);
         return true;
     }
+
+    /**
+     * Enforces user restrictions disallowing Bluetooth (or its configuration) if there are any.
+     *
+     * @return if there was any user restriction to enforce.
+     */
+    @VisibleForTesting
+    boolean maybeEnforceRestrictions() {
+        EnforcedAdmin admin = mRestrictionUtils.checkIfRestrictionEnforced(
+                mContext, UserManager.DISALLOW_BLUETOOTH);
+        if (admin == null) {
+            admin = mRestrictionUtils.checkIfRestrictionEnforced(
+                    mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH);
+        }
+        mSwitchWidget.setDisabledByAdmin(admin);
+        if (admin != null) {
+            mSwitchWidget.setChecked(false);
+            if (mSwitch != null) {
+                mSwitch.setEnabled(false);
+                mSwitch.setChecked(false);
+            }
+        }
+        return admin != null;
+    }
+
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java
index f98a209..c071570 100644
--- a/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.support.v7.preference.PreferenceScreen;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.core.lifecycle.LifecycleObserver;
@@ -26,9 +27,9 @@
 import com.android.settings.core.lifecycle.events.OnStart;
 import com.android.settings.core.lifecycle.events.OnStop;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
-import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settings.widget.MasterSwitchController;
+import com.android.settings.widget.MasterSwitchPreference;
+import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 public class BluetoothMasterSwitchPreferenceController extends PreferenceController
@@ -41,12 +42,20 @@
     private MasterSwitchPreference mBtPreference;
     private BluetoothEnabler mBluetoothEnabler;
     private BluetoothSummaryUpdater mSummaryUpdater;
+    private RestrictionUtils mRestrictionUtils;
 
     public BluetoothMasterSwitchPreferenceController(Context context,
             LocalBluetoothManager bluetoothManager) {
+        this(context, bluetoothManager, new RestrictionUtils());
+    }
+
+    @VisibleForTesting
+    public BluetoothMasterSwitchPreferenceController(Context context,
+            LocalBluetoothManager bluetoothManager, RestrictionUtils restrictionUtils) {
         super(context);
         mBluetoothManager = bluetoothManager;
         mSummaryUpdater = new BluetoothSummaryUpdater(mContext, this, mBluetoothManager);
+        mRestrictionUtils = restrictionUtils;
     }
 
     @Override
@@ -56,7 +65,8 @@
         mBluetoothEnabler = new BluetoothEnabler(mContext,
             new MasterSwitchController(mBtPreference),
             FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(), mBluetoothManager,
-            MetricsEvent.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE);
+            MetricsEvent.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
+            mRestrictionUtils);
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/RestrictionUtils.java b/src/com/android/settings/bluetooth/RestrictionUtils.java
new file mode 100644
index 0000000..9c0c481
--- /dev/null
+++ b/src/com/android/settings/bluetooth/RestrictionUtils.java
@@ -0,0 +1,43 @@
+/*
+ * 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.bluetooth;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * A utility class to aid testing.
+ */
+public class RestrictionUtils {
+
+    public RestrictionUtils() {}
+
+    /**
+     *  Utility method to check if user restriction is enforced on the current user.
+     *
+     * <p> It helps with testing - override it to avoid calling static method which calls system
+     * API.
+     */
+    public EnforcedAdmin checkIfRestrictionEnforced(Context context, String restriction) {
+        return RestrictedLockUtils.checkIfRestrictionEnforced(
+                context, restriction, UserHandle.myUserId());
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
index 7761afc..074bef2 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
@@ -15,12 +15,15 @@
  */
 package com.android.settings.bluetooth;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.os.UserManager;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.widget.MasterSwitchController;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import org.junit.Before;
@@ -30,30 +33,108 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
+import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class BluetoothEnablerTest {
 
+    private static final EnforcedAdmin FAKE_ENFORCED_ADMIN =
+            new EnforcedAdmin(new ComponentName("test.package", "test.Class"), 10);
+
     @Mock
     private MetricsFeatureProvider mMetricsFeatureProvider;
     @Mock
     private Context mContext;
+    @Mock
+    private MasterSwitchController mMasterSwitchController;
+    @Mock
+    private RestrictionUtils mRestrictionUtils;
+
+    private BluetoothEnabler mBluetoothEnabler;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mBluetoothEnabler = new BluetoothEnabler(
+                mContext,
+                mMasterSwitchController,
+                mMetricsFeatureProvider,
+                mock(LocalBluetoothManager.class),
+                123,
+                mRestrictionUtils);
     }
 
     @Test
     public void onSwitchToggled_shouldLogActionWithSuppliedEvent() {
-        BluetoothEnabler bluetoothEnabler = new BluetoothEnabler(mContext,
-            mock(MasterSwitchController.class), mMetricsFeatureProvider,
-            mock(LocalBluetoothManager.class), 123);
-        bluetoothEnabler.onSwitchToggled(false);
+        // WHEN the switch is toggled...
+        mBluetoothEnabler.onSwitchToggled(false);
 
+        // THEN the corresponding metrics action is logged.
         verify(mMetricsFeatureProvider).action(mContext, 123, false);
     }
+
+    @Test
+    public void maybeEnforceRestrictions_noRestrictions() {
+        // GIVEN there are no restrictions set...
+        when(mRestrictionUtils.checkIfRestrictionEnforced(any(Context.class), any(String.class)))
+                .thenReturn(null);
+
+        // WHEN the maybeEnforceRestrictions is called...
+        // THEN false is returned to indicate there was no restriction to enforce
+        assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isFalse();
+
+        // THEN a null EnfoceAdmin is set.
+        verify(mMasterSwitchController).setDisabledByAdmin(null);
+        // THEN the state of the switch isn't changed.
+        verify(mMasterSwitchController, never()).setChecked(anyBoolean());
+    }
+
+    @Test
+    public void maybeEnforceRestrictions_disallowBluetoothRestrictionSet() {
+        // GIVEN Bluetooth has been disallowed...
+        when(mRestrictionUtils.checkIfRestrictionEnforced(
+                mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(FAKE_ENFORCED_ADMIN);
+        when(mRestrictionUtils.checkIfRestrictionEnforced(
+                mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(null);
+
+        // WHEN the maybeEnforceRestrictions is called...
+        // THEN true is returned to indicate there was a restriction to enforce.
+        assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isTrue();
+
+        // THEN the expected EnfoceAdmin is set.
+        verify(mMasterSwitchController).setDisabledByAdmin(FAKE_ENFORCED_ADMIN);
+
+        // THEN the switch is unchecked.
+        verify(mMasterSwitchController).setChecked(false);
+    }
+
+    @Test
+    public void maybeEnforceRestrictions_disallowConfigBluetoothRestrictionSet() {
+        // GIVEN configuring Bluetooth has been disallowed...
+        when(mRestrictionUtils.checkIfRestrictionEnforced(
+                mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(null);
+        when(mRestrictionUtils.checkIfRestrictionEnforced(
+                mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(FAKE_ENFORCED_ADMIN);
+
+        // WHEN the maybeEnforceRestrictions is called...
+        // THEN true is returned to indicate there was a restriction to enforce.
+        assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isTrue();
+
+        // THEN the expected EnfoceAdmin is set.
+        verify(mMasterSwitchController).setDisabledByAdmin(FAKE_ENFORCED_ADMIN);
+
+        // THEN the switch is unchecked.
+        verify(mMasterSwitchController).setChecked(false);
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java
index 0e39c5d..177130e 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java
@@ -52,6 +52,8 @@
     private PreferenceScreen mScreen;
     @Mock
     private MasterSwitchPreference mPreference;
+    @Mock
+    private RestrictionUtils mRestrictionUtils;
 
     private Context mContext;
     private BluetoothMasterSwitchPreferenceController mController;
@@ -60,7 +62,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application.getApplicationContext();
-        mController = new BluetoothMasterSwitchPreferenceController(mContext, mBluetoothManager);
+        mController = new BluetoothMasterSwitchPreferenceController(
+                mContext, mBluetoothManager, mRestrictionUtils);
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
     }