summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chalard Jean <jchalard@google.com> 2018-12-27 20:59:41 +0900
committer Chalard Jean <jchalard@google.com> 2019-01-21 15:21:09 +0900
commitbf73e66d4db7e2cd235017333260ee2fdceea5ee (patch)
treed1f92c3215c276ce5e4df17f4fd595bf88678eb6
parent91549b6d1be1e0d8d0deb9da45050eb76165a39d (diff)
[MS08] Read back attributes and blobs.
Test: New tests in IpMemoryStore Bug: 113554482 Change-Id: I2ddfef0c2ed37459c038f75d1dfc92fdefbf58f5
-rw-r--r--services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java98
-rw-r--r--services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java54
-rw-r--r--tests/net/java/android/net/ipmemorystore/ParcelableTests.java7
-rw-r--r--tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java210
4 files changed, 341 insertions, 28 deletions
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
index 25522763df09..238f07715ce3 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -29,8 +29,11 @@ import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.Status;
import android.util.Log;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -71,7 +74,7 @@ public class IpMemoryStoreDatabase {
public static final String COLTYPE_DNSADDRESSES = "BLOB";
public static final String COLNAME_MTU = "mtu";
- public static final String COLTYPE_MTU = "INTEGER";
+ public static final String COLTYPE_MTU = "INTEGER DEFAULT -1";
public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
+ TABLENAME + " ("
@@ -122,7 +125,7 @@ public class IpMemoryStoreDatabase {
/** The SQLite DB helper */
public static class DbHelper extends SQLiteOpenHelper {
// Update this whenever changing the schema.
- private static final int SCHEMA_VERSION = 1;
+ private static final int SCHEMA_VERSION = 2;
private static final String DATABASE_FILENAME = "IpMemoryStore.db";
public DbHelper(@NonNull final Context context) {
@@ -166,6 +169,21 @@ public class IpMemoryStoreDatabase {
return os.toByteArray();
}
+ @NonNull
+ private static ArrayList<InetAddress> decodeAddressList(@NonNull final byte[] encoded) {
+ final ByteArrayInputStream is = new ByteArrayInputStream(encoded);
+ final ArrayList<InetAddress> addresses = new ArrayList<>();
+ int d = -1;
+ while ((d = is.read()) != -1) {
+ final byte[] bytes = new byte[d];
+ is.read(bytes, 0, d);
+ try {
+ addresses.add(InetAddress.getByAddress(bytes));
+ } catch (UnknownHostException e) { /* Hopefully impossible */ }
+ }
+ return addresses;
+ }
+
// Convert a NetworkAttributes object to content values to store them in a table compliant
// with the contract defined in NetworkAttributesContract.
@NonNull
@@ -275,4 +293,80 @@ public class IpMemoryStoreDatabase {
toContentValues(key, clientId, name, data), SQLiteDatabase.CONFLICT_REPLACE);
return (res == -1) ? Status.ERROR_STORAGE : Status.SUCCESS;
}
+
+ @Nullable
+ static NetworkAttributes retrieveNetworkAttributes(@NonNull final SQLiteDatabase db,
+ @NonNull final String key) {
+ final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
+ null, // columns, null means everything
+ NetworkAttributesContract.COLNAME_L2KEY + " = ?", // selection
+ new String[] { key }, // selectionArgs
+ null, // groupBy
+ null, // having
+ null); // orderBy
+ // L2KEY is the primary key ; it should not be possible to get more than one
+ // result here. 0 results means the key was not found.
+ if (cursor.getCount() != 1) return null;
+ cursor.moveToFirst();
+
+ // Make sure the data hasn't expired
+ final long expiry = cursor.getLong(
+ cursor.getColumnIndexOrThrow(NetworkAttributesContract.COLNAME_EXPIRYDATE));
+ if (expiry < System.currentTimeMillis()) return null;
+
+ final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+ final int assignedV4AddressInt = getInt(cursor,
+ NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
+ final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
+ final byte[] dnsAddressesBlob =
+ getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
+ final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
+ if (0 != assignedV4AddressInt) {
+ builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
+ }
+ builder.setGroupHint(groupHint);
+ if (null != dnsAddressesBlob) {
+ builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
+ }
+ if (mtu >= 0) {
+ builder.setMtu(mtu);
+ }
+ return builder.build();
+ }
+
+ private static final String[] DATA_COLUMN = new String[] {
+ PrivateDataContract.COLNAME_DATA
+ };
+ @Nullable
+ static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key,
+ @NonNull final String clientId, @NonNull final String name) {
+ final Cursor cursor = db.query(PrivateDataContract.TABLENAME,
+ DATA_COLUMN, // columns
+ PrivateDataContract.COLNAME_L2KEY + " = ? AND " // selection
+ + PrivateDataContract.COLNAME_CLIENT + " = ? AND "
+ + PrivateDataContract.COLNAME_DATANAME + " = ?",
+ new String[] { key, clientId, name }, // selectionArgs
+ null, // groupBy
+ null, // having
+ null); // orderBy
+ // The query above is querying by (composite) primary key, so it should not be possible to
+ // get more than one result here. 0 results means the key was not found.
+ if (cursor.getCount() != 1) return null;
+ cursor.moveToFirst();
+ return cursor.getBlob(0); // index in the DATA_COLUMN array
+ }
+
+ // Helper methods
+ static String getString(final Cursor cursor, final String columnName) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getString(columnIndex) : null;
+ }
+ static byte[] getBlob(final Cursor cursor, final String columnName) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getBlob(columnIndex) : null;
+ }
+ static int getInt(final Cursor cursor, final String columnName, final int defaultValue) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getInt(columnIndex) : defaultValue;
+ }
}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
index f002da851287..444b299d49e6 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -19,6 +19,7 @@ package com.android.server.net.ipmemorystore;
import static android.net.ipmemorystore.Status.ERROR_DATABASE_CANNOT_BE_OPENED;
import static android.net.ipmemorystore.Status.ERROR_GENERIC;
import static android.net.ipmemorystore.Status.ERROR_ILLEGAL_ARGUMENT;
+import static android.net.ipmemorystore.Status.SUCCESS;
import static com.android.server.net.ipmemorystore.IpMemoryStoreDatabase.EXPIRY_ERROR;
@@ -278,9 +279,32 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
* the query.
*/
@Override
- public void retrieveNetworkAttributes(@NonNull final String l2Key,
- @NonNull final IOnNetworkAttributesRetrieved listener) {
- // TODO : implement this.
+ public void retrieveNetworkAttributes(@Nullable final String l2Key,
+ @Nullable final IOnNetworkAttributesRetrieved listener) {
+ if (null == listener) return;
+ mExecutor.execute(() -> {
+ try {
+ if (null == l2Key) {
+ listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, null);
+ return;
+ }
+ if (null == mDb) {
+ listener.onL2KeyResponse(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key,
+ null);
+ return;
+ }
+ try {
+ final NetworkAttributes attributes =
+ IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key);
+ listener.onL2KeyResponse(makeStatus(SUCCESS), l2Key,
+ null == attributes ? null : attributes.toParcelable());
+ } catch (final Exception e) {
+ listener.onL2KeyResponse(makeStatus(ERROR_GENERIC), l2Key, null);
+ }
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
/**
@@ -297,6 +321,28 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
@Override
public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
@NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
- // TODO : implement this.
+ if (null == listener) return;
+ mExecutor.execute(() -> {
+ try {
+ if (null == l2Key) {
+ listener.onBlobRetrieved(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, name, null);
+ return;
+ }
+ if (null == mDb) {
+ listener.onBlobRetrieved(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key,
+ name, null);
+ return;
+ }
+ try {
+ final Blob b = new Blob();
+ b.data = IpMemoryStoreDatabase.retrieveBlob(mDb, l2Key, clientId, name);
+ listener.onBlobRetrieved(makeStatus(SUCCESS), l2Key, name, b);
+ } catch (final Exception e) {
+ listener.onBlobRetrieved(makeStatus(ERROR_GENERIC), l2Key, name, null);
+ }
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
}
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index a9f9758bb1f8..1fc67a8212ae 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -27,6 +27,7 @@ import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Modifier;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
@@ -60,6 +61,12 @@ public class ParcelableTests {
builder.setMtu(null);
in = builder.build();
assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+ // Verify that this test does not miss any new field added later.
+ // If any field is added to NetworkAttributes it must be tested here for parceling
+ // roundtrip.
+ assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
}
@Test
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index 00137f83f52e..94bcd28bf009 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -16,6 +16,9 @@
package com.android.server.net.ipmemorystore;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
@@ -23,8 +26,11 @@ import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
import android.net.ipmemorystore.Status;
import android.net.ipmemorystore.StatusParcelable;
import android.os.IBinder;
@@ -41,8 +47,12 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
+import java.lang.reflect.Modifier;
import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -92,6 +102,59 @@ public class IpMemoryStoreServiceTest {
};
}
+ /** Helper method to make an IOnBlobRetrievedListener */
+ private interface OnBlobRetrievedListener {
+ void onBlobRetrieved(Status status, String l2Key, String name, byte[] data);
+ }
+ private IOnBlobRetrievedListener onBlobRetrieved(final OnBlobRetrievedListener functor) {
+ return new IOnBlobRetrievedListener() {
+ @Override
+ public void onBlobRetrieved(final StatusParcelable statusParcelable,
+ final String l2Key, final String name, final Blob blob) throws RemoteException {
+ functor.onBlobRetrieved(new Status(statusParcelable), l2Key, name,
+ null == blob ? null : blob.data);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
+ /** Helper method to make an IOnNetworkAttributesRetrievedListener */
+ private interface OnNetworkAttributesRetrievedListener {
+ void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr);
+ }
+ private IOnNetworkAttributesRetrieved onNetworkAttributesRetrieved(
+ final OnNetworkAttributesRetrievedListener functor) {
+ return new IOnNetworkAttributesRetrieved() {
+ @Override
+ public void onL2KeyResponse(final StatusParcelable status, final String l2Key,
+ final NetworkAttributesParcelable attributes)
+ throws RemoteException {
+ functor.onNetworkAttributesRetrieved(new Status(status), l2Key,
+ null == attributes ? null : new NetworkAttributes(attributes));
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
+ // Helper method to factorize some boilerplate
+ private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ functor.accept(latch);
+ try {
+ latch.await(5000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail(timeoutMessage);
+ }
+ }
+
@Test
public void testNetworkAttributes() {
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
@@ -102,18 +165,103 @@ public class IpMemoryStoreServiceTest {
na.setGroupHint("hint1");
na.setMtu(219);
final String l2Key = UUID.randomUUID().toString();
- final CountDownLatch latch = new CountDownLatch(1);
- mService.storeNetworkAttributes(l2Key, na.build().toParcelable(),
- onStatus(status -> {
- assertTrue("Store status not successful : " + status.resultCode,
+ NetworkAttributes attributes = na.build();
+ doLatched("Did not complete storing attributes", latch ->
+ mService.storeNetworkAttributes(l2Key, attributes.toParcelable(),
+ onStatus(status -> {
+ assertTrue("Store status not successful : " + status.resultCode,
+ status.isSuccess());
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving attributes", latch ->
+ mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(attributes, attr);
+ latch.countDown();
+ })));
+
+ final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder();
+ try {
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
+ } catch (UnknownHostException e) { /* Still can't happen */ }
+ final NetworkAttributes attributes2 = na2.build();
+ doLatched("Did not complete storing attributes 2", latch ->
+ mService.storeNetworkAttributes(l2Key, attributes2.toParcelable(),
+ onStatus(status -> latch.countDown())));
+
+ doLatched("Did not complete retrieving attributes 2", latch ->
+ mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(attributes.assignedV4Address, attr.assignedV4Address);
+ assertEquals(attributes.groupHint, attr.groupHint);
+ assertEquals(attributes.mtu, attr.mtu);
+ assertEquals(attributes2.dnsAddresses, attr.dnsAddresses);
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving attributes 3", latch ->
+ mService.retrieveNetworkAttributes(l2Key + "nonexistent",
+ onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key + "nonexistent", key);
+ assertNull("Retrieved data not stored", attr);
+ latch.countDown();
+ }
+ )));
+
+ // Verify that this test does not miss any new field added later.
+ // If any field is added to NetworkAttributes it must be tested here for storing
+ // and retrieving.
+ assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
+ }
+
+ @Test
+ public void testInvalidAttributes() {
+ doLatched("Did not complete storing bad attributes", latch ->
+ mService.storeNetworkAttributes("key", null, onStatus(status -> {
+ assertFalse("Success storing on a null key",
status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
latch.countDown();
- }));
- try {
- latch.await(5000, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- fail("Did not complete storing attributes");
- }
+ })));
+
+ final NetworkAttributes na = new NetworkAttributes.Builder().setMtu(2).build();
+ doLatched("Did not complete storing bad attributes", latch ->
+ mService.storeNetworkAttributes(null, na.toParcelable(), onStatus(status -> {
+ assertFalse("Success storing null attributes on a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete storing bad attributes", latch ->
+ mService.storeNetworkAttributes(null, null, onStatus(status -> {
+ assertFalse("Success storing null attributes on a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving bad attributes", latch ->
+ mService.retrieveNetworkAttributes(null, onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertFalse("Success retrieving attributes for a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ assertNull(key);
+ assertNull(attr);
+ })));
}
@Test
@@ -121,18 +269,36 @@ public class IpMemoryStoreServiceTest {
final Blob b = new Blob();
b.data = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 };
final String l2Key = UUID.randomUUID().toString();
- final CountDownLatch latch = new CountDownLatch(1);
- mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
- onStatus(status -> {
- assertTrue("Store status not successful : " + status.resultCode,
- status.isSuccess());
- latch.countDown();
- }));
- try {
- latch.await(5000, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- fail("Did not complete storing private data");
- }
+ doLatched("Did not complete storing private data", latch ->
+ mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
+ onStatus(status -> {
+ assertTrue("Store status not successful : " + status.resultCode,
+ status.isSuccess());
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving private data", latch ->
+ mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved(
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME);
+ Arrays.equals(b.data, data);
+ latch.countDown();
+ })));
+
+ // Most puzzling error message ever
+ doLatched("Did not complete retrieving nothing", latch ->
+ mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME + "2", onBlobRetrieved(
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME + "2");
+ assertNull(data);
+ latch.countDown();
+ })));
}
@Test