OSDN Git Service

[MS02] Write the skeleton for the IP memory store.
authorChalard Jean <jchalard@google.com>
Fri, 7 Dec 2018 14:09:02 +0000 (23:09 +0900)
committerChalard Jean <jchalard@google.com>
Fri, 11 Jan 2019 06:01:00 +0000 (15:01 +0900)
This implements a minimal contract for the IP Memory Store
feature.

Test: Created new tests for parcelable code. For the rest, created
      empty tests, but there is no logic, it's boilerplate day
Bug: 116512211
Change-Id: I15dc6275b370d671385ddfdb058a8b8d39952d6e

19 files changed:
Android.bp
core/java/android/net/IIpMemoryStore.aidl
core/java/android/net/IpMemoryStore.java
core/java/android/net/ipmemorystore/Blob.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/IOnStatusListener.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/NetworkAttributes.java [new file with mode: 0644]
core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/SameL3NetworkResponse.java [new file with mode: 0644]
core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl [new file with mode: 0644]
core/java/android/net/ipmemorystore/Status.java [new file with mode: 0644]
core/java/android/net/ipmemorystore/StatusParcelable.aidl [new file with mode: 0644]
services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
tests/net/java/android/net/IpMemoryStoreTest.java
tests/net/java/android/net/ipmemorystore/ParcelableTests.java [new file with mode: 0644]
tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java

index dad71a2..8d0e1d3 100644 (file)
@@ -716,16 +716,6 @@ java_defaults {
 
 }
 
-// AIDL interfaces between the core system and the networking mainline module.
-aidl_interface {
-    name: "networkstack-aidl-interfaces",
-    local_include_dir: "core/java",
-    srcs: [
-        "core/java/android/net/IIpMemoryStore.aidl",
-    ],
-    api_dir: "aidl/networkstack",
-}
-
 filegroup {
     name: "libincident_aidl",
     srcs: [
@@ -837,12 +827,14 @@ aidl_interface {
     srcs: [
         "core/java/android/net/INetworkMonitor.aidl",
         "core/java/android/net/INetworkMonitorCallbacks.aidl",
+        "core/java/android/net/IIpMemoryStore.aidl",
         "core/java/android/net/INetworkStackConnector.aidl",
         "core/java/android/net/INetworkStackStatusCallback.aidl",
         "core/java/android/net/PrivateDnsConfigParcel.aidl",
         "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
         "core/java/android/net/dhcp/IDhcpServer.aidl",
         "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
+        "core/java/android/net/ipmemorystore/**/*.aidl",
     ],
     api_dir: "aidl/networkstack",
 }
index 8662bd1..6f88dec 100644 (file)
 
 package android.net;
 
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+
 /** {@hide} */
-interface IIpMemoryStore {
+oneway interface IIpMemoryStore {
+    /**
+     * Store network attributes for a given L2 key.
+     * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
+     * calling findL2Key with the attributes and storing in the returned value.
+     *
+     * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+     *              key and only care about grouping can pass a unique ID here like the ones
+     *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+     *              relevance of such a network will lead to it being evicted soon if it's not
+     *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+     * @param attributes The attributes for this network.
+     * @param listener A listener that will be invoked to inform of the completion of this call,
+     *                 or null if the client is not interested in learning about success/failure.
+     * @return (through the listener) The L2 key. This is useful if the L2 key was not specified.
+     *         If the call failed, the L2 key will be null.
+     */
+    void storeNetworkAttributes(String l2Key, in NetworkAttributesParcelable attributes,
+            IOnStatusListener listener);
+
+    /**
+     * Store a binary blob associated with an L2 key and a name.
+     *
+     * @param l2Key The L2 key for this network.
+     * @param clientId The ID of the client.
+     * @param name The name of this data.
+     * @param data The data to store.
+     * @param listener A listener to inform of the completion of this call, or null if the client
+     *        is not interested in learning about success/failure.
+     * @return (through the listener) A status to indicate success or failure.
+     */
+    void storeBlob(String l2Key, String clientId, String name, in Blob data,
+            IOnStatusListener listener);
+
+    /**
+     * Returns the best L2 key associated with the attributes.
+     *
+     * This will find a record that would be in the same group as the passed attributes. This is
+     * useful to choose the key for storing a sample or private data when the L2 key is not known.
+     * If multiple records are group-close to these attributes, the closest match is returned.
+     * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+     * order) L2 key is returned.
+     * If no record matches these attributes, null is returned.
+     *
+     * @param attributes The attributes of the network to find.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) The L2 key if one matched, or null.
+     */
+    void findL2Key(in NetworkAttributesParcelable attributes, IOnL2KeyResponseListener listener);
+
+    /**
+     * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+     * to the same L3 network. Group-closeness is used to determine this.
+     *
+     * @param l2Key1 The key for the first network.
+     * @param l2Key2 The key for the second network.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) A SameL3NetworkResponse containing the answer and confidence.
+     */
+    void isSameNetwork(String l2Key1, String l2Key2, IOnSameNetworkResponseListener listener);
+
+    /**
+     * Retrieve the network attributes for a key.
+     * If no record is present for this key, this will return null attributes.
+     *
+     * @param l2Key The key of the network to query.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) The network attributes and the L2 key associated with
+     *         the query.
+     */
+    void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrieved listener);
+
     /**
-     * Returns the version of the memory store.
-     * This is just a fake method returning 1 to have some method in there
-     * without adding any actual complexity to review.
+     * Retrieve previously stored private data.
+     * If no data was stored for this L2 key and name this will return null.
+     *
+     * @param l2Key The L2 key.
+     * @param clientId The id of the client that stored this data.
+     * @param name The name of the data.
+     * @param listener The listener that will be invoked to return the answer.
+     * @return (through the listener) The private data (or null), with the L2 key
+     *         and the name of the data associated with the query.
      */
-    int version();
+    void retrieveBlob(String l2Key, String clientId, String name,
+            IOnBlobRetrievedListener listener);
 }
index 177992e..b35f097 100644 (file)
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributes;
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
@@ -39,13 +47,126 @@ public class IpMemoryStore {
     }
 
     /**
-     * Returns the version of the memory store
-     * @hide
-     **/
-    // TODO : remove this
-    public int version() {
+     * Store network attributes for a given L2 key.
+     * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
+     * calling findL2Key with the attributes and storing in the returned value.
+     *
+     * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+     *              key and only care about grouping can pass a unique ID here like the ones
+     *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+     *              relevance of such a network will lead to it being evicted soon if it's not
+     *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+     * @param attributes The attributes for this network.
+     * @param listener A listener that will be invoked to inform of the completion of this call,
+     *                 or null if the client is not interested in learning about success/failure.
+     * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
+     * If the call failed, the L2 key will be null.
+     */
+    public void storeNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final NetworkAttributes attributes,
+            @Nullable final IOnStatusListener listener) {
         try {
-            return mService.version();
+            mService.storeNetworkAttributes(l2Key, attributes.toParcelable(), listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Store a binary blob associated with an L2 key and a name.
+     *
+     * @param l2Key The L2 key for this network.
+     * @param clientId The ID of the client.
+     * @param name The name of this data.
+     * @param data The data to store.
+     * @param listener A listener to inform of the completion of this call, or null if the client
+     *        is not interested in learning about success/failure.
+     * Through the listener, returns a status to indicate success or failure.
+     */
+    public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final Blob data,
+            @Nullable final IOnStatusListener listener) {
+        try {
+            mService.storeBlob(l2Key, clientId, name, data, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the best L2 key associated with the attributes.
+     *
+     * This will find a record that would be in the same group as the passed attributes. This is
+     * useful to choose the key for storing a sample or private data when the L2 key is not known.
+     * If multiple records are group-close to these attributes, the closest match is returned.
+     * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+     * order) L2 key is returned.
+     * If no record matches these attributes, null is returned.
+     *
+     * @param attributes The attributes of the network to find.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the L2 key if one matched, or null.
+     */
+    public void findL2Key(@NonNull final NetworkAttributes attributes,
+            @NonNull final IOnL2KeyResponseListener listener) {
+        try {
+            mService.findL2Key(attributes.toParcelable(), listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+     * to the same L3 network. Group-closeness is used to determine this.
+     *
+     * @param l2Key1 The key for the first network.
+     * @param l2Key2 The key for the second network.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
+     */
+    public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
+            @NonNull final IOnSameNetworkResponseListener listener) {
+        try {
+            mService.isSameNetwork(l2Key1, l2Key2, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve the network attributes for a key.
+     * If no record is present for this key, this will return null attributes.
+     *
+     * @param l2Key The key of the network to query.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the network attributes and the L2 key associated with
+     *         the query.
+     */
+    public void retrieveNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final IOnNetworkAttributesRetrieved listener) {
+        try {
+            mService.retrieveNetworkAttributes(l2Key, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve previously stored private data.
+     * If no data was stored for this L2 key and name this will return null.
+     *
+     * @param l2Key The L2 key.
+     * @param clientId The id of the client that stored this data.
+     * @param name The name of the data.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the private data (or null), with the L2 key
+     *         and the name of the data associated with the query.
+     */
+    public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+        try {
+            mService.retrieveBlob(l2Key, clientId, name, listener);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/ipmemorystore/Blob.aidl b/core/java/android/net/ipmemorystore/Blob.aidl
new file mode 100644 (file)
index 0000000..9dbef11
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+/**
+ * A blob of data opaque to the memory store. The client mutates this at its own risk,
+ * and it is strongly suggested to never do it at all and treat this as immutable.
+ * {@hide}
+ */
+parcelable Blob {
+    byte[] data;
+}
diff --git a/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
new file mode 100644 (file)
index 0000000..4926feb
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnBlobRetrievedListener {
+    /**
+     * Private data was retrieved for the L2 key and name specified.
+     * Note this does not return the client ID, as clients are expected to only ever use one ID.
+     */
+     void onBlobRetrieved(in StatusParcelable status, in String l2Key, in String name,
+             in Blob data);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
new file mode 100644 (file)
index 0000000..dea0cc4
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnL2KeyResponseListener {
+    /**
+     * The operation completed with the specified L2 key.
+     */
+     void onL2KeyResponse(in StatusParcelable status, in String l2Key);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
new file mode 100644 (file)
index 0000000..57f59a1
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnNetworkAttributesRetrieved {
+    /**
+     * Network attributes were fetched for the specified L2 key. While the L2 key will never
+     * be null, the attributes may be if no data is stored about this L2 key.
+     */
+     void onL2KeyResponse(in StatusParcelable status, in String l2Key,
+             in NetworkAttributesParcelable attributes);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl b/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
new file mode 100644 (file)
index 0000000..294bd3b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.net.ipmemorystore.SameL3NetworkResponseParcelable;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnSameNetworkResponseListener {
+    /**
+     * The memory store has come up with the answer to a query that was sent.
+     */
+     void onSameNetworkResponse(in StatusParcelable status,
+             in SameL3NetworkResponseParcelable response);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnStatusListener.aidl b/core/java/android/net/ipmemorystore/IOnStatusListener.aidl
new file mode 100644 (file)
index 0000000..5d07504
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnStatusListener {
+    /**
+     * The operation has completed with the specified status.
+     */
+     void onComplete(in StatusParcelable status);
+}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java
new file mode 100644 (file)
index 0000000..d7e5b27
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A POD object to represent attributes of a single L2 network entry.
+ * @hide
+ */
+public class NetworkAttributes {
+    private static final boolean DBG = true;
+
+    // The v4 address that was assigned to this device the last time it joined this network.
+    // This typically comes from DHCP but could be something else like static configuration.
+    // This does not apply to IPv6.
+    // TODO : add a list of v6 prefixes for the v6 case.
+    @Nullable
+    public final Inet4Address assignedV4Address;
+
+    // Optionally supplied by the client if it has an opinion on L3 network. For example, this
+    // could be a hash of the SSID + security type on WiFi.
+    @Nullable
+    public final String groupHint;
+
+    // The list of DNS server addresses.
+    @Nullable
+    public final List<InetAddress> dnsAddresses;
+
+    // The mtu on this network.
+    @Nullable
+    public final Integer mtu;
+
+    NetworkAttributes(
+            @Nullable final Inet4Address assignedV4Address,
+            @Nullable final String groupHint,
+            @Nullable final List<InetAddress> dnsAddresses,
+            @Nullable final Integer mtu) {
+        if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+        this.assignedV4Address = assignedV4Address;
+        this.groupHint = groupHint;
+        this.dnsAddresses = null == dnsAddresses ? null :
+                Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
+        this.mtu = mtu;
+    }
+
+    @VisibleForTesting
+    public NetworkAttributes(@NonNull final NetworkAttributesParcelable parcelable) {
+        // The call to the other constructor must be the first statement of this constructor,
+        // so everything has to be inline
+        this((Inet4Address) getByAddressOrNull(parcelable.assignedV4Address),
+                parcelable.groupHint,
+                blobArrayToInetAddressList(parcelable.dnsAddresses),
+                parcelable.mtu >= 0 ? parcelable.mtu : null);
+    }
+
+    @Nullable
+    private static InetAddress getByAddressOrNull(@Nullable final byte[] address) {
+        try {
+            return InetAddress.getByAddress(address);
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
+
+    @Nullable
+    private static List<InetAddress> blobArrayToInetAddressList(@Nullable final Blob[] blobs) {
+        if (null == blobs) return null;
+        final ArrayList<InetAddress> list = new ArrayList<>(blobs.length);
+        for (final Blob b : blobs) {
+            final InetAddress addr = getByAddressOrNull(b.data);
+            if (null != addr) list.add(addr);
+        }
+        return list;
+    }
+
+    @Nullable
+    private static Blob[] inetAddressListToBlobArray(@Nullable final List<InetAddress> addresses) {
+        if (null == addresses) return null;
+        final ArrayList<Blob> blobs = new ArrayList<>();
+        for (int i = 0; i < addresses.size(); ++i) {
+            final InetAddress addr = addresses.get(i);
+            if (null == addr) continue;
+            final Blob b = new Blob();
+            b.data = addr.getAddress();
+            blobs.add(b);
+        }
+        return blobs.toArray(new Blob[0]);
+    }
+
+    /** Converts this NetworkAttributes to a parcelable object */
+    @NonNull
+    public NetworkAttributesParcelable toParcelable() {
+        final NetworkAttributesParcelable parcelable = new NetworkAttributesParcelable();
+        parcelable.assignedV4Address =
+                (null == assignedV4Address) ? null : assignedV4Address.getAddress();
+        parcelable.groupHint = groupHint;
+        parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
+        parcelable.mtu = (null == mtu) ? -1 : mtu;
+        return parcelable;
+    }
+
+    /** @hide */
+    public static class Builder {
+        @Nullable
+        private Inet4Address mAssignedAddress;
+        @Nullable
+        private String mGroupHint;
+        @Nullable
+        private List<InetAddress> mDnsAddresses;
+        @Nullable
+        private Integer mMtu;
+
+        /**
+         * Set the assigned address.
+         * @param assignedV4Address The assigned address.
+         * @return This builder.
+         */
+        public Builder setAssignedV4Address(@Nullable final Inet4Address assignedV4Address) {
+            mAssignedAddress = assignedV4Address;
+            return this;
+        }
+
+        /**
+         * Set the group hint.
+         * @param groupHint The group hint.
+         * @return This builder.
+         */
+        public Builder setGroupHint(@Nullable final String groupHint) {
+            mGroupHint = groupHint;
+            return this;
+        }
+
+        /**
+         * Set the DNS addresses.
+         * @param dnsAddresses The DNS addresses.
+         * @return This builder.
+         */
+        public Builder setDnsAddresses(@Nullable final List<InetAddress> dnsAddresses) {
+            if (DBG && null != dnsAddresses) {
+                // Parceling code crashes if one of the addresses is null, therefore validate
+                // them when running in debug.
+                for (final InetAddress address : dnsAddresses) {
+                    if (null == address) throw new IllegalArgumentException("Null DNS address");
+                }
+            }
+            this.mDnsAddresses = dnsAddresses;
+            return this;
+        }
+
+        /**
+         * Set the MTU.
+         * @param mtu The MTU.
+         * @return This builder.
+         */
+        public Builder setMtu(@Nullable final Integer mtu) {
+            if (null != mtu && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+            mMtu = mtu;
+            return this;
+        }
+
+        /**
+         * Return the built NetworkAttributes object.
+         * @return The built NetworkAttributes object.
+         */
+        public NetworkAttributes build() {
+            return new NetworkAttributes(mAssignedAddress, mGroupHint, mDnsAddresses, mMtu);
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof NetworkAttributes)) return false;
+        final NetworkAttributes other = (NetworkAttributes) o;
+        return Objects.equals(assignedV4Address, other.assignedV4Address)
+                && Objects.equals(groupHint, other.groupHint)
+                && Objects.equals(dnsAddresses, other.dnsAddresses)
+                && Objects.equals(mtu, other.mtu);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu);
+    }
+}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
new file mode 100644 (file)
index 0000000..0894d72
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+// Blob[] is used to represent an array of byte[], as structured AIDL does not support arrays
+// of arrays.
+import android.net.ipmemorystore.Blob;
+
+/**
+ * An object to represent attributes of a single L2 network entry.
+ * See NetworkAttributes.java for a description of each field. The types used in this class
+ * are structured parcelable types instead of the richer types of the NetworkAttributes object,
+ * but they have the same purpose. The NetworkAttributes.java file also contains the code
+ * to convert the richer types to the parcelable types and back.
+ * @hide
+ */
+parcelable NetworkAttributesParcelable {
+    byte[] assignedV4Address;
+    String groupHint;
+    Blob[] dnsAddresses;
+    int mtu;
+}
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
new file mode 100644 (file)
index 0000000..0cb37e9
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * An object representing the answer to a query whether two given L2 networks represent the
+ * same L3 network. Parcels as a SameL3NetworkResponseParceled object.
+ * @hide
+ */
+public class SameL3NetworkResponse {
+    @IntDef(prefix = "NETWORK_",
+            value = {NETWORK_SAME, NETWORK_DIFFERENT, NETWORK_NEVER_CONNECTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkSameness {}
+
+    /**
+     * Both L2 networks represent the same L3 network.
+     */
+    public static final int NETWORK_SAME = 1;
+
+    /**
+     * The two L2 networks represent a different L3 network.
+     */
+    public static final int NETWORK_DIFFERENT = 2;
+
+    /**
+     * The device has never connected to at least one of these two L2 networks, or data
+     * has been wiped. Therefore the device has never seen the L3 network behind at least
+     * one of these two L2 networks, and can't evaluate whether it's the same as the other.
+     */
+    public static final int NETWORK_NEVER_CONNECTED = 3;
+
+    /**
+     * The first L2 key specified in the query.
+     */
+    @NonNull
+    public final String l2Key1;
+
+    /**
+     * The second L2 key specified in the query.
+     */
+    @NonNull
+    public final String l2Key2;
+
+    /**
+     * A confidence value indicating whether the two L2 networks represent the same L3 network.
+     *
+     * If both L2 networks were known, this value will be between 0.0 and 1.0, with 0.0
+     * representing complete confidence that the given L2 networks represent a different
+     * L3 network, and 1.0 representing complete confidence that the given L2 networks
+     * represent the same L3 network.
+     * If at least one of the L2 networks was not known, this value will be outside of the
+     * 0.0~1.0 range.
+     *
+     * Most apps should not be interested in this, and are encouraged to use the collapsing
+     * {@link #getNetworkSameness()} function below.
+     */
+    public final float confidence;
+
+    /**
+     * @return whether the two L2 networks represent the same L3 network. Either
+     *     {@code NETWORK_SAME}, {@code NETWORK_DIFFERENT} or {@code NETWORK_NEVER_CONNECTED}.
+     */
+    @NetworkSameness
+    public final int getNetworkSameness() {
+        if (confidence > 1.0 || confidence < 0.0) return NETWORK_NEVER_CONNECTED;
+        return confidence > 0.5 ? NETWORK_SAME : NETWORK_DIFFERENT;
+    }
+
+    SameL3NetworkResponse(@NonNull final String l2Key1, @NonNull final String l2Key2,
+            final float confidence) {
+        this.l2Key1 = l2Key1;
+        this.l2Key2 = l2Key2;
+        this.confidence = confidence;
+    }
+
+    /** Builds a SameL3NetworkResponse from a parcelable object */
+    @VisibleForTesting
+    public SameL3NetworkResponse(@NonNull final SameL3NetworkResponseParcelable parceled) {
+        this(parceled.l2Key1, parceled.l2Key2, parceled.confidence);
+    }
+
+    /** Converts this SameL3NetworkResponse to a parcelable object */
+    @NonNull
+    public SameL3NetworkResponseParcelable toParcelable() {
+        final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
+        parcelable.l2Key1 = l2Key1;
+        parcelable.l2Key2 = l2Key2;
+        parcelable.confidence = confidence;
+        return parcelable;
+    }
+
+    // Note key1 and key2 have to match each other for this to return true. If
+    // key1 matches o.key2 and the other way around this returns false.
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof SameL3NetworkResponse)) return false;
+        final SameL3NetworkResponse other = (SameL3NetworkResponse) o;
+        return l2Key1.equals(other.l2Key1) && l2Key2.equals(other.l2Key2)
+                && confidence == other.confidence;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(l2Key1, l2Key2, confidence);
+    }
+}
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
new file mode 100644 (file)
index 0000000..7196699
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+/** {@hide} */
+parcelable SameL3NetworkResponseParcelable {
+    String l2Key1;
+    String l2Key2;
+    float confidence;
+}
diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java
new file mode 100644 (file)
index 0000000..5b016ec
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A parcelable status representing the result of an operation.
+ * Parcels as StatusParceled.
+ * @hide
+ */
+public class Status {
+    public static final int SUCCESS = 0;
+
+    public final int resultCode;
+
+    public Status(final int resultCode) {
+        this.resultCode = resultCode;
+    }
+
+    Status(@NonNull final StatusParcelable parcelable) {
+        this(parcelable.resultCode);
+    }
+
+    /** Converts this Status to a parcelable object */
+    @NonNull
+    public StatusParcelable toParcelable() {
+        final StatusParcelable parcelable = new StatusParcelable();
+        parcelable.resultCode = resultCode;
+        return parcelable;
+    }
+
+    public boolean isSuccess() {
+        return SUCCESS == resultCode;
+    }
+}
diff --git a/core/java/android/net/ipmemorystore/StatusParcelable.aidl b/core/java/android/net/ipmemorystore/StatusParcelable.aidl
new file mode 100644 (file)
index 0000000..fb36ef4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+/** {@hide} */
+parcelable StatusParcelable {
+  int resultCode;
+}
index 8680b8d..c9759bf 100644 (file)
 package com.android.server.net.ipmemorystore;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IIpMemoryStore;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
 
 /**
  * Implementation for the IP memory store.
  * This component offers specialized services for network components to store and retrieve
  * knowledge about networks, and provides intelligence that groups level 2 networks together
  * into level 3 networks.
+ *
  * @hide
  */
 public class IpMemoryStoreService extends IIpMemoryStore.Stub {
@@ -35,9 +44,108 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
     }
 
     /**
-     * TODO : remove this useless method.
+     * Store network attributes for a given L2 key.
+     *
+     * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+     *              key and only care about grouping can pass a unique ID here like the ones
+     *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+     *              relevance of such a network will lead to it being evicted soon if it's not
+     *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+     * @param attributes The attributes for this network.
+     * @param listener A listener to inform of the completion of this call, or null if the client
+     *        is not interested in learning about success/failure.
+     * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
+     * If the call failed, the L2 key will be null.
+     */
+    @Override
+    public void storeNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final NetworkAttributesParcelable attributes,
+            @Nullable final IOnStatusListener listener) {
+        // TODO : implement this
+    }
+
+    /**
+     * Store a binary blob associated with an L2 key and a name.
+     *
+     * @param l2Key The L2 key for this network.
+     * @param clientId The ID of the client.
+     * @param name The name of this data.
+     * @param data The data to store.
+     * @param listener The listener that will be invoked to return the answer, or null if the
+     *        is not interested in learning about success/failure.
+     * Through the listener, returns a status to indicate success or failure.
+     */
+    @Override
+    public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final Blob data,
+            @Nullable final IOnStatusListener listener) {
+        // TODO : implement this
+    }
+
+    /**
+     * Returns the best L2 key associated with the attributes.
+     *
+     * This will find a record that would be in the same group as the passed attributes. This is
+     * useful to choose the key for storing a sample or private data when the L2 key is not known.
+     * If multiple records are group-close to these attributes, the closest match is returned.
+     * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+     * order) L2 key is returned.
+     * If no record matches these attributes, null is returned.
+     *
+     * @param attributes The attributes of the network to find.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the L2 key if one matched, or null.
+     */
+    @Override
+    public void findL2Key(@NonNull final NetworkAttributesParcelable attributes,
+            @NonNull final IOnL2KeyResponseListener listener) {
+        // TODO : implement this
+    }
+
+    /**
+     * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+     * to the same L3 network. Group-closeness is used to determine this.
+     *
+     * @param l2Key1 The key for the first network.
+     * @param l2Key2 The key for the second network.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
+     */
+    @Override
+    public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
+            @NonNull final IOnSameNetworkResponseListener listener) {
+        // TODO : implement this
+    }
+
+    /**
+     * Retrieve the network attributes for a key.
+     * If no record is present for this key, this will return null attributes.
+     *
+     * @param l2Key The key of the network to query.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the network attributes and the L2 key associated with
+     *         the query.
+     */
+    @Override
+    public void retrieveNetworkAttributes(@NonNull final String l2Key,
+            @NonNull final IOnNetworkAttributesRetrieved listener) {
+        // TODO : implement this.
+    }
+
+    /**
+     * Retrieve previously stored private data.
+     * If no data was stored for this L2 key and name this will return null.
+     *
+     * @param l2Key The L2 key.
+     * @param clientId The id of the client that stored this data.
+     * @param name The name of the data.
+     * @param listener The listener that will be invoked to return the answer.
+     * Through the listener, returns the private data if any or null if none, with the L2 key
+     *         and the name of the data associated with the query.
      */
-    public int version() {
-        return 1;
+    @Override
+    public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
+            @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+        // TODO : implement this.
     }
 }
index 0de7302..eae9710 100644 (file)
 
 package android.net;
 
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-
 import android.content.Context;
-import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -37,17 +33,32 @@ public class IpMemoryStoreTest {
     Context mMockContext;
     @Mock
     IIpMemoryStore mMockService;
+    IpMemoryStore mStore;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mStore = new IpMemoryStore(mMockContext, mMockService);
+    }
+
+    @Test
+    public void testNetworkAttributes() {
+        // TODO : implement this
     }
 
-    // TODO : remove this useless test
     @Test
-    public void testVersion() throws RemoteException {
-        doReturn(30).when(mMockService).version();
-        final IpMemoryStore store = new IpMemoryStore(mMockContext, mMockService);
-        assertEquals(store.version(), 30);
+    public void testPrivateData() {
+        // TODO : implement this
     }
+
+    @Test
+    public void testFindL2Key() {
+        // TODO : implement this
+    }
+
+    @Test
+    public void testIsSameNetwork() {
+        // TODO : implement this
+    }
+
 }
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
new file mode 100644 (file)
index 0000000..a9f9758
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.ipmemorystore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParcelableTests {
+    @Test
+    public void testNetworkAttributesParceling() throws Exception {
+        final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+        NetworkAttributes in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+        builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+        // groupHint stays null this time around
+        builder.setDnsAddresses(Collections.emptyList());
+        builder.setMtu(18);
+        in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+        builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
+        builder.setGroupHint("groupHint");
+        builder.setDnsAddresses(Arrays.asList(
+                InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
+                InetAddress.getByName("6.7.8.9")));
+        builder.setMtu(1_000_000);
+        in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+        builder.setMtu(null);
+        in = builder.build();
+        assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+    }
+
+    @Test
+    public void testPrivateDataParceling() throws Exception {
+        final Blob in = new Blob();
+        in.data = new byte[] {89, 111, 108, 111};
+        final Blob out = parcelingRoundTrip(in);
+        // Object.equals on byte[] tests the references
+        assertEquals(in.data.length, out.data.length);
+        assertTrue(Arrays.equals(in.data, out.data));
+    }
+
+    @Test
+    public void testSameL3NetworkResponseParceling() throws Exception {
+        final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
+        parcelable.l2Key1 = "key 1";
+        parcelable.l2Key2 = "key 2";
+        parcelable.confidence = 0.43f;
+
+        final SameL3NetworkResponse in = new SameL3NetworkResponse(parcelable);
+        assertEquals("key 1", in.l2Key1);
+        assertEquals("key 2", in.l2Key2);
+        assertEquals(0.43f, in.confidence, 0.01f /* delta */);
+
+        final SameL3NetworkResponse out =
+                new SameL3NetworkResponse(parcelingRoundTrip(in.toParcelable()));
+
+        assertEquals(in, out);
+        assertEquals(in.l2Key1, out.l2Key1);
+        assertEquals(in.l2Key2, out.l2Key2);
+        assertEquals(in.confidence, out.confidence, 0.01f /* delta */);
+    }
+
+    private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception {
+        final Parcel p = Parcel.obtain();
+        in.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        final byte[] marshalledData = p.marshall();
+        p.recycle();
+
+        final Parcel q = Parcel.obtain();
+        q.unmarshall(marshalledData, 0, marshalledData.length);
+        q.setDataPosition(0);
+
+        final Parcelable.Creator<T> creator = (Parcelable.Creator<T>)
+                in.getClass().getField("CREATOR").get(null); // static object, so null receiver
+        final T unmarshalled = (T) creator.createFromParcel(q);
+        q.recycle();
+        return unmarshalled;
+    }
+}
index b2cf415..859a54d 100644 (file)
@@ -16,8 +16,6 @@
 
 package com.android.server.net.ipmemorystore;
 
-import static org.junit.Assert.assertEquals;
-
 import android.content.Context;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -40,10 +38,27 @@ public class IpMemoryStoreServiceTest {
         MockitoAnnotations.initMocks(this);
     }
 
-    // TODO : remove this useless test
     @Test
-    public void testVersion() {
+    public void testNetworkAttributes() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+
+    @Test
+    public void testPrivateData() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+
+    @Test
+    public void testFindL2Key() {
+        final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+        // TODO : implement this
+    }
+
+    @Test
+    public void testIsSameNetwork() {
         final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
-        assertEquals(service.version(), 1);
+        // TODO : implement this
     }
 }