Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c657a844 authored by Xiao Ma's avatar Xiao Ma Committed by android-build-merger
Browse files

Merge "adopt non-blocking method to obtain the IpMemoryStore service."

am: b340a698

Change-Id: Idd6110141795f86dcf28eafab87683eda4c70b93
parents 32f24046 b340a698
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -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.
@@ -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);
    }
}
+29 −4
Original line number Diff line number Diff line
@@ -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,
@@ -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);
                    }

@@ -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
+58 −50
Original line number Diff line number Diff line
@@ -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,
@@ -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.
@@ -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)));
        }
    }

@@ -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)));
        }
    }

@@ -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));
        }
    }

@@ -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));
        }
    }

@@ -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));
        }
    }

@@ -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));
        }
    }
}
+263 −14
Original line number Diff line number Diff line
@@ -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;
@@ -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() {
@@ -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()));
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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;