Add APIs to manage profiles in the VpnBlobStore

Add hidden apis for settings to store profiles directly into the
VpnBlobStore.

Bug: 307903113
Test: m
Change-Id: I148ceedcb2ae235d89a3dd5ffba4a8ef26b5d104
diff --git a/core/java/android/net/IVpnManager.aidl b/core/java/android/net/IVpnManager.aidl
index f302378..5149967 100644
--- a/core/java/android/net/IVpnManager.aidl
+++ b/core/java/android/net/IVpnManager.aidl
@@ -60,6 +60,12 @@
     LegacyVpnInfo getLegacyVpnInfo(int userId);
     boolean updateLockdownVpn();
 
+    /** Profile store APIs */
+    byte[] getFromVpnProfileStore(String name);
+    boolean putIntoVpnProfileStore(String name, in byte[] blob);
+    boolean removeFromVpnProfileStore(String name);
+    String[] listFromVpnProfileStore(String prefix);
+
     /** General system APIs */
     VpnConfig getVpnConfig(int userId);
     void factoryReset();
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index ff47f3f..c50bc56 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -717,4 +717,81 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get the vpn profile owned by the calling uid with the given name from the vpn database.
+     *
+     * <p>Note this method should not be used for platform VPN profiles. </p>
+     *
+     * @param name The name of the profile to retrieve.
+     * @return the unstructured blob for the matching vpn profile.
+     * Returns null if no profile with a matching name was found.
+     * @hide
+     */
+    @Nullable
+    public byte[] getFromVpnProfileStore(@NonNull String name) {
+        try {
+            return mService.getFromVpnProfileStore(name);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Put the given vpn profile owned by the calling uid with the given name into the vpn database.
+     * Existing profiles with the same name will be replaced.
+     *
+     * <p>Note this method should not be used for platform VPN profiles.
+     * To update a platform VPN, use provisionVpnProfile() instead. </p>
+     *
+     * @param name The name of the profile to put.
+     * @param blob The profile.
+     * @return true if the profile was successfully added. False otherwise.
+     * @hide
+     */
+    public boolean putIntoVpnProfileStore(@NonNull String name, @NonNull byte[] blob) {
+        try {
+            return mService.putIntoVpnProfileStore(name, blob);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes the vpn profile owned by the calling uid with the given name from the vpn database.
+     *
+     * <p>Note this method should not be used for platform VPN profiles.
+     * To remove a platform VPN, use deleteVpnProfile() instead.</p>
+     *
+     * @param name The name of the profile to be removed.
+     * @return true if a profile was removed. False if no profile with a matching name was found.
+     * @hide
+     */
+    public boolean removeFromVpnProfileStore(@NonNull String name) {
+        try {
+            return mService.removeFromVpnProfileStore(name);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a list of the name suffixes of the vpn profiles owned by the calling uid in the vpn
+     * database matching the given prefix, sorted in ascending order.
+     *
+     * <p>Note this method should not be used for platform VPN profiles. </p>
+     *
+     * @param prefix The prefix to match.
+     * @return an array of strings representing the name suffixes stored in the profile database
+     * matching the given prefix. The return value may be empty but never null.
+     * @hide
+     */
+    @NonNull
+    public String[] listFromVpnProfileStore(@NonNull String prefix) {
+        try {
+            return mService.listFromVpnProfileStore(prefix);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 1d1e2d9..626fa70 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -1007,6 +1007,71 @@
         }
     }
 
+    /**
+     * Get the vpn profile owned by the calling uid with the given name from the vpn database.
+     *
+     * <p>Note this method should not be used for platform VPN profiles. </p>
+     *
+     * @param name The name of the profile to retrieve.
+     * @return the unstructured blob for the matching vpn profile.
+     * Returns null if no profile with a matching name was found.
+     * @hide
+     */
+    @Override
+    @Nullable
+    public byte[] getFromVpnProfileStore(@NonNull String name) {
+        return mVpnProfileStore.get(name);
+    }
+
+    /**
+     * Put the given vpn profile owned by the calling uid with the given name into the vpn database.
+     * Existing profiles with the same name will be replaced.
+     *
+     * <p>Note this method should not be used for platform VPN profiles.
+     * To update a platform VPN, use provisionVpnProfile() instead. </p>
+     *
+     * @param name The name of the profile to put.
+     * @param blob The profile.
+     * @return true if the profile was successfully added. False otherwise.
+     * @hide
+     */
+    @Override
+    public boolean putIntoVpnProfileStore(@NonNull String name, @NonNull byte[] blob) {
+        return mVpnProfileStore.put(name, blob);
+    }
+
+    /**
+     * Removes the vpn profile owned by the calling uid with the given name from the vpn database.
+     *
+     * <p>Note this method should not be used for platform VPN profiles.
+     * To remove a platform VPN, use deleteVpnProfile() instead.</p>
+     *
+     * @param name The name of the profile to be removed.
+     * @return true if a profile was removed. False if no profile with a matching name was found.
+     * @hide
+     */
+    @Override
+    public boolean removeFromVpnProfileStore(@NonNull String name) {
+        return mVpnProfileStore.remove(name);
+    }
+
+    /**
+     * Returns a list of the name suffixes of the vpn profiles owned by the calling uid in the vpn
+     * database matching the given prefix, sorted in ascending order.
+     *
+     * <p>Note this method should not be used for platform VPN profiles. </p>
+     *
+     * @param prefix The prefix to match.
+     * @return an array of strings representing the name suffixes stored in the profile database
+     * matching the given prefix. The return value may be empty but never null.
+     * @hide
+     */
+    @Override
+    @NonNull
+    public String[] listFromVpnProfileStore(@NonNull String prefix) {
+        return mVpnProfileStore.list(prefix);
+    }
+
     private void ensureRunningOnHandlerThread() {
         if (mHandler.getLooper().getThread() != Thread.currentThread()) {
             throw new IllegalStateException(
diff --git a/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java b/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java
index ecc70e3..8495de4 100644
--- a/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java
+++ b/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java
@@ -397,4 +397,35 @@
         // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
         assertNull(mService.getVpnLockdownAllowlist(SECONDARY_USER.id));
     }
+
+    @Test
+    public void testGetFromVpnProfileStore() {
+        final String name = Credentials.VPN + TEST_VPN_PKG;
+        mService.getFromVpnProfileStore(name);
+        verify(mVpnProfileStore).get(name);
+    }
+
+    @Test
+    public void testPutIntoVpnProfileStore() {
+        final String name = Credentials.VPN + TEST_VPN_PKG;
+        final VpnProfile vpnProfile = new VpnProfile(TEST_VPN_PKG);
+        final byte[] encodedProfile = vpnProfile.encode();
+
+        mService.putIntoVpnProfileStore(name, encodedProfile);
+        verify(mVpnProfileStore).put(name, encodedProfile);
+    }
+
+    @Test
+    public void testRemoveFromVpnProfileStore() {
+        final String name = Credentials.VPN + TEST_VPN_PKG;
+        mService.removeFromVpnProfileStore(name);
+        verify(mVpnProfileStore).remove(name);
+    }
+
+    @Test
+    public void testListFromVpnProfileStore() {
+        final String name = Credentials.VPN + TEST_VPN_PKG;
+        mService.listFromVpnProfileStore(name);
+        verify(mVpnProfileStore).list(name);
+    }
 }