Define VcnStatusCallback#onVcnStatusChanged.

This CL defines VcnStatusCallback#onVcnStatusChanged for notifying
VcnStatusCallbacks when the status of their specified subscription group
changes. Specifically, status codes will be provided to notify callbacks
when a VCN is started, stopped, enters Safe Mode, and when there are no
matching VCNs.

Bug: 180659281
Test: atest FrameworksVcnTests
Change-Id: Icf686c3da6c5bdeeab88f60495d2ad438fa15692
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index 555e9b5..d91cef5 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -17,8 +17,9 @@
 package android.net.vcn;
 
 /** @hide */
-interface IVcnStatusCallback {
+oneway interface IVcnStatusCallback {
     void onEnteredSafeMode();
+    void onVcnStatusChanged(int statusCode);
     void onGatewayConnectionError(
             in int[] gatewayNetworkCapabilities,
             int errorCode,
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 729e68a..eb8c251 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -349,6 +349,56 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+        VCN_STATUS_CODE_NOT_CONFIGURED,
+        VCN_STATUS_CODE_INACTIVE,
+        VCN_STATUS_CODE_ACTIVE,
+        VCN_STATUS_CODE_SAFE_MODE
+    })
+    public @interface VcnStatusCode {}
+
+    /**
+     * Value indicating that the VCN for the subscription group is not configured, or that the
+     * callback is not privileged for the subscription group.
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0;
+
+    /**
+     * Value indicating that the VCN for the subscription group is inactive.
+     *
+     * <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the
+     * provisioning package is not privileged.
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_INACTIVE = 1;
+
+    /**
+     * Value indicating that the VCN for the subscription group is active.
+     *
+     * <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning
+     * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered
+     * active while it is connecting, fully connected, and disconnecting.
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_ACTIVE = 2;
+
+    /**
+     * Value indicating that the VCN for the subscription group is in Safe Mode.
+     *
+     * <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to
+     * establish a connection within a system-determined timeout (while underlying networks were
+     * available).
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_SAFE_MODE = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
         VCN_ERROR_CODE_INTERNAL_ERROR,
         VCN_ERROR_CODE_CONFIG_ERROR,
         VCN_ERROR_CODE_NETWORK_ERROR
@@ -403,8 +453,18 @@
          *
          * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
          * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
+         *
+         * @hide
          */
-        public abstract void onEnteredSafeMode();
+        public void onEnteredSafeMode() {}
+
+        /**
+         * Invoked when status of the VCN for this callback's subscription group changes.
+         *
+         * @param statusCode the code for the status change encountered by this {@link
+         *     VcnStatusCallback}'s subscription group.
+         */
+        public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
 
         /**
          * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
@@ -436,6 +496,11 @@
      * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier
      * privileges for the specified subscription at the time of invocation.
      *
+     * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the
+     * current status for the specified subscription group's VCN. If the registrant is not
+     * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be
+     * returned.
+     *
      * @param subscriptionGroup The subscription group to match for callbacks
      * @param executor The {@link Executor} to be used for invoking callbacks
      * @param callback The VcnStatusCallback to be registered
@@ -539,6 +604,12 @@
                     () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
         }
 
+        @Override
+        public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
+        }
+
         // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
         @Override
         public void onGatewayConnectionError(
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index ce8a898..66590c9 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -16,6 +16,8 @@
 
 package android.net.vcn;
 
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
+
 import static androidx.test.InstrumentationRegistry.getContext;
 
 import static org.junit.Assert.assertEquals;
@@ -204,6 +206,9 @@
         cbBinder.onEnteredSafeMode();
         verify(mMockStatusCallback).onEnteredSafeMode();
 
+        cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+        verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+
         cbBinder.onGatewayConnectionError(
                 UNDERLYING_NETWORK_CAPABILITIES,
                 VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,