Merge "Use certificate build target" into main
diff --git a/framework/Android.bp b/framework/Android.bp
index 68e3323..6617369 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -135,6 +135,7 @@
         "framework-annotations-lib",
         "framework-connectivity.stubs.module_lib",
         "framework-location.stubs.module_lib",
+        "framework-tethering.stubs.module_lib",
     ],
     installable: false,
     visibility: [
@@ -154,31 +155,32 @@
     ],
 
     impl_only_libs: [
-        "framework-location.stubs.module_lib",
         "framework-connectivity.stubs.module_lib",
+        "framework-location.stubs.module_lib",
+        "framework-tethering.stubs.module_lib",
     ],
 
     public: {
         libs: [
-            "framework-location.stubs",
             "framework-connectivity.stubs",
+            "framework-location.stubs",
+            "framework-tethering.stubs",
         ],
     },
     system: {
         libs: [
-            "framework-location.stubs.system",
             "framework-connectivity.stubs.system",
+            "framework-location.stubs.system",
+            "framework-tethering.stubs.system",
         ],
     },
     module_lib: {
         libs: [
-            "framework-location.stubs.module_lib",
             "framework-connectivity.stubs.module_lib",
+            "framework-location.stubs.module_lib",
+            "framework-tethering.stubs.module_lib",
         ],
     },
-    api_srcs: [
-        ":wifi_javadoc_only_files",
-    ],
 
     jarjar_rules: ":wifi-jarjar-rules",
 
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 190a4ad..d99b5b8 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -436,6 +436,16 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApInfo> CREATOR;
   }
 
+  @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public final class SoftApState implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getFailureReason();
+    method @Nullable public String getIface();
+    method public int getState();
+    method @Nullable public android.net.TetheringManager.TetheringRequest getTetheringRequest();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApState> CREATOR;
+  }
+
   public final class WifiClient implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.net.MacAddress getMacAddress();
@@ -713,6 +723,7 @@
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startTetheredHotspot(@Nullable android.net.wifi.SoftApConfiguration);
+    method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startTetheredHotspotRequest(@NonNull android.net.TetheringManager.TetheringRequest);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopRestrictingAutoJoinToSubscriptionId();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
@@ -914,6 +925,7 @@
     method @Deprecated public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo);
     method public default void onInfoChanged(@NonNull java.util.List<android.net.wifi.SoftApInfo>);
     method public default void onStateChanged(int, int);
+    method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public default void onStateChanged(@NonNull android.net.wifi.SoftApState);
   }
 
   public static interface WifiManager.TrafficStateCallback {
diff --git a/framework/java/android/net/wifi/SoftApState.java b/framework/java/android/net/wifi/SoftApState.java
new file mode 100644
index 0000000..54dc45c
--- /dev/null
+++ b/framework/java/android/net/wifi/SoftApState.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 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 android.net.wifi;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.TetheringManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import java.util.Objects;
+
+/**
+ * A class representing the current state of SoftAp.
+ * @see WifiManager.SoftApCallback#onStateChanged(SoftApState)
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi("com.android.wifi.flags.android_v_wifi_api")
+public final class SoftApState implements Parcelable {
+
+    final int mState;
+    final int mFailureReason;
+    final TetheringManager.TetheringRequest mTetheringRequest;
+    final String mIface;
+
+    /**
+     * @hide
+     */
+    public SoftApState(int state, int failureReason,
+            @Nullable TetheringManager.TetheringRequest tetheringRequest,
+            @Nullable String iface) {
+        mState = state;
+        mFailureReason = failureReason;
+        mTetheringRequest = tetheringRequest;
+        mIface = iface;
+    }
+
+    private SoftApState(@NonNull Parcel in) {
+        mState = in.readInt();
+        mFailureReason = in.readInt();
+        if (SdkLevel.isAtLeastV()) {
+            // TetheringRequest is parcelable starting in V.
+            mTetheringRequest = in.readParcelable(
+                    TetheringManager.TetheringRequest.class.getClassLoader());
+        } else {
+            mTetheringRequest = null;
+        }
+        mIface = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mState);
+        dest.writeInt(mFailureReason);
+        if (SdkLevel.isAtLeastV()) {
+            // TetheringRequest is parcelable starting in V.
+            dest.writeParcelable(mTetheringRequest, flags);
+        }
+        dest.writeString(mIface);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<SoftApState> CREATOR = new Creator<SoftApState>() {
+        @Override
+        @NonNull
+        public SoftApState createFromParcel(Parcel in) {
+            return new SoftApState(in);
+        }
+
+        @Override
+        @NonNull
+        public SoftApState[] newArray(int size) {
+            return new SoftApState[size];
+        }
+    };
+
+    /**
+     * Get the AP state.
+     *
+     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+     *                {@link WifiManager#WIFI_AP_STATE_DISABLING},
+     *                {@link WifiManager#WIFI_AP_STATE_ENABLED},
+     *                {@link WifiManager#WIFI_AP_STATE_ENABLING},
+     *                {@link WifiManager#WIFI_AP_STATE_FAILED}
+     */
+    public @WifiManager.WifiApState int getState() {
+        return mState;
+    }
+
+    /**
+     * Get the failure reason if the state is {@link WifiManager#WIFI_AP_STATE_FAILED}.
+     *
+     * @return One of {@link WifiManager#SAP_START_FAILURE_GENERAL},
+     *                {@link WifiManager#SAP_START_FAILURE_NO_CHANNEL},
+     *                {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION},
+     *                {@link WifiManager#SAP_START_FAILURE_USER_REJECTED}
+     */
+    public @WifiManager.SapStartFailure int getFailureReason() {
+        return mFailureReason;
+    }
+
+    /**
+     * Gets the TetheringRequest of the Soft AP.
+     */
+    @Nullable
+    public TetheringManager.TetheringRequest getTetheringRequest() {
+        return mTetheringRequest;
+    }
+
+    /**
+     * Gets the iface of the Soft AP.
+     */
+    @Nullable
+    public String getIface() {
+        return mIface;
+    }
+
+    @Override
+    public String toString() {
+        return "SoftApState{"
+                + "mState=" + mState
+                + ", mFailureReason=" + mFailureReason
+                + ", mTetheringRequest=" + mTetheringRequest
+                + ", mIface='" + mIface + '\''
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SoftApState stateInfo)) return false;
+        return mState == stateInfo.mState && mFailureReason == stateInfo.mFailureReason
+                && Objects.equals(mTetheringRequest, stateInfo.mTetheringRequest)
+                && Objects.equals(mIface, stateInfo.mIface);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mState, mFailureReason, mTetheringRequest, mIface);
+    }
+}
diff --git a/framework/java/android/net/wifi/WifiManager.java b/framework/java/android/net/wifi/WifiManager.java
index 5a9a75c..f6c52ef 100644
--- a/framework/java/android/net/wifi/WifiManager.java
+++ b/framework/java/android/net/wifi/WifiManager.java
@@ -55,6 +55,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.NetworkStack;
+import android.net.TetheringManager;
 import android.net.Uri;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
@@ -5626,6 +5627,37 @@
         }
     }
 
+    /**
+     * Start Soft AP (hotspot) mode for tethering purposes with the specified TetheringRequest.
+     * Note that starting Soft AP mode may disable station mode operation if the device does not
+     * support concurrency.
+     * </p>
+     * This will fail and return {@code false} under the following circumstances:
+     * <ul>
+     *     <li>No interfaces are currently available for hotspot. See
+     *     {@link #reportCreateInterfaceImpact(int, boolean, Executor, BiConsumer)}. </li>
+     *     <li>TetheringRequest is misconfigured.</li>
+     *     <li>Wi-Fi tethering is disallowed for the current user.</li>
+     * </ul>
+     *
+     * @param request A valid TetheringRequest specifying the configuration of the SAP.
+     *
+     * @return {@code true} if the start operation was successfully posted, {@code false} otherwise.
+     *         If {@code true} was returned, then the success/failure of the request will be
+     *         conveyed afterwards via SoftApCallback.
+     *
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.android_v_wifi_api")
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    })
+    public boolean startTetheredHotspotRequest(@NonNull TetheringManager.TetheringRequest request) {
+        Log.e(TAG, "startTetheredHotspotRequest not supported before API 35");
+        return false;
+    }
 
     /**
      * Stop SoftAp mode.
@@ -6518,6 +6550,14 @@
         default void onStateChanged(@WifiApState int state, @SapStartFailure int failureReason) {}
 
         /**
+         * Called when soft AP state changes.
+         *
+         * @param state the new state.
+         */
+        @FlaggedApi("com.android.wifi.flags.android_v_wifi_api")
+        default void onStateChanged(@NonNull SoftApState state) {}
+
+        /**
          * Called when the connected clients to soft AP changes.
          *
          * @param clients the currently connected clients