summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java18
-rw-r--r--core/tests/companiontests/src/android/companion/SystemDataTransportTest.java54
-rw-r--r--services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java4
-rw-r--r--services/companion/java/com/android/server/companion/transport/Transport.java76
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) {