Merge "Prevent HTML Injection on the Device Admin request screen" into sc-dev
diff --git a/res/layout/accessibility_edit_magnification_shortcut.xml b/res/layout/accessibility_edit_magnification_shortcut.xml
index bed1255..5f885b9 100644
--- a/res/layout/accessibility_edit_magnification_shortcut.xml
+++ b/res/layout/accessibility_edit_magnification_shortcut.xml
@@ -25,13 +25,21 @@
         android:orientation="vertical"
         android:padding="24dp">
 
+        <ImageView
+            android:id="@+id/image"
+            android:layout_width="@dimen/accessibility_imageview_size"
+            android:layout_height="@dimen/accessibility_imageview_size"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="@dimen/accessibility_textview_layout_margin_bottom"
+            android:scaleType="fitCenter"/>
+
         <TextView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/accessibility_magnification_switch_shortcut_message"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
-            android:layout_marginBottom="24dp"/>
+            android:layout_marginBottom="@dimen/accessibility_textview_layout_margin_bottom"/>
 
         <LinearLayout
             android:layout_width="match_parent"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index aca3322..e56b2c8 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -292,6 +292,8 @@
     <dimen name="accessibility_layout_margin_start_end">16dp</dimen>
     <dimen name="accessibility_button_preference_padding_top_bottom">18dp</dimen>
     <dimen name="accessibility_imageview_size">176dp</dimen>
+    <!-- The margin between two Textviews-->
+    <dimen name="accessibility_textview_layout_margin_bottom">24dp</dimen>
 
     <!-- Restricted icon in switch bar -->
     <dimen name="restricted_icon_margin_end">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2a05a4d..c1c165e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6565,8 +6565,10 @@
     <string name="vpn_settings_title">VPN</string>
     <!-- Title of preference to enter the VPN settings activity [CHAR LIMIT=30] -->
     <string name="vpn_settings_insecure_single">Not secure</string>
-    <!-- Title of preference to enter the VPN settings activity [CHAR LIMIT=30] -->
-    <string name="vpn_settings_insecure_multiple"><xliff:g id="vpn_count" example="1">%d</xliff:g> not secure</string>
+    <!-- Title of preference to enter the VPN settings activity, if there are multiple vpns but only 1 insecure vpn [CHAR LIMIT=60] -->
+    <string name="vpn_settings_single_insecure_multiple_total"><xliff:g id="vpn_count" example="1">%d</xliff:g> not secure</string>
+    <!-- Title of preference to enter the VPN settings activity, if there are multiple vpns and multiple insecure vpns [CHAR LIMIT=60] -->
+    <string name="vpn_settings_multiple_insecure_multiple_total"><xliff:g id="vpn_count" example="1">%d</xliff:g> not secure</string>
 
     <!-- Title of Adaptive connectivity. Adaptive connectivity is a feature which automatically manages network connections for better battery life and performance. [CHAR LIMIT=60] -->
     <string name="adaptive_connectivity_title">Adaptive connectivity</string>
@@ -13135,13 +13137,13 @@
     <!-- Brightness category name in Display Settings [CHAR LIMIT=none] -->
     <string name="category_name_brightness">Brightness</string>
     <!-- Lock screen category name in Display Settings [CHAR LIMIT=none] -->
-    <string name="category_name_lock_display">Lock Display</string>
+    <string name="category_name_lock_display">Lock display</string>
     <!-- Appearance category name in Display Settings [CHAR LIMIT=none] -->
     <string name="category_name_appearance">Appearance</string>
     <!-- Color category name in Display Settings [CHAR LIMIT=none] -->
     <string name="category_name_color">Color</string>
     <!-- Name of Other display controls category in Display Settings [CHAR LIMIT=none] -->
-    <string name="category_name_display_controls">Other Display Controls</string>
+    <string name="category_name_display_controls">Other display controls</string>
     <!-- Others category name [CHAR LIMIT=none] -->
     <string name="category_name_others">Others</string>
     <!-- General category name [CHAR LIMIT=none] -->
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
index 5d2a3fa..da3b479 100644
--- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -168,6 +168,7 @@
                 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
                 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
     }
+
     private static void setEditShortcutButtonsListener(AlertDialog dialog,
             View.OnClickListener listener) {
         final View contentView = dialog.findViewById(R.id.container_layout);
@@ -215,6 +216,8 @@
             case DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT:
                 contentView = inflater.inflate(
                         R.layout.accessibility_edit_magnification_shortcut, null);
+                final ImageView image = contentView.findViewById(R.id.image);
+                image.setImageResource(retrieveSoftwareShortcutImageResId(context));
                 break;
             default:
                 throw new IllegalArgumentException();
@@ -247,7 +250,7 @@
         final int lineHeight = summary.getLineHeight();
 
         setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight),
-                retrieveImageResId(context));
+                retrieveSoftwareShortcutImageResId(context));
     }
 
     private static void initHardwareShortcut(Context context, View view) {
@@ -286,7 +289,7 @@
                 ? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight);
     }
 
-    private static int retrieveImageResId(Context context) {
+    private static int retrieveSoftwareShortcutImageResId(Context context) {
         return AccessibilityUtil.isFloatingMenuEnabled(context)
                 ? R.drawable.accessibility_shortcut_type_software_floating
                 : R.drawable.accessibility_shortcut_type_software;
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index e502d65..cc14586 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -85,6 +85,7 @@
     private static final int RIGHT_DEVICE_ID = 2;
     private static final int CASE_DEVICE_ID = 3;
     private static final int MAIN_DEVICE_ID = 4;
+    private static final float HALF_ALPHA = 0.5f;
 
     @VisibleForTesting
     LayoutPreference mLayoutPreference;
@@ -430,10 +431,12 @@
     @VisibleForTesting
     void updateIcon(ImageView imageView, String iconUri) {
         if (mIconCache.containsKey(iconUri)) {
+            imageView.setAlpha(1f);
             imageView.setImageBitmap(mIconCache.get(iconUri));
             return;
         }
 
+        imageView.setAlpha(HALF_ALPHA);
         ThreadUtils.postOnBackgroundThread(() -> {
             final Uri uri = Uri.parse(iconUri);
             try {
@@ -444,6 +447,7 @@
                         mContext.getContentResolver(), uri);
                 ThreadUtils.postOnMainThread(() -> {
                     mIconCache.put(iconUri, bitmap);
+                    imageView.setAlpha(1f);
                     imageView.setImageBitmap(bitmap);
                 });
             } catch (IOException e) {
diff --git a/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java b/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
index 48a3e95..f5c30f5 100644
--- a/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
+++ b/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
@@ -17,7 +17,7 @@
 package com.android.settings.development;
 
 import android.content.Context;
-import android.os.SystemProperties;
+import android.provider.DeviceConfig;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -35,9 +35,9 @@
 
     private static final String BLUETOOTH_GABELDORSCHE_KEY =
             "bluetooth_gabeldorsche_enable";
+
     @VisibleForTesting
-    static final String BLUETOOTH_GABELDORSCHE_PROPERTY =
-            "bluetooth.gd.enabled";
+    static final String CURRENT_GD_FLAG = "INIT_gd_scanning";
 
     public BluetoothGabeldorschePreferenceController(Context context) {
         super(context);
@@ -51,22 +51,23 @@
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         final boolean isEnabled = (Boolean) newValue;
-        SystemProperties.set(BLUETOOTH_GABELDORSCHE_PROPERTY,
-                isEnabled ? "true" : "false");
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG, isEnabled ? "true" : "false", false /* makeDefault */);
         return true;
     }
 
     @Override
     public void updateState(Preference preference) {
-        final boolean isEnabled = SystemProperties.getBoolean(
-                BLUETOOTH_GABELDORSCHE_PROPERTY, false /* default */);
+        final boolean isEnabled = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_BLUETOOTH, CURRENT_GD_FLAG, false /* default */);
         ((SwitchPreference) mPreference).setChecked(isEnabled);
     }
 
     @Override
     protected void onDeveloperOptionsSwitchDisabled() {
         super.onDeveloperOptionsSwitchDisabled();
-        SystemProperties.set(BLUETOOTH_GABELDORSCHE_PROPERTY, "false");
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG, null, false /* makeDefault */);
         ((SwitchPreference) mPreference).setChecked(false);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java
index ed52f48..309551b 100644
--- a/src/com/android/settings/fuelgauge/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryEntry.java
@@ -55,13 +55,20 @@
 
     public static final class NameAndIcon {
         public final String name;
+        public final String packageName;
         public final Drawable icon;
         public final int iconId;
 
         public NameAndIcon(String name, Drawable icon, int iconId) {
+            this(name, /*packageName=*/ null, icon, iconId);
+        }
+
+        public NameAndIcon(
+                String name, String packageName, Drawable icon, int iconId) {
             this.name = name;
             this.icon = icon;
             this.iconId = iconId;
+            this.packageName = packageName;
         }
     }
 
@@ -102,7 +109,15 @@
                     }
                     be = sRequestQueue.remove(0);
                 }
-                be.loadNameAndIcon();
+                final NameAndIcon nameAndIcon =
+                    BatteryEntry.loadNameAndIcon(
+                        be.mContext, be.getUid(), sHandler, be, be.mDefaultPackageName);
+                if (nameAndIcon != null) {
+                    be.icon = getNonNull(be.icon, nameAndIcon.icon);
+                    be.name = getNonNull(be.name, nameAndIcon.name);
+                    be.mDefaultPackageName = getNonNull(
+                        be.mDefaultPackageName, nameAndIcon.packageName);
+                }
             }
         }
     }
@@ -262,27 +277,33 @@
     /**
      * Loads the app label and icon image and stores into the cache.
      */
-    public void loadNameAndIcon() {
+    public static NameAndIcon loadNameAndIcon(
+            Context context,
+            int uid,
+            Handler handler,
+            BatteryEntry batteryEntry,
+            String defaultPackageName) {
+        String name = null;
+        Drawable icon = null;
         // Bail out if the current sipper is not an App sipper.
-        final int uid = getUid();
         if (uid == 0 || uid == Process.INVALID_UID) {
-            return;
+            return null;
         }
 
-        final PackageManager pm = mContext.getPackageManager();
+        final PackageManager pm = context.getPackageManager();
         final String[] packages;
         if (uid == Process.SYSTEM_UID) {
-            packages = new String[]{PACKAGE_SYSTEM};
+            packages = new String[] {PACKAGE_SYSTEM};
         } else {
             packages = pm.getPackagesForUid(uid);
         }
 
         if (packages != null) {
-            String[] packageLabels = new String[packages.length];
+            final String[] packageLabels = new String[packages.length];
             System.arraycopy(packages, 0, packageLabels, 0, packages.length);
 
             // Convert package names to user-facing labels where possible
-            IPackageManager ipm = AppGlobals.getPackageManager();
+            final IPackageManager ipm = AppGlobals.getPackageManager();
             final int userId = UserHandle.getUserId(uid);
             for (int i = 0; i < packageLabels.length; i++) {
                 try {
@@ -293,12 +314,12 @@
                                 + packageLabels[i] + ", user " + userId);
                         continue;
                     }
-                    CharSequence label = ai.loadLabel(pm);
+                    final CharSequence label = ai.loadLabel(pm);
                     if (label != null) {
                         packageLabels[i] = label.toString();
                     }
                     if (ai.icon != 0) {
-                        mDefaultPackageName = packages[i];
+                        defaultPackageName = packages[i];
                         icon = ai.loadIcon(pm);
                         break;
                     }
@@ -326,7 +347,7 @@
                             if (nm != null) {
                                 name = nm.toString();
                                 if (pi.applicationInfo.icon != 0) {
-                                    mDefaultPackageName = pkgName;
+                                    defaultPackageName = pkgName;
                                     icon = pi.applicationInfo.loadIcon(pm);
                                 }
                                 break;
@@ -352,12 +373,13 @@
         UidToDetail utd = new UidToDetail();
         utd.name = name;
         utd.icon = icon;
-        utd.packageName = mDefaultPackageName;
+        utd.packageName = defaultPackageName;
 
         sUidCache.put(uidString, utd);
-        if (sHandler != null) {
-            sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
+        if (handler != null) {
+            handler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, batteryEntry));
         }
+        return new NameAndIcon(name, defaultPackageName, icon, /*iconId=*/ 0);
     }
 
     /**
@@ -557,4 +579,8 @@
         }
         return new NameAndIcon(name, null /* icon */, iconId);
     }
+
+    private static <T> T getNonNull(T originalObj, T newObj) {
+        return newObj != null ? newObj : originalObj;
+    }
 }
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index 0bec7ac..4944f43 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -169,9 +169,14 @@
                         - connectedLegacyVpnCount;
                 if (vpnCount == 1) {
                     summary = mContext.getString(R.string.vpn_settings_insecure_single);
+                } else if (insecureVpnCount == 1) {
+                    summary = mContext.getString(
+                            R.string.vpn_settings_single_insecure_multiple_total,
+                            insecureVpnCount);
                 } else {
                     summary = mContext.getString(
-                            R.string.vpn_settings_insecure_multiple, insecureVpnCount);
+                            R.string.vpn_settings_multiple_insecure_multiple_total,
+                            insecureVpnCount);
                 }
             }
         }
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java
new file mode 100644
index 0000000..472a960
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 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.BluetoothGabeldorschePreferenceController
+        .CURRENT_GD_FLAG;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.DeviceConfig;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothGabeldorschePreferenceControllerTest {
+
+    @Mock
+    private SwitchPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private BluetoothGabeldorschePreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        Context context = RuntimeEnvironment.application;
+        mController = new BluetoothGabeldorschePreferenceController(context);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+                .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onPreferenceChanged_settingEnabled_shouldTurnOnBluetoothGabeldorsche() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        boolean enabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG, false /* defaultValue */);
+
+        assertThat(enabled).isTrue();
+    }
+
+    @Test
+    public void onPreferenceChanged_settingDisabled_shouldTurnOffBluetoothGabeldorsche() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        boolean enabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG, false /* defaultValue */);
+
+        assertThat(enabled).isFalse();
+    }
+
+    @Test
+    public void updateState_settingEnabled_preferenceShouldBeChecked() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG, "true", false /* makeDefault */);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG, "false", false /* makeDefault */);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void onDeveloperOptionsDisabled_shouldDisablePreference() {
+        mController.onDeveloperOptionsDisabled();
+
+        String configStr = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
+                CURRENT_GD_FLAG);
+
+        assertThat(configStr).isNull();
+        verify(mPreference).setEnabled(false);
+        verify(mPreference).setChecked(false);
+    }
+}