diff options
4 files changed, 144 insertions, 8 deletions
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index e0ce917fa3b3..0105346c147f 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -221,6 +221,12 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) @TestApi 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. * * @hide @@ -244,6 +250,18 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) @SystemApi(client = MODULE_LIBRARIES) 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. diff --git a/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java b/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java index 2b4123af3885..73d7fe952af3 100644 --- a/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java +++ b/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java @@ -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) { diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index f648f09ea43d..5ef984b3de53 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -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); } } diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java index 32d4061db1f7..22b18ac9653b 100644 --- a/services/companion/java/com/android/server/companion/transport/Transport.java +++ b/services/companion/java/com/android/server/companion/transport/Transport.java @@ -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) { |