Merge "Block location accuracy when DISALLOW_CONFIG_LOCATION is set." into pi-dev
diff --git a/src/com/android/settings/location/InjectedSetting.java b/src/com/android/settings/location/InjectedSetting.java
index e5f1e68..7eae872 100644
--- a/src/com/android/settings/location/InjectedSetting.java
+++ b/src/com/android/settings/location/InjectedSetting.java
@@ -22,7 +22,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.Immutable;
-import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
 
 /**
  * Specifies a setting that is being injected into Settings > Location > Location services.
@@ -65,32 +66,19 @@
      */
     public final String settingsActivity;
 
-    private InjectedSetting(String packageName, String className,
-            String title, int iconId, UserHandle userHandle, String settingsActivity) {
-        this.packageName = Preconditions.checkNotNull(packageName, "packageName");
-        this.className = Preconditions.checkNotNull(className, "className");
-        this.title = Preconditions.checkNotNull(title, "title");
-        this.iconId = iconId;
-        this.mUserHandle = userHandle;
-        this.settingsActivity = Preconditions.checkNotNull(settingsActivity);
-    }
-
     /**
-     * Returns a new instance, or null.
+     * The user restriction associated with this setting.
      */
-    public static InjectedSetting newInstance(String packageName, String className,
-            String title, int iconId, UserHandle userHandle, String settingsActivity) {
-        if (packageName == null || className == null ||
-                TextUtils.isEmpty(title) || TextUtils.isEmpty(settingsActivity)) {
-            if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
-                Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
-                        + packageName + ", class=" + className
-                        + ", title=" + title + ", settingsActivity=" + settingsActivity);
-            }
-            return null;
-        }
-        return new InjectedSetting(packageName, className, title, iconId, userHandle,
-                settingsActivity);
+    public final String userRestriction;
+
+    private InjectedSetting(Builder builder) {
+        this.packageName = builder.mPackageName;
+        this.className = builder.mClassName;
+        this.title = builder.mTitle;
+        this.iconId = builder.mIconId;
+        this.mUserHandle = builder.mUserHandle;
+        this.settingsActivity = builder.mSettingsActivity;
+        this.userRestriction = builder.mUserRestriction;
     }
 
     @Override
@@ -102,6 +90,7 @@
                 ", iconId=" + iconId +
                 ", userId=" + mUserHandle.getIdentifier() +
                 ", settingsActivity='" + settingsActivity + '\'' +
+                ", userRestriction='" + userRestriction +
                 '}';
     }
 
@@ -121,10 +110,13 @@
 
         InjectedSetting that = (InjectedSetting) o;
 
-        return packageName.equals(that.packageName) && className.equals(that.className)
-                && title.equals(that.title) && iconId == that.iconId
-                && mUserHandle.equals(that.mUserHandle)
-                && settingsActivity.equals(that.settingsActivity);
+        return Objects.equals(packageName, that.packageName)
+                && Objects.equals(className, that.className)
+                && Objects.equals(title, that.title)
+                && Objects.equals(iconId, that.iconId)
+                && Objects.equals(mUserHandle, that.mUserHandle)
+                && Objects.equals(settingsActivity, that.settingsActivity)
+                && Objects.equals(userRestriction, that.userRestriction);
     }
 
     @Override
@@ -133,8 +125,67 @@
         result = 31 * result + className.hashCode();
         result = 31 * result + title.hashCode();
         result = 31 * result + iconId;
-        result = 31 * result + mUserHandle.hashCode();
+        result = 31 * result + (mUserHandle == null ? 0 : mUserHandle.hashCode());
         result = 31 * result + settingsActivity.hashCode();
+        result = 31 * result + (userRestriction == null ? 0 : userRestriction.hashCode());
         return result;
     }
+
+    public static class Builder {
+        private String mPackageName;
+        private String mClassName;
+        private String mTitle;
+        private int mIconId;
+        private UserHandle mUserHandle;
+        private String mSettingsActivity;
+        private String mUserRestriction;
+
+        public Builder setPackageName(String packageName) {
+            mPackageName = packageName;
+            return this;
+        }
+
+        public Builder setClassName(String className) {
+            mClassName = className;
+            return this;
+        }
+
+        public Builder setTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        public Builder setIconId(int iconId) {
+            mIconId = iconId;
+            return this;
+        }
+
+        public Builder setUserHandle(UserHandle userHandle) {
+            mUserHandle = userHandle;
+            return this;
+        }
+
+        public Builder setSettingsActivity(String settingsActivity) {
+            mSettingsActivity = settingsActivity;
+            return this;
+        }
+
+        public Builder setUserRestriction(String userRestriction) {
+            mUserRestriction = userRestriction;
+            return this;
+        }
+
+        public InjectedSetting build() {
+            if (mPackageName == null || mClassName == null || TextUtils.isEmpty(mTitle)
+                    || TextUtils.isEmpty(mSettingsActivity)) {
+                if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
+                    Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
+                            + mPackageName + ", class=" + mClassName
+                            + ", title=" + mTitle + ", settingsActivity=" + mSettingsActivity);
+                }
+                return null;
+            }
+            return new InjectedSetting(this);
+        }
+    }
 }
diff --git a/src/com/android/settings/location/LocationServicePreferenceController.java b/src/com/android/settings/location/LocationServicePreferenceController.java
index 0a6a5c1..a1d2069 100644
--- a/src/com/android/settings/location/LocationServicePreferenceController.java
+++ b/src/com/android/settings/location/LocationServicePreferenceController.java
@@ -25,6 +25,7 @@
 import android.support.v7.preference.PreferenceScreen;
 import android.util.Log;
 
+import com.android.settings.widget.RestrictedAppPreference;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -88,7 +89,13 @@
     @Override
     public void updateState(Preference preference) {
         mCategoryLocationServices.removeAll();
-        LocationSettings.addPreferencesSorted(getLocationServices(), mCategoryLocationServices);
+        final List<Preference> prefs = getLocationServices();
+        for (Preference pref : prefs) {
+            if (pref instanceof RestrictedAppPreference) {
+                ((RestrictedAppPreference) pref).checkRestrictionAndSetDisabled();
+            }
+        }
+        LocationSettings.addPreferencesSorted(prefs, mCategoryLocationServices);
     }
 
     @Override
diff --git a/src/com/android/settings/location/SettingsInjector.java b/src/com/android/settings/location/SettingsInjector.java
index dfa5143..2c6a4f3 100644
--- a/src/com/android/settings/location/SettingsInjector.java
+++ b/src/com/android/settings/location/SettingsInjector.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -37,11 +38,14 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.v7.preference.Preference;
+import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.util.Xml;
 
 import com.android.settings.widget.AppPreference;
+import com.android.settings.widget.RestrictedAppPreference;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -215,12 +219,21 @@
                     sa.getResourceId(android.R.styleable.SettingInjectorService_icon, 0);
             final String settingsActivity =
                     sa.getString(android.R.styleable.SettingInjectorService_settingsActivity);
+            final String userRestriction = sa.getString(
+                    android.R.styleable.SettingInjectorService_userRestriction);
             if (Log.isLoggable(TAG, Log.DEBUG)) {
                 Log.d(TAG, "parsed title: " + title + ", iconId: " + iconId
                         + ", settingsActivity: " + settingsActivity);
             }
-            return InjectedSetting.newInstance(packageName, className,
-                    title, iconId, userHandle, settingsActivity);
+            return new InjectedSetting.Builder()
+                    .setPackageName(packageName)
+                    .setClassName(className)
+                    .setTitle(title)
+                    .setIconId(iconId)
+                    .setUserHandle(userHandle)
+                    .setSettingsActivity(settingsActivity)
+                    .setUserRestriction(userRestriction)
+                    .build();
         } finally {
             sa.recycle();
         }
@@ -290,15 +303,26 @@
      */
     private Preference addServiceSetting(Context prefContext, List<Preference> prefs,
             InjectedSetting info) {
-        PackageManager pm = mContext.getPackageManager();
-        Drawable appIcon = pm.getDrawable(info.packageName, info.iconId, null);
-        Drawable icon = pm.getUserBadgedIcon(appIcon, info.mUserHandle);
-        Preference pref = new AppPreference(prefContext);
+        final PackageManager pm = mContext.getPackageManager();
+        Drawable appIcon = null;
+        try {
+            final PackageItemInfo itemInfo = new PackageItemInfo();
+            itemInfo.icon = info.iconId;
+            itemInfo.packageName = info.packageName;
+            final ApplicationInfo appInfo = pm.getApplicationInfo(info.packageName,
+                PackageManager.GET_META_DATA);
+            appIcon = IconDrawableFactory.newInstance(mContext)
+                .getBadgedIcon(itemInfo, appInfo, info.mUserHandle.getIdentifier());
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Can't get ApplicationInfo for " + info.packageName, e);
+        }
+        Preference pref = TextUtils.isEmpty(info.userRestriction)
+                ? new AppPreference(prefContext)
+                : new RestrictedAppPreference(prefContext, info.userRestriction);
         pref.setTitle(info.title);
         pref.setSummary(null);
-        pref.setIcon(icon);
+        pref.setIcon(appIcon);
         pref.setOnPreferenceClickListener(new ServiceSettingClickedListener(info));
-
         prefs.add(pref);
         return pref;
     }
diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java
new file mode 100644
index 0000000..af6d8d1
--- /dev/null
+++ b/src/com/android/settings/widget/RestrictedAppPreference.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 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.widget;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedPreferenceHelper;
+
+/**
+ * {@link AppPreference} that implements user restriction utilities using
+ * {@link com.android.settingslib.RestrictedPreferenceHelper}.
+ * Used to show policy transparency on {@link AppPreference}.
+ */
+public class RestrictedAppPreference extends AppPreference {
+    private RestrictedPreferenceHelper mHelper;
+    private String userRestriction;
+
+    public RestrictedAppPreference(Context context) {
+        super(context);
+        initialize(null, null);
+    }
+
+    public RestrictedAppPreference(Context context, String userRestriction) {
+        super(context);
+        initialize(null, userRestriction);
+    }
+
+    public RestrictedAppPreference(Context context, AttributeSet attrs, String userRestriction) {
+        super(context, attrs);
+        initialize(attrs, userRestriction);
+    }
+
+    private void initialize(AttributeSet attrs, String userRestriction) {
+        setWidgetLayoutResource(R.layout.restricted_icon);
+        mHelper = new RestrictedPreferenceHelper(getContext(), this, attrs);
+        this.userRestriction = userRestriction;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        mHelper.onBindViewHolder(holder);
+        final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
+        if (restrictedIcon != null) {
+            restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    @Override
+    public void performClick() {
+        if (!mHelper.performClick()) {
+            super.performClick();
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (isDisabledByAdmin() && enabled) {
+            return;
+        }
+        super.setEnabled(enabled);
+    }
+
+    public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
+        if (mHelper.setDisabledByAdmin(admin)) {
+            notifyChanged();
+        }
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mHelper.isDisabledByAdmin();
+    }
+
+    public void useAdminDisabledSummary(boolean useSummary) {
+        mHelper.useAdminDisabledSummary(useSummary);
+    }
+
+    @Override
+    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+        mHelper.onAttachedToHierarchy();
+        super.onAttachedToHierarchy(preferenceManager);
+    }
+
+    public void checkRestrictionAndSetDisabled() {
+        if (TextUtils.isEmpty(userRestriction)) {
+            return;
+        }
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/location/InjectedSettingTest.java b/tests/robotests/src/com/android/settings/location/InjectedSettingTest.java
new file mode 100644
index 0000000..fb99958
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/location/InjectedSettingTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public final class InjectedSettingTest {
+
+    private static final String TEST_STRING = "test";
+
+    @Test
+    public void buildWithoutPackageName_ShouldReturnNull() {
+        assertThat(((new InjectedSetting.Builder())
+                .setClassName(TEST_STRING)
+                .setTitle(TEST_STRING)
+                .setSettingsActivity(TEST_STRING).build())).isNull();
+    }
+
+    private InjectedSetting getTestSetting() {
+        return new InjectedSetting.Builder()
+                .setPackageName(TEST_STRING)
+                .setClassName(TEST_STRING)
+                .setTitle(TEST_STRING)
+                .setSettingsActivity(TEST_STRING).build();
+    }
+
+    @Test
+    public void testEquals() {
+        InjectedSetting setting1 = getTestSetting();
+        InjectedSetting setting2 = getTestSetting();
+        assertThat(setting1).isEqualTo(setting2);
+    }
+
+    @Test
+    public void testHashCode() {
+        InjectedSetting setting = getTestSetting();
+        assertThat(setting.hashCode()).isEqualTo(1225314048);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/location/LocationServicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationServicePreferenceControllerTest.java
index 195e1b4..099ef7d 100644
--- a/tests/robotests/src/com/android/settings/location/LocationServicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/LocationServicePreferenceControllerTest.java
@@ -24,16 +24,25 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.admin.DevicePolicyManager;
 import android.arch.lifecycle.LifecycleOwner;
+import android.content.ComponentName;
 import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.widget.RestrictedAppPreference;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,11 +50,13 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
-
-import java.util.ArrayList;
-import java.util.List;
+import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+        shadows = {
+                ShadowUserManager.class
+        })
 public class LocationServicePreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -56,6 +67,8 @@
     private PreferenceScreen mScreen;
     @Mock
     private SettingsInjector mSettingsInjector;
+    @Mock
+    private DevicePolicyManager mDevicePolicyManager;
 
     private Context mContext;
     private LocationServicePreferenceController mController;
@@ -73,6 +86,9 @@
         final String key = mController.getPreferenceKey();
         when(mScreen.findPreference(key)).thenReturn(mCategory);
         when(mCategory.getKey()).thenReturn(key);
+        when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+                .thenReturn(mDevicePolicyManager);
+
     }
 
     @Test
@@ -132,4 +148,33 @@
 
         verify(mSettingsInjector).reloadStatusMessages();
     }
+
+    @Test
+    public void withUserRestriction_shouldDisableLocationAccuracy() {
+        final List<Preference> preferences = new ArrayList<>();
+        final RestrictedAppPreference pref = new RestrictedAppPreference(mContext,
+                UserManager.DISALLOW_CONFIG_LOCATION);
+        pref.setTitle("Location Accuracy");
+        preferences.add(pref);
+        doReturn(preferences).when(mSettingsInjector)
+                .getInjectedSettings(any(Context.class), anyInt());
+
+        int userId = UserHandle.myUserId();
+        List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
+        enforcingUsers.add(new UserManager.EnforcingUser(userId,
+                UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
+        ComponentName componentName = new ComponentName("test", "test");
+        // Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null.
+        ShadowUserManager.getShadow().setUserRestrictionSources(
+                UserManager.DISALLOW_CONFIG_LOCATION,
+                UserHandle.of(userId),
+                enforcingUsers);
+        when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(componentName);
+
+        mController.displayPreference(mScreen);
+        mController.updateState(mCategory);
+
+        assertThat(pref.isEnabled()).isFalse();
+        assertThat(pref.isDisabledByAdmin()).isTrue();
+    }
 }