Build IkeSessionParams from PersistableBundle without Context

This CL adds another Builder constructor and allows building
IkeSessionParams without providing a Context. This CL can
support making IkeSessionParams from PersistableBundle without
requring a Context.

There will be a followup CL to deprecate the Builder constructor
that requires a Context and to expose the new constructor.

Previously the Context was used to get a ConnectivityManager and
check the network state when building IkeSessionParams. However
it is unnecessary because an active network should not be required
until IKE client starts the packet exchanges. Also requiring a
Context to build IkeSessionParams makes it hard to store
IkeSessionParams in a Parcelable object like VcnConfig

Bug: 163604823
Test: FrameworksIkeTests, CtsIkeTestCases
Change-Id: If281f574c247b415f1b3bb38eb4264b9a68bd4be
diff --git a/src/java/android/net/ipsec/ike/IkeSessionParams.java b/src/java/android/net/ipsec/ike/IkeSessionParams.java
index 0cd23d6..84b630e 100644
--- a/src/java/android/net/ipsec/ike/IkeSessionParams.java
+++ b/src/java/android/net/ipsec/ike/IkeSessionParams.java
@@ -224,7 +224,11 @@
     private static final String IS_IKE_FRAGMENT_SUPPORTED_KEY = "mIsIkeFragmentationSupported";
 
     @NonNull private final String mServerHostname;
-    @NonNull private final Network mNetwork;
+
+    // @see #getNetwork for reasons of changing the annotation from @NonNull to @Nullable in Android
+    // S and why it is safe.
+    @Nullable private final Network mNetwork;
+
     @Nullable private final Network mCallerConfiguredNetwork;
 
     @NonNull private final IkeSaProposal[] mSaProposals;
@@ -319,11 +323,10 @@
      * @hide
      */
     @NonNull
-    public static IkeSessionParams fromPersistableBundle(
-            @NonNull PersistableBundle in, Context context) {
+    public static IkeSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
         Objects.requireNonNull(in, "PersistableBundle is null");
 
-        IkeSessionParams.Builder builder = new IkeSessionParams.Builder(context);
+        IkeSessionParams.Builder builder = new IkeSessionParams.Builder();
 
         builder.setServerHostname(in.getString(SERVER_HOST_NAME_KEY));
 
@@ -436,7 +439,16 @@
     /**
      * Retrieves the configured or default {@link Network}
      *
-     * <p>If caller did not set any Network, this method will return the default Network resolved in
+     * <p>This method is deprecated and its annotation has been changed from @NonNull to @Nullable
+     * since Android S. This method needs to be @Nullable because a new Builder constructor {@link
+     * Builder#Builder() was added in Android S, and by using the new constructor the return value
+     * of this method will be null. Also making this method @Nullable will not break the backwards
+     * compatibility because for any app that uses the deprecated constructor
+     * {@link Builder#Builder(Context)}, the return value of this method is still guaranteed to
+     * be non-null.
+     *
+     * <p>For a caller that used {@link Builder#Builder(Context)} and did not set any Network,
+     * this method will return the default Network resolved in
      * {@link IkeSessionParams.Builder#build()}. The return value of this method is only
      * informational because if MOBIKE is enabled, IKE Session may switch to a different default
      * Network.
@@ -450,6 +462,7 @@
     @Deprecated
     @SystemApi
     @NonNull
+    // TODO: b/163604823 Make it @Nullable
     public Network getNetwork() {
         return mNetwork;
     }
@@ -1116,7 +1129,12 @@
 
     /** This class can be used to incrementally construct a {@link IkeSessionParams}. */
     public static final class Builder {
-        @NonNull private final ConnectivityManager mConnectivityManager;
+        // This field has changed from @NonNull to @Nullable since Android S. It has to be @Nullable
+        // because the new constructor #Builder() will not need and will not able to get a
+        // ConnectivityManager instance anymore. Making it @Nullable does not break the backwards
+        // compatibility because if apps use the old constructor #Builder(Context), the Builder and
+        // the IkeSessionParams built from it will still work in the old way. @see #Builder(Context)
+        @Nullable private ConnectivityManager mConnectivityManager;
 
         @NonNull private final List<IkeSaProposal> mSaProposalList = new LinkedList<>();
         @NonNull private final List<IkeConfigAttribute> mConfigRequestList = new ArrayList<>();
@@ -1129,7 +1147,6 @@
 
         @NonNull private String mServerHostname;
         @Nullable private Network mCallerConfiguredNetwork;
-        @Nullable private Network mNetwork;
 
         @Nullable private IkeIdentification mLocalIdentification;
         @Nullable private IkeIdentification mRemoteIdentification;
@@ -1153,13 +1170,32 @@
         /**
          * Construct Builder
          *
+         * <p>This constructor is deprecated since Android S. Apps that use this constructor can
+         * still expect {@link #build()} to throw if no configured or default network was found. But
+         * apps that use {@link #Builder()} MUST NOT expect that behavior anymore.
+         *
+         * <p>This method is deprecated because it is unnecessary to try resolving a default network
+         * or to validate network state before IkeSession starts the packet exchanges. It will also
+         * makes it hard to store IkeSessionParams in a {@link android.os.Parcelable} object such as
+         * VcnConfig.
+         *
          * @param context a valid {@link Context} instance.
          */
+        // TODO: b/163604823 Deprecate this method
         public Builder(@NonNull Context context) {
             this((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
         }
 
+        /**
+         * Construct Builder
+         *
+         * @hide
+         */
+        public Builder() {}
+
         /** @hide */
+        // TODO: b/178389011 This constructor should be removed when #Builder(Context) can be safely
+        // removed. See #Builder(Context) for reasons.
         @VisibleForTesting
         public Builder(ConnectivityManager connectManager) {
             mConnectivityManager = connectManager;
@@ -1195,7 +1231,6 @@
             }
 
             mCallerConfiguredNetwork = network;
-            mNetwork = network;
             return this;
         }
 
@@ -1671,12 +1706,20 @@
                 throw new IllegalArgumentException("IKE SA proposal not found");
             }
 
-            Network network =
-                    mCallerConfiguredNetwork != null
-                            ? mCallerConfiguredNetwork
-                            : mConnectivityManager.getActiveNetwork();
-            if (network == null) {
-                throw new IllegalArgumentException("Network not found");
+            // TODO: b/178389011 This code block should be removed when
+            // IkeSessionParams#getNetwork() and #Builder(Context) can be safely removed. This block
+            // makes sure if the Builder is constructed with the deprecated constructor
+            // #Builder(Context), #build() still works in the same way and will throw exception when
+            // there is no configured or default network.
+            Network defaultOrConfiguredNetwork = null;
+            if (mConnectivityManager != null) {
+                defaultOrConfiguredNetwork =
+                        mCallerConfiguredNetwork != null
+                                ? mCallerConfiguredNetwork
+                                : mConnectivityManager.getActiveNetwork();
+                if (defaultOrConfiguredNetwork == null) {
+                    throw new IllegalArgumentException("Network not found");
+                }
             }
 
             if (mServerHostname == null
@@ -1710,7 +1753,7 @@
 
             return new IkeSessionParams(
                     mServerHostname,
-                    network,
+                    defaultOrConfiguredNetwork,
                     mCallerConfiguredNetwork,
                     mSaProposalList.toArray(new IkeSaProposal[0]),
                     mLocalIdentification,
diff --git a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java b/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java
index 7ad524a..8053f7a 100644
--- a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java
+++ b/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java
@@ -262,10 +262,17 @@
 
     @Test
     public void testIkeSessionParamsEncodeDecodeIsLossLess() throws Exception {
-        IkeSessionParams sessionParams = buildWithPskCommon(REMOTE_IPV4_HOST_ADDRESS).build();
+        IkeSessionParams sessionParams =
+                new IkeSessionParams.Builder()
+                        .setServerHostname(REMOTE_IPV4_HOST_ADDRESS)
+                        .addSaProposal(mIkeSaProposal)
+                        .setLocalIdentification(mLocalIdentification)
+                        .setRemoteIdentification(mRemoteIdentification)
+                        .setAuthPsk(PSK)
+                        .build();
 
         PersistableBundle bundle = sessionParams.toPersistableBundle();
-        IkeSessionParams result = IkeSessionParams.fromPersistableBundle(bundle, mMockContext);
+        IkeSessionParams result = IkeSessionParams.fromPersistableBundle(bundle);
 
         assertEquals(sessionParams, result);
     }