[WIFI] Trigger WiFi recovery mode

Trigger WiFi recovery. The WiFi chip will get disabled and then
re-enabled. The state at the exit may not match the entry state.

Bug: 175084231
Test: atest com.android.server.wifi
Test: atest android.net.wifi
Test: adb shell cmd wifi trigger-recovery
Change-Id: Ie46bc697dcc003758c5df7c7c9d594e365086c7d
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index eba7443..e24f562 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -495,6 +495,7 @@
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_AIRPLANE_MODE) public void restartWifiSubsystem(@Nullable String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
     method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration restoreSoftApBackupData(@NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 866e913..e7b8475 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -300,4 +300,6 @@
     void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged, boolean enabled);
 
     boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged);
+
+    void restartWifiSubsystem(String reason);
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2b931a3..9fd70cb 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3041,6 +3041,37 @@
     }
 
     /**
+     * Restart the Wi-Fi subsystem.
+     *
+     * Restarts the Wi-Fi subsystem - effectively disabling it and re-enabling it. All existing
+     * Access Point (AP) associations are torn down, all Soft APs are disabled, Wi-Fi Direct and
+     * Wi-Fi Aware are disabled.
+     *
+     * The state of the system after restart is not guaranteed to match its state before the API is
+     * called - for instance the device may associate to a different Access Point (AP), and tethered
+     * hotspots may or may not be restored.
+     *
+     * @param reason If non-null, requests a bug report and attaches the reason string to it. A bug
+     *               report may still not be generated based on framework criteria - for instance,
+     *               build type or throttling. The WiFi subsystem is restarted whether or not a bug
+     *               report is requested or generated.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_AIRPLANE_MODE)
+    public void restartWifiSubsystem(@Nullable String reason) {
+        if (!SdkLevel.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            mService.restartWifiSubsystem(reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the Wi-Fi enabled state.
      * @return One of {@link #WIFI_STATE_DISABLED},
      *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},