Add hidden API to reset stable LOHS credentials

This API supposed to be used by Settings app or a projection receiver
app.

Bug: 192254706
CTS-Coverage-Bug: 206154096

Test: atest CarServiceUnitTests
Change-Id: Ib7cd46a4c67943f0677bca847002ddd84b67e361
Merged-In: Ib7cd46a4c67943f0677bca847002ddd84b67e361
diff --git a/car-lib/src/android/car/CarProjectionManager.java b/car-lib/src/android/car/CarProjectionManager.java
index 66590a5..f283a2c 100644
--- a/car-lib/src/android/car/CarProjectionManager.java
+++ b/car-lib/src/android/car/CarProjectionManager.java
@@ -703,6 +703,21 @@
     }
 
     /**
+     * Resets projection access point credentials if system was configured to persist local-only
+     * hotspot credentials.
+     *
+     * @hide
+     */
+    @RequiresPermission(Car.PERMISSION_CAR_PROJECTION)
+    public void resetProjectionAccessPointCredentials() {
+        try {
+            mService.resetProjectionAccessPointCredentials();
+        } catch (RemoteException e) {
+            handleRemoteExceptionFromCarService(e);
+        }
+    }
+
+    /**
      * Callback class for applications to receive updates about the LocalOnlyHotspot status.
      */
     public abstract static class ProjectionAccessPointCallback {
diff --git a/car-lib/src/android/car/ICarProjection.aidl b/car-lib/src/android/car/ICarProjection.aidl
index 5be9791..e82e83a 100644
--- a/car-lib/src/android/car/ICarProjection.aidl
+++ b/car-lib/src/android/car/ICarProjection.aidl
@@ -91,4 +91,10 @@
 
     /** Returns a list of available Wi-Fi channels */
     int[] getAvailableWifiChannels(int band) = 12;
+
+    /**
+     * Resets projection access point credentials if system was configured to persist local-only
+     * hotspot credentials.
+     */
+    void resetProjectionAccessPointCredentials() = 13;
 }
diff --git a/car-test-lib/src/android/car/testapi/FakeCarProjectionService.java b/car-test-lib/src/android/car/testapi/FakeCarProjectionService.java
index 275ae1c..49e8fd7 100644
--- a/car-test-lib/src/android/car/testapi/FakeCarProjectionService.java
+++ b/car-test-lib/src/android/car/testapi/FakeCarProjectionService.java
@@ -193,6 +193,10 @@
         return new int[] {2412 /* Channel 1 */, 5180 /* Channel 36 */};
     }
 
+    @Override
+    public void resetProjectionAccessPointCredentials() throws RemoteException {
+        // Nothing to do.
+    }
 
     @Override
     public void setProjectionOptions(ProjectionOptions projectionOptions) {
diff --git a/service/src/com/android/car/CarProjectionService.java b/service/src/com/android/car/CarProjectionService.java
index 4d2f164..acff9a2 100644
--- a/service/src/com/android/car/CarProjectionService.java
+++ b/service/src/com/android/car/CarProjectionService.java
@@ -651,6 +651,28 @@
         }
     }
 
+    @Override
+    public void resetProjectionAccessPointCredentials() {
+        ICarImpl.assertProjectionPermission(mContext);
+
+        if (!mStableLocalOnlyHotspotConfig) {
+            Slog.i(TAG, "Resetting local-only hotspot credentials ignored as credentials do"
+                    + " not persist.");
+            return;
+        }
+
+        Slog.i(TAG, "Clearing local-only hotspot credentials.");
+        getSharedPreferences()
+                .edit()
+                .clear()
+                .apply();
+
+        synchronized (mLock) {
+            mApConfiguration = null;
+        }
+    }
+
+    @GuardedBy("mLock")
     private void startLocalOnlyApLocked() {
         if (mLocalOnlyHotspotReservation != null) {
             Slog.i(TAG, "Local-only hotspot is already registered.");
diff --git a/tests/carservice_unit_test/src/com/android/car/CarProjectionServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarProjectionServiceTest.java
index 428811f..d063b08 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarProjectionServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarProjectionServiceTest.java
@@ -222,6 +222,23 @@
     }
 
     @Test
+    public void resetProjectionAccessPointCredentials() throws Exception {
+        mService.setStableLocalOnlyHotspotConfig(true);
+
+        WifiManager.LocalOnlyHotspotCallback callback = startProjectionLohs(false);
+
+        // Simulate framework saying AP successfully created.
+        callback.onStarted(mLohsReservation);
+        assertMessageSent(PROJECTION_AP_STARTED, AP_CONFIG);
+        mService.stopProjectionAccessPoint(mToken);
+        verify(mLohsReservation).close();
+
+        mService.resetProjectionAccessPointCredentials();
+
+        assertThat(mService.restoreApConfiguration().isPresent()).isFalse();
+    }
+
+    @Test
     public void updateProjectionStatus_subscribeAfterUpdate() throws Exception {
         final ProjectionStatus status = createProjectionStatus();
         mService.updateProjectionStatus(status, mToken);