Loading packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java +5 −3 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ package android.net; import android.annotation.NonNull; import android.content.Context; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; /** * service used to communicate with the ip memory store service in network stack, * which is running in the same module. Loading @@ -35,8 +38,7 @@ public class NetworkStackIpMemoryStore extends IpMemoryStoreClient { } @Override @NonNull protected IIpMemoryStore getService() { return mService; protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { cb.accept(mService); } } services/net/java/android/net/IpMemoryStore.java +29 −4 Original line number Diff line number Diff line Loading @@ -18,11 +18,14 @@ package android.net; import android.annotation.NonNull; import android.content.Context; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; /** * Manager class used to communicate with the ip memory store service in the network stack, Loading @@ -30,15 +33,18 @@ import java.util.concurrent.ExecutionException; * @hide */ public class IpMemoryStore extends IpMemoryStoreClient { private final CompletableFuture<IIpMemoryStore> mService; private static final String TAG = IpMemoryStore.class.getSimpleName(); @NonNull private final CompletableFuture<IIpMemoryStore> mService; @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode; public IpMemoryStore(@NonNull final Context context) { super(context); mService = new CompletableFuture<>(); mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService); getNetworkStackClient().fetchIpMemoryStore( new IIpMemoryStoreCallbacks.Stub() { @Override public void onIpMemoryStoreFetched(final IIpMemoryStore memoryStore) { public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) { mService.complete(memoryStore); } Loading @@ -49,9 +55,28 @@ public class IpMemoryStore extends IpMemoryStoreClient { }); } /* * If the IpMemoryStore is ready, this function will run the request synchronously. * Otherwise, it will enqueue the requests for execution immediately after the * service becomes ready. The requests are guaranteed to be executed in the order * they are sumbitted. */ @Override protected IIpMemoryStore getService() throws InterruptedException, ExecutionException { return mService.get(); protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { mTailNode.getAndUpdate(future -> future.handle((store, exception) -> { if (exception != null) { // this should never happens since we also catch the exception below Log.wtf(TAG, "Error fetching IpMemoryStore", exception); return store; } try { cb.accept(store); } catch (Exception e) { Log.wtf(TAG, "Exception occured: " + e.getMessage()); } return store; })); } @VisibleForTesting Loading services/net/java/android/net/IpMemoryStoreClient.java +58 −50 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; /** * service used to communicate with the ip memory store service in network stack, Loading @@ -46,8 +47,25 @@ public abstract class IpMemoryStoreClient { mContext = context; } @NonNull protected abstract IIpMemoryStore getService() throws InterruptedException, ExecutionException; protected abstract void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException; @FunctionalInterface private interface ThrowingRunnable { void run() throws RemoteException; } private void ignoringRemoteException(ThrowingRunnable r) { ignoringRemoteException("Failed to execute remote procedure call", r); } private void ignoringRemoteException(String message, ThrowingRunnable r) { try { r.run(); } catch (RemoteException e) { Log.e(TAG, message, e); } } /** * Store network attributes for a given L2 key. Loading @@ -69,14 +87,12 @@ public abstract class IpMemoryStoreClient { @NonNull final NetworkAttributes attributes, @Nullable final OnStatusListener listener) { try { try { getService().storeNetworkAttributes(l2Key, attributes.toParcelable(), OnStatusListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onComplete(new Status(Status.ERROR_UNKNOWN)); } } catch (RemoteException e) { Log.e(TAG, "Error storing network attributes", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(), OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error storing network attributes", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } } Loading @@ -95,14 +111,12 @@ public abstract class IpMemoryStoreClient { @NonNull final String name, @NonNull final Blob data, @Nullable final OnStatusListener listener) { try { try { getService().storeBlob(l2Key, clientId, name, data, OnStatusListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onComplete(new Status(Status.ERROR_UNKNOWN)); } } catch (RemoteException e) { Log.e(TAG, "Error storing blob", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.storeBlob(l2Key, clientId, name, data, OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error storing blob", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } } Loading @@ -123,14 +137,12 @@ public abstract class IpMemoryStoreClient { public void findL2Key(@NonNull final NetworkAttributes attributes, @NonNull final OnL2KeyResponseListener listener) { try { try { getService().findL2Key(attributes.toParcelable(), OnL2KeyResponseListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null); } } catch (RemoteException e) { Log.e(TAG, "Error finding L2 Key", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.findL2Key(attributes.toParcelable(), OnL2KeyResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error finding L2 Key", () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null)); } } Loading @@ -146,14 +158,12 @@ public abstract class IpMemoryStoreClient { public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2, @NonNull final OnSameL3NetworkResponseListener listener) { try { try { getService().isSameNetwork(l2Key1, l2Key2, OnSameL3NetworkResponseListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null); } } catch (RemoteException e) { Log.e(TAG, "Error checking for network sameness", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.isSameNetwork(l2Key1, l2Key2, OnSameL3NetworkResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error checking for network sameness", () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null)); } } Loading @@ -169,14 +179,13 @@ public abstract class IpMemoryStoreClient { public void retrieveNetworkAttributes(@NonNull final String l2Key, @NonNull final OnNetworkAttributesRetrievedListener listener) { try { try { getService().retrieveNetworkAttributes(l2Key, OnNetworkAttributesRetrievedListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null); } } catch (RemoteException e) { Log.e(TAG, "Error retrieving network attributes", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.retrieveNetworkAttributes(l2Key, OnNetworkAttributesRetrievedListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error retrieving network attributes", () -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null)); } } Loading @@ -194,14 +203,13 @@ public abstract class IpMemoryStoreClient { public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final OnBlobRetrievedListener listener) { try { try { getService().retrieveBlob(l2Key, clientId, name, OnBlobRetrievedListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null); } } catch (RemoteException e) { Log.e(TAG, "Error retrieving blob", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.retrieveBlob(l2Key, clientId, name, OnBlobRetrievedListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error retrieving blob", () -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null)); } } } tests/net/java/android/net/IpMemoryStoreTest.java +263 −14 Original line number Diff line number Diff line Loading @@ -16,10 +16,26 @@ package android.net; import static org.mockito.ArgumentMatchers.any; 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.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.ipmemorystore.Blob; import android.net.ipmemorystore.IOnStatusListener; import android.net.ipmemorystore.NetworkAttributes; import android.net.ipmemorystore.NetworkAttributesParcelable; import android.net.ipmemorystore.Status; import android.os.RemoteException; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -27,28 +43,57 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.UnknownHostException; import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest public class IpMemoryStoreTest { private static final String TAG = IpMemoryStoreTest.class.getSimpleName(); private static final String TEST_CLIENT_ID = "testClientId"; private static final String TEST_DATA_NAME = "testData"; private static final String TEST_OTHER_DATA_NAME = TEST_DATA_NAME + "Other"; private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 }; private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes( "hint", 219); @Mock Context mMockContext; @Mock NetworkStackClient mNetworkStackClient; @Mock IIpMemoryStore mMockService; @Mock IOnStatusListener mIOnStatusListener; IpMemoryStore mStore; @Captor ArgumentCaptor<IIpMemoryStoreCallbacks> mCbCaptor; @Captor ArgumentCaptor<NetworkAttributesParcelable> mNapCaptor; @Before public void setUp() { MockitoAnnotations.initMocks(this); } private void startIpMemoryStore(boolean supplyService) { if (supplyService) { doAnswer(invocation -> { ((IIpMemoryStoreCallbacks) invocation.getArgument(0)) .onIpMemoryStoreFetched(mMockService); return null; }).when(mNetworkStackClient).fetchIpMemoryStore(any()); } else { doNothing().when(mNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture()); } mStore = new IpMemoryStore(mMockContext) { @Override protected NetworkStackClient getNetworkStackClient() { Loading @@ -57,24 +102,228 @@ public class IpMemoryStoreTest { }; } private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) { return new NetworkAttributes.Builder() .setGroupHint(hint) .setMtu(mtu) .build(); } @Test public void testNetworkAttributes() { // TODO : implement this public void testNetworkAttributes() throws Exception { startIpMemoryStore(true); final String l2Key = "fakeKey"; mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); verify(mMockService, times(1)).retrieveNetworkAttributes(eq(l2Key), any()); } @Test public void testPrivateData() { // TODO : implement this public void testPrivateData() throws RemoteException { startIpMemoryStore(true); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); verify(mMockService, times(1)).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); verify(mMockService, times(1)).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); } @Test public void testFindL2Key() { // TODO : implement this public void testFindL2Key() throws UnknownHostException, RemoteException, Exception { startIpMemoryStore(true); final String l2Key = "fakeKey"; mStore.findL2Key(TEST_NETWORK_ATTRIBUTES, (status, key) -> { assertTrue("Retrieve network sameness not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); }); verify(mMockService, times(1)).findL2Key(mNapCaptor.capture(), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } @Test public void testIsSameNetwork() { // TODO : implement this public void testIsSameNetwork() throws UnknownHostException, RemoteException { startIpMemoryStore(true); final String l2Key1 = "fakeKey1"; final String l2Key2 = "fakeKey2"; mStore.isSameNetwork(l2Key1, l2Key2, (status, answer) -> { assertFalse("Retrieve network sameness suspiciously successful : " + status.resultCode, status.isSuccess()); assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); assertNull(answer); }); verify(mMockService, times(1)).isSameNetwork(eq(l2Key1), eq(l2Key2), any()); } @Test public void testEnqueuedIpMsRequests() throws Exception { startIpMemoryStore(false); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); // get ipms service ready mCbCaptor.getValue().onIpMemoryStoreFetched(mMockService); InOrder inOrder = inOrder(mMockService); inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } @Test public void testEnqueuedIpMsRequestsWithException() throws Exception { startIpMemoryStore(true); doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any()); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); // verify the rest of the queue is still processed in order even if the remote exception // occurs when calling one or more requests InOrder inOrder = inOrder(mMockService); inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } @Test public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception { startIpMemoryStore(true); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { throw new RuntimeException("retrieveNetworkAttributes test"); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { throw new RuntimeException("storeBlob test"); }); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); // verify the rest of the queue is still processed in order even if when one or more // callback throw the remote exception InOrder inOrder = inOrder(mMockService); inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } } tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.server.connectivity.ipmemorystore; package com.android.server.net.ipmemorystore; import static org.junit.Assert.assertEquals; Loading Loading
packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java +5 −3 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ package android.net; import android.annotation.NonNull; import android.content.Context; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; /** * service used to communicate with the ip memory store service in network stack, * which is running in the same module. Loading @@ -35,8 +38,7 @@ public class NetworkStackIpMemoryStore extends IpMemoryStoreClient { } @Override @NonNull protected IIpMemoryStore getService() { return mService; protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { cb.accept(mService); } }
services/net/java/android/net/IpMemoryStore.java +29 −4 Original line number Diff line number Diff line Loading @@ -18,11 +18,14 @@ package android.net; import android.annotation.NonNull; import android.content.Context; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; /** * Manager class used to communicate with the ip memory store service in the network stack, Loading @@ -30,15 +33,18 @@ import java.util.concurrent.ExecutionException; * @hide */ public class IpMemoryStore extends IpMemoryStoreClient { private final CompletableFuture<IIpMemoryStore> mService; private static final String TAG = IpMemoryStore.class.getSimpleName(); @NonNull private final CompletableFuture<IIpMemoryStore> mService; @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode; public IpMemoryStore(@NonNull final Context context) { super(context); mService = new CompletableFuture<>(); mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService); getNetworkStackClient().fetchIpMemoryStore( new IIpMemoryStoreCallbacks.Stub() { @Override public void onIpMemoryStoreFetched(final IIpMemoryStore memoryStore) { public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) { mService.complete(memoryStore); } Loading @@ -49,9 +55,28 @@ public class IpMemoryStore extends IpMemoryStoreClient { }); } /* * If the IpMemoryStore is ready, this function will run the request synchronously. * Otherwise, it will enqueue the requests for execution immediately after the * service becomes ready. The requests are guaranteed to be executed in the order * they are sumbitted. */ @Override protected IIpMemoryStore getService() throws InterruptedException, ExecutionException { return mService.get(); protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { mTailNode.getAndUpdate(future -> future.handle((store, exception) -> { if (exception != null) { // this should never happens since we also catch the exception below Log.wtf(TAG, "Error fetching IpMemoryStore", exception); return store; } try { cb.accept(store); } catch (Exception e) { Log.wtf(TAG, "Exception occured: " + e.getMessage()); } return store; })); } @VisibleForTesting Loading
services/net/java/android/net/IpMemoryStoreClient.java +58 −50 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; /** * service used to communicate with the ip memory store service in network stack, Loading @@ -46,8 +47,25 @@ public abstract class IpMemoryStoreClient { mContext = context; } @NonNull protected abstract IIpMemoryStore getService() throws InterruptedException, ExecutionException; protected abstract void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException; @FunctionalInterface private interface ThrowingRunnable { void run() throws RemoteException; } private void ignoringRemoteException(ThrowingRunnable r) { ignoringRemoteException("Failed to execute remote procedure call", r); } private void ignoringRemoteException(String message, ThrowingRunnable r) { try { r.run(); } catch (RemoteException e) { Log.e(TAG, message, e); } } /** * Store network attributes for a given L2 key. Loading @@ -69,14 +87,12 @@ public abstract class IpMemoryStoreClient { @NonNull final NetworkAttributes attributes, @Nullable final OnStatusListener listener) { try { try { getService().storeNetworkAttributes(l2Key, attributes.toParcelable(), OnStatusListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onComplete(new Status(Status.ERROR_UNKNOWN)); } } catch (RemoteException e) { Log.e(TAG, "Error storing network attributes", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(), OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error storing network attributes", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } } Loading @@ -95,14 +111,12 @@ public abstract class IpMemoryStoreClient { @NonNull final String name, @NonNull final Blob data, @Nullable final OnStatusListener listener) { try { try { getService().storeBlob(l2Key, clientId, name, data, OnStatusListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onComplete(new Status(Status.ERROR_UNKNOWN)); } } catch (RemoteException e) { Log.e(TAG, "Error storing blob", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.storeBlob(l2Key, clientId, name, data, OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error storing blob", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } } Loading @@ -123,14 +137,12 @@ public abstract class IpMemoryStoreClient { public void findL2Key(@NonNull final NetworkAttributes attributes, @NonNull final OnL2KeyResponseListener listener) { try { try { getService().findL2Key(attributes.toParcelable(), OnL2KeyResponseListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null); } } catch (RemoteException e) { Log.e(TAG, "Error finding L2 Key", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.findL2Key(attributes.toParcelable(), OnL2KeyResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error finding L2 Key", () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null)); } } Loading @@ -146,14 +158,12 @@ public abstract class IpMemoryStoreClient { public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2, @NonNull final OnSameL3NetworkResponseListener listener) { try { try { getService().isSameNetwork(l2Key1, l2Key2, OnSameL3NetworkResponseListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null); } } catch (RemoteException e) { Log.e(TAG, "Error checking for network sameness", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.isSameNetwork(l2Key1, l2Key2, OnSameL3NetworkResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error checking for network sameness", () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null)); } } Loading @@ -169,14 +179,13 @@ public abstract class IpMemoryStoreClient { public void retrieveNetworkAttributes(@NonNull final String l2Key, @NonNull final OnNetworkAttributesRetrievedListener listener) { try { try { getService().retrieveNetworkAttributes(l2Key, OnNetworkAttributesRetrievedListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null); } } catch (RemoteException e) { Log.e(TAG, "Error retrieving network attributes", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.retrieveNetworkAttributes(l2Key, OnNetworkAttributesRetrievedListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error retrieving network attributes", () -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null)); } } Loading @@ -194,14 +203,13 @@ public abstract class IpMemoryStoreClient { public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final OnBlobRetrievedListener listener) { try { try { getService().retrieveBlob(l2Key, clientId, name, OnBlobRetrievedListener.toAIDL(listener)); } catch (InterruptedException | ExecutionException m) { listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null); } } catch (RemoteException e) { Log.e(TAG, "Error retrieving blob", e); runWhenServiceReady(service -> ignoringRemoteException( () -> service.retrieveBlob(l2Key, clientId, name, OnBlobRetrievedListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error retrieving blob", () -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null)); } } }
tests/net/java/android/net/IpMemoryStoreTest.java +263 −14 Original line number Diff line number Diff line Loading @@ -16,10 +16,26 @@ package android.net; import static org.mockito.ArgumentMatchers.any; 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.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.ipmemorystore.Blob; import android.net.ipmemorystore.IOnStatusListener; import android.net.ipmemorystore.NetworkAttributes; import android.net.ipmemorystore.NetworkAttributesParcelable; import android.net.ipmemorystore.Status; import android.os.RemoteException; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -27,28 +43,57 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.UnknownHostException; import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest public class IpMemoryStoreTest { private static final String TAG = IpMemoryStoreTest.class.getSimpleName(); private static final String TEST_CLIENT_ID = "testClientId"; private static final String TEST_DATA_NAME = "testData"; private static final String TEST_OTHER_DATA_NAME = TEST_DATA_NAME + "Other"; private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 }; private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes( "hint", 219); @Mock Context mMockContext; @Mock NetworkStackClient mNetworkStackClient; @Mock IIpMemoryStore mMockService; @Mock IOnStatusListener mIOnStatusListener; IpMemoryStore mStore; @Captor ArgumentCaptor<IIpMemoryStoreCallbacks> mCbCaptor; @Captor ArgumentCaptor<NetworkAttributesParcelable> mNapCaptor; @Before public void setUp() { MockitoAnnotations.initMocks(this); } private void startIpMemoryStore(boolean supplyService) { if (supplyService) { doAnswer(invocation -> { ((IIpMemoryStoreCallbacks) invocation.getArgument(0)) .onIpMemoryStoreFetched(mMockService); return null; }).when(mNetworkStackClient).fetchIpMemoryStore(any()); } else { doNothing().when(mNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture()); } mStore = new IpMemoryStore(mMockContext) { @Override protected NetworkStackClient getNetworkStackClient() { Loading @@ -57,24 +102,228 @@ public class IpMemoryStoreTest { }; } private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) { return new NetworkAttributes.Builder() .setGroupHint(hint) .setMtu(mtu) .build(); } @Test public void testNetworkAttributes() { // TODO : implement this public void testNetworkAttributes() throws Exception { startIpMemoryStore(true); final String l2Key = "fakeKey"; mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); verify(mMockService, times(1)).retrieveNetworkAttributes(eq(l2Key), any()); } @Test public void testPrivateData() { // TODO : implement this public void testPrivateData() throws RemoteException { startIpMemoryStore(true); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); verify(mMockService, times(1)).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); verify(mMockService, times(1)).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); } @Test public void testFindL2Key() { // TODO : implement this public void testFindL2Key() throws UnknownHostException, RemoteException, Exception { startIpMemoryStore(true); final String l2Key = "fakeKey"; mStore.findL2Key(TEST_NETWORK_ATTRIBUTES, (status, key) -> { assertTrue("Retrieve network sameness not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); }); verify(mMockService, times(1)).findL2Key(mNapCaptor.capture(), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } @Test public void testIsSameNetwork() { // TODO : implement this public void testIsSameNetwork() throws UnknownHostException, RemoteException { startIpMemoryStore(true); final String l2Key1 = "fakeKey1"; final String l2Key2 = "fakeKey2"; mStore.isSameNetwork(l2Key1, l2Key2, (status, answer) -> { assertFalse("Retrieve network sameness suspiciously successful : " + status.resultCode, status.isSuccess()); assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); assertNull(answer); }); verify(mMockService, times(1)).isSameNetwork(eq(l2Key1), eq(l2Key2), any()); } @Test public void testEnqueuedIpMsRequests() throws Exception { startIpMemoryStore(false); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); // get ipms service ready mCbCaptor.getValue().onIpMemoryStoreFetched(mMockService); InOrder inOrder = inOrder(mMockService); inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } @Test public void testEnqueuedIpMsRequestsWithException() throws Exception { startIpMemoryStore(true); doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any()); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); // verify the rest of the queue is still processed in order even if the remote exception // occurs when calling one or more requests InOrder inOrder = inOrder(mMockService); inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } @Test public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception { startIpMemoryStore(true); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, status -> { assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); }); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { throw new RuntimeException("retrieveNetworkAttributes test"); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, status -> { throw new RuntimeException("storeBlob test"); }); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, status.isSuccess()); assertEquals(l2Key, key); assertEquals(name, TEST_DATA_NAME); assertTrue(Arrays.equals(b.data, data.data)); }); // verify the rest of the queue is still processed in order even if when one or more // callback throw the remote exception InOrder inOrder = inOrder(mMockService); inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } }
tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.server.connectivity.ipmemorystore; package com.android.server.net.ipmemorystore; import static org.junit.Assert.assertEquals; Loading