Merge "Add code OWNERS to test OWNERS for internal widgets"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a5b91f..95fc7ec 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8705,6 +8705,7 @@
 
   public final class DeviceConfig {
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 2d40219..93c1c20 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -815,7 +815,7 @@
     }
 
     /**
-     * Create a new property with the the provided name and value in the provided namespace, or
+     * Create a new property with the provided name and value in the provided namespace, or
      * update the value of such a property if it already exists. The same name can exist in multiple
      * namespaces and might have different values in any or all namespaces.
      * <p>
@@ -865,6 +865,22 @@
     }
 
     /**
+     * Delete a property with the provided name and value in the provided namespace
+     *
+     * @param namespace   The namespace containing the property to create or update.
+     * @param name        The name of the property to create or update.
+     * @return True if the property was deleted or it did not exist in the first place.
+     * False if the storage implementation throws errors.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(WRITE_DEVICE_CONFIG)
+    public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+        return Settings.Config.deleteString(contentResolver, namespace, name);
+    }
+
+    /**
      * Reset properties to their default values by removing the underlying values.
      * <p>
      * The method accepts an optional namespace parameter. If provided, only properties set within
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4372f19..b7fcc03 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2829,6 +2829,7 @@
         // for the fast path of retrieving settings.
         private final String mCallGetCommand;
         private final String mCallSetCommand;
+        private final String mCallDeleteCommand;
         private final String mCallListCommand;
         private final String mCallSetAllCommand;
 
@@ -2840,17 +2841,19 @@
         private GenerationTracker mGenerationTracker;
 
         <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
-                String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
-            this(uri, getCommand, setCommand, null, null, providerHolder,
+                String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
+                Class<T> callerClass) {
+            this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
                     callerClass);
         }
 
         private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
-                String setCommand, String listCommand, String setAllCommand,
+                String setCommand, String deleteCommand, String listCommand, String setAllCommand,
                 ContentProviderHolder providerHolder, Class<T> callerClass) {
             mUri = uri;
             mCallGetCommand = getCommand;
             mCallSetCommand = setCommand;
+            mCallDeleteCommand = deleteCommand;
             mCallListCommand = listCommand;
             mCallSetAllCommand = setAllCommand;
             mProviderHolder = providerHolder;
@@ -2908,6 +2911,20 @@
             }
         }
 
+        public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
+            try {
+                Bundle arg = new Bundle();
+                arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+                IContentProvider cp = mProviderHolder.getProvider(cr);
+                cp.call(cr.getAttributionSource(),
+                        mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
+                return false;
+            }
+            return true;
+        }
+
         @UnsupportedAppUsage
         public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
             // Check if the target settings key is readable. Reject if the caller is not system and
@@ -3370,6 +3387,7 @@
                 CONTENT_URI,
                 CALL_METHOD_GET_SYSTEM,
                 CALL_METHOD_PUT_SYSTEM,
+                CALL_METHOD_DELETE_SYSTEM,
                 sProviderHolder,
                 System.class);
 
@@ -5690,6 +5708,7 @@
                 CONTENT_URI,
                 CALL_METHOD_GET_SECURE,
                 CALL_METHOD_PUT_SECURE,
+                CALL_METHOD_DELETE_SECURE,
                 sProviderHolder,
                 Secure.class);
 
@@ -15159,6 +15178,7 @@
                     CONTENT_URI,
                     CALL_METHOD_GET_GLOBAL,
                     CALL_METHOD_PUT_GLOBAL,
+                    CALL_METHOD_DELETE_GLOBAL,
                     sProviderHolder,
                     Global.class);
 
@@ -16391,6 +16411,7 @@
                 DeviceConfig.CONTENT_URI,
                 CALL_METHOD_GET_CONFIG,
                 CALL_METHOD_PUT_CONFIG,
+                CALL_METHOD_DELETE_CONFIG,
                 CALL_METHOD_LIST_CONFIG,
                 CALL_METHOD_SET_ALL_CONFIG,
                 sProviderHolder,
@@ -16500,6 +16521,26 @@
         }
 
         /**
+         * Delete a name/value pair from the database for the specified namespace.
+         *
+         * @param resolver to access the database with.
+         * @param namespace to delete the name/value pair from.
+         * @param name to delete.
+         * @return true if the value was deleted, false on database errors. If the name/value pair
+         * did not exist, return True.
+         *
+         * @see #resetToDefaults(ContentResolver, int, String)
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+        static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+                @NonNull String name) {
+            return sNameValueCache.deleteStringForUser(resolver,
+                    createCompositeName(namespace, name), resolver.getUserId());
+        }
+
+        /**
          * Reset the values to their defaults.
          * <p>
          * The method accepts an optional prefix parameter. If provided, only pairs with a name that
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd39cde..8d4f29a 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -827,4 +827,80 @@
         return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
     }
 
+    @Test
+    public void deleteProperty_nullNamespace() {
+        try {
+            DeviceConfig.deleteProperty(null, KEY);
+            Assert.fail("Null namespace should have resulted in an NPE.");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void deleteProperty_nullName() {
+        try {
+            DeviceConfig.deleteProperty(NAMESPACE, null);
+            Assert.fail("Null name should have resulted in an NPE.");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void deletePropertyString() {
+        final String value = "new_value";
+        final String default_value = "default";
+        DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyBoolean() {
+        final boolean value = true;
+        final boolean default_value = false;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyInt() {
+        final int value = 123;
+        final int default_value = 999;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyLong() {
+        final long value = 456789;
+        final long default_value = 123456;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyFloat() {
+        final float value = 456.789f;
+        final float default_value = 123.456f;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deleteProperty_empty() {
+        assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
+        assertThat(result).isNull();
+    }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index feee4a1..c03ed03 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -471,6 +471,9 @@
     <!-- Permission needed for CTS test - MatchContentFrameRateTest -->
     <uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" />
 
+    <!-- Permissions needed for manual testing telephony time zone detector behavior -->
+    <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
+
     <!-- Permissions needed for CTS test - TimeManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
     <uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" />
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a6da4a6..35217db 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2692,6 +2692,9 @@
                         return; // VPN has been shut down.
                     }
 
+                    // Clear mInterface to prevent Ikev2VpnRunner being cleared when
+                    // interfaceRemoved() is called.
+                    mInterface = null;
                     // Without MOBIKE, we have no way to seamlessly migrate. Close on old
                     // (non-default) network, and start the new one.
                     resetIkeState();