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

Commit 12430ba4 authored by Raphael Kim's avatar Raphael Kim Committed by Android (Google) Code Review
Browse files

Merge "[V][CDM] Add one-way message types to CDM transport." into main

parents 8f49d50d 84400d58
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -219,6 +219,12 @@ public final class CompanionDeviceManager {
     * @hide
     */
    public static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
    /**
     * Test message type without a response.
     *
     * @hide
     */
    public static final int MESSAGE_ONEWAY_PING = 0x43807378; // +PIN
    /**
     * Message header assigned to the remote authentication handshakes.
     *
@@ -237,6 +243,18 @@ public final class CompanionDeviceManager {
     * @hide
     */
    public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
    /**
     * Message header assigned to the one-way message sent from the wearable device.
     *
     * @hide
     */
    public static final int MESSAGE_ONEWAY_FROM_WEARABLE = 0x43708287; // +FRW
    /**
     * Message header assigned to the one-way message sent to the wearable device.
     *
     * @hide
     */
    public static final int MESSAGE_ONEWAY_TO_WEARABLE = 0x43847987; // +TOW

    /**
     * The length limit of Association tag.
+49 −5
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.companion;

import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_PING;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING;

import android.content.Context;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
@@ -36,16 +39,22 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Tests that CDM can intake incoming messages in the system data transport and output results.
 *
 * Build/Install/Run: atest CompanionTests:SystemDataTransportTest
 */
public class SystemDataTransportTest extends InstrumentationTestCase {
    private static final String TAG = "SystemDataTransportTest";

    private static final int MESSAGE_INVALID = 0xF00DCAFE;

    private static final int MESSAGE_ONEWAY_INVALID = 0x43434343; // ++++
    private static final int MESSAGE_RESPONSE_INVALID = 0x33333333; // !!!!
    private static final int MESSAGE_REQUEST_INVALID = 0x63636363; // ????
    private static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN

    private static final int MESSAGE_RESPONSE_INVALID = 0x33333333; // !!!!
    private static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
    private static final int MESSAGE_RESPONSE_FAILURE = 0x33706573; // !FAI

@@ -122,8 +131,6 @@ public class SystemDataTransportTest extends InstrumentationTestCase {
        new Random().nextBytes(blob);

        final byte[] input = generatePacket(MESSAGE_REQUEST_PING, /* sequence */ 1, blob);
        final byte[] expected = generatePacket(MESSAGE_RESPONSE_SUCCESS, /* sequence */ 1, blob);
        assertTransportBehavior(input, expected);
    }

    public void testMultiplePingPing() {
@@ -176,6 +183,43 @@ public class SystemDataTransportTest extends InstrumentationTestCase {
        testPingHandRolled();
    }

    public void testInvalidOnewayMessages() throws InterruptedException {
        // Add a callback
        final CountDownLatch received = new CountDownLatch(1);
        mCdm.addOnMessageReceivedListener(Runnable::run, MESSAGE_ONEWAY_INVALID,
                (id, data) -> received.countDown());

        final byte[] input = generatePacket(MESSAGE_ONEWAY_INVALID, /* sequence */ 1);
        final ByteArrayInputStream in = new ByteArrayInputStream(input);
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        mCdm.attachSystemDataTransport(mAssociationId, in, out);

        // Assert that a one-way message was ignored (does not trigger a callback)
        assertFalse(received.await(5, TimeUnit.SECONDS));

        // There should not be a response to one-way messages
        assertEquals(0, out.toByteArray().length);
    }


    public void testOnewayMessages() throws InterruptedException {
        // Add a callback
        final CountDownLatch received = new CountDownLatch(1);
        mCdm.addOnMessageReceivedListener(Runnable::run, MESSAGE_ONEWAY_PING,
                (id, data) -> received.countDown());

        final byte[] input = generatePacket(MESSAGE_ONEWAY_PING, /* sequence */ 1);
        final ByteArrayInputStream in = new ByteArrayInputStream(input);
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        mCdm.attachSystemDataTransport(mAssociationId, in, out);

        // Assert that a one-way message was received
        assertTrue(received.await(1, TimeUnit.SECONDS));

        // There should not be a response to one-way messages
        assertEquals(0, out.toByteArray().length);
    }

    public static byte[] concat(byte[]... blobs) {
        int length = 0;
        for (byte[] blob : blobs) {
+2 −2
Original line number Diff line number Diff line
@@ -130,7 +130,7 @@ public class CompanionTransportManager {
        synchronized (mTransports) {
            for (int i = 0; i < associationIds.length; i++) {
                if (mTransports.contains(associationIds[i])) {
                    mTransports.get(associationIds[i]).requestForResponse(message, data);
                    mTransports.get(associationIds[i]).sendMessage(message, data);
                }
            }
        }
@@ -220,7 +220,7 @@ public class CompanionTransportManager {
            if (transport == null) {
                return CompletableFuture.failedFuture(new IOException("Missing transport"));
            }
            return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
            return transport.sendMessage(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
        }
    }

+75 −1
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.server.companion.transport;

import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_FROM_WEARABLE;
import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_PING;
import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_TO_WEARABLE;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PERMISSION_RESTORE;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING;
@@ -80,6 +83,10 @@ public abstract class Transport {
        return (message & 0xFF000000) == 0x33000000;
    }

    private static boolean isOneway(int message) {
        return (message & 0xFF000000) == 0x43000000;
    }

    @GuardedBy("mPendingRequests")
    protected final SparseArray<CompletableFuture<byte[]>> mPendingRequests =
            new SparseArray<>();
@@ -134,6 +141,42 @@ public abstract class Transport {
    protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
            throws IOException;

    /**
     * Send a message using this transport. If the message was a request, then the returned Future
     * object will complete successfully only if the remote device both received and processed it
     * as expected. If the message was a send-and-forget type message, then the Future object will
     * resolve successfully immediately (with null) upon sending the message.
     *
     * @param message the message type
     * @param data the message payload
     * @return Future object containing the result of the sent message.
     */
    public Future<byte[]> sendMessage(int message, byte[] data) {
        final CompletableFuture<byte[]> pending = new CompletableFuture<>();
        if (isOneway(message)) {
            return sendAndForget(message, data);
        } else if (isRequest(message)) {
            return requestForResponse(message, data);
        } else {
            Slog.w(TAG, "Failed to send message 0x" + Integer.toHexString(message));
            pending.completeExceptionally(new IllegalArgumentException(
                    "The message being sent must be either a one-way or a request."
            ));
        }
        return pending;
    }

    /**
     * @deprecated Method was renamed to sendMessage(int, byte[]) to support both
     * send-and-forget type messages as well as wait-for-response type messages.
     *
     * @param message request message type
     * @param data the message payload
     * @return future object containing the result of the request.
     *
     * @see #sendMessage(int, byte[])
     */
    @Deprecated
    public Future<byte[]> requestForResponse(int message, byte[] data) {
        if (DEBUG) Slog.d(TAG, "Requesting for response");
        final int sequence = mNextSequence.incrementAndGet();
@@ -154,6 +197,20 @@ public abstract class Transport {
        return pending;
    }

    private Future<byte[]> sendAndForget(int message, byte[]data) {
        if (DEBUG) Slog.d(TAG, "Sending a one-way message");
        final CompletableFuture<byte[]> pending = new CompletableFuture<>();

        try {
            sendMessage(message, -1, data);
            pending.complete(null);
        } catch (IOException e) {
            pending.completeExceptionally(e);
        }

        return pending;
    }

    protected final void handleMessage(int message, int sequence, @NonNull byte[] data)
            throws IOException {
        if (DEBUG) {
@@ -162,7 +219,9 @@ public abstract class Transport {
                    + " from association " + mAssociationId);
        }

        if (isRequest(message)) {
        if (isOneway(message)) {
            processOneway(message, data);
        } else if (isRequest(message)) {
            try {
                processRequest(message, sequence, data);
            } catch (IOException e) {
@@ -175,6 +234,21 @@ public abstract class Transport {
        }
    }

    private void processOneway(int message, byte[] data) {
        switch (message) {
            case MESSAGE_ONEWAY_PING:
            case MESSAGE_ONEWAY_FROM_WEARABLE:
            case MESSAGE_ONEWAY_TO_WEARABLE: {
                callback(message, data);
                break;
            }
            default: {
                Slog.w(TAG, "Ignoring unknown message 0x" + Integer.toHexString(message));
                break;
            }
        }
    }

    private void processRequest(int message, int sequence, byte[] data)
            throws IOException {
        switch (message) {