summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/companion/java/com/android/server/companion/CompanionApplicationController.java17
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java10
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java11
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java3
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/CompanionMessageProcessor.java103
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java64
-rw-r--r--services/companion/java/com/android/server/companion/securechannel/CompanionSecureCommunicationsManager.java44
7 files changed, 177 insertions, 75 deletions
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index 943599cd83c7..0deb03923663 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -103,7 +103,10 @@ public class CompanionApplicationController {
mCompanionServicesRegister.invalidate(userId);
}
- void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName,
+ /**
+ * CDM binds to the companion app.
+ */
+ public void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName,
boolean bindImportant) {
if (DEBUG) {
Log.i(TAG, "bind() u" + userId + "/" + packageName
@@ -143,7 +146,10 @@ public class CompanionApplicationController {
}
}
- void unbindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
+ /**
+ * CDM unbinds the companion app.
+ */
+ public void unbindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName);
final List<CompanionDeviceServiceConnector> serviceConnectors;
@@ -237,9 +243,9 @@ public class CompanionApplicationController {
primaryServiceConnector.postOnDeviceDisappeared(association);
}
- /** Pass an encryped secure message to the companion application for transporting. */
+ /** Pass an encrypted secure message to the companion application for transporting. */
public void dispatchMessage(@UserIdInt int userId, @NonNull String packageName,
- int associationId, @NonNull byte[] message) {
+ int associationId, int messageId, @NonNull byte[] message) {
if (DEBUG) {
Log.i(TAG, "dispatchMessage() u" + userId + "/" + packageName
+ " associationId=" + associationId);
@@ -256,7 +262,8 @@ public class CompanionApplicationController {
return;
}
- primaryServiceConnector.postOnMessageDispatchedFromSystem(associationId, message);
+ primaryServiceConnector.postOnMessageDispatchedFromSystem(associationId, messageId,
+ message);
}
private void onPrimaryServiceBindingDied(@UserIdInt int userId, @NonNull String packageName) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index fc3a12dd5fb7..ca4f76d8f416 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -626,7 +626,15 @@ public class CompanionDeviceManagerService extends SystemService {
+ " message(Base64)=" + Base64.encodeToString(message, 0));
}
- mSecureCommsManager.receiveSecureMessage(associationId, message);
+ AssociationInfo association = getAssociationWithCallerChecks(associationId);
+ if (association == null) {
+ throw new IllegalArgumentException("Association with ID " + associationId + " "
+ + "does not exist "
+ + "or belongs to a different package "
+ + "or belongs to a different user");
+ }
+
+ mSecureCommsManager.receiveSecureMessage(messageId, associationId, message);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index ef39b4b13f22..b9b29ffb4e86 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -98,15 +98,10 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
post(companionService -> companionService.onDeviceDisappeared(associationInfo));
}
- void postOnMessageDispatchedFromSystem(int associationId, @NonNull byte[] message) {
- // We always use messageId 0 (at least for now).
- // Unlike the message itself, the messageId is not encoded, which means that the CDM on the
- // other (receiving) end CAN NOT and MUST NOT trust this messageId.
- // If CDM needs to pass messageId around to the other side - it should embed it in the
- // message body.
+ void postOnMessageDispatchedFromSystem(int associationId, int messageId,
+ @NonNull byte[] message) {
post(companionService ->
- companionService.onMessageDispatchedFromSystem(
- /* messageId*/ 0, associationId, message));
+ companionService.onMessageDispatchedFromSystem(messageId, associationId, message));
}
/**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index c071bf640bd8..3d8e87ba6378 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -107,7 +107,8 @@ class CompanionDeviceShellCommand extends ShellCommand {
message = sb.toString().getBytes(UTF_8);
}
- mSecureCommsManager.sendSecureMessage(associationId, message);
+ mSecureCommsManager.sendSecureMessage(associationId, /* messageId */ 0,
+ message);
break;
default:
diff --git a/services/companion/java/com/android/server/companion/datatransfer/CompanionMessageProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/CompanionMessageProcessor.java
index 826bafa05441..e83e634ea859 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/CompanionMessageProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/CompanionMessageProcessor.java
@@ -16,6 +16,8 @@
package com.android.server.companion.datatransfer;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -39,6 +41,12 @@ public class CompanionMessageProcessor {
private static final String LOG_TAG = CompanionMessageProcessor.class.getSimpleName();
+ /** Listener for incoming complete messages. */
+ interface Listener {
+ /** When a complete message is received from the companion app. */
+ void onCompleteMessageReceived(@NonNull CompanionMessageInfo message);
+ }
+
// Rough size for each CompanionMessage, each message can exceed 50K for a little, but not
// too much. Hard limit is 100K, WCS data processing limit. Closer to 100K, less stable at
// the WCS data processing layer. Refer to
@@ -48,6 +56,9 @@ public class CompanionMessageProcessor {
private final CompanionSecureCommunicationsManager mSecureCommsManager;
+ @Nullable
+ private Listener mListener;
+
// Association id -> (parent id -> received messages)
private final Map<Integer, Map<Integer, List<CompanionMessageInfo>>> mAssociationsMessagesMap =
new HashMap<>();
@@ -56,6 +67,11 @@ public class CompanionMessageProcessor {
public CompanionMessageProcessor(CompanionSecureCommunicationsManager secureCommsManager) {
mSecureCommsManager = secureCommsManager;
+ mSecureCommsManager.setListener(this::onDecryptedMessageReceived);
+ }
+
+ public void setListener(@NonNull Listener listener) {
+ mListener = listener;
}
/**
@@ -72,7 +88,8 @@ public class CompanionMessageProcessor {
for (int i = 0; i < totalMessageCount; i++) {
ProtoOutputStream proto = new ProtoOutputStream();
- proto.write(CompanionMessage.ID, parentMessageId + i + 1);
+ int messageId = parentMessageId + i + 1;
+ proto.write(CompanionMessage.ID, messageId);
long paginationInfoToken = proto.start(CompanionMessage.PAGINATION_INFO);
proto.write(CompanionMessage.PaginationInfo.PARENT_ID, parentMessageId);
@@ -87,29 +104,68 @@ public class CompanionMessageProcessor {
Slog.i(LOG_TAG, "Sending " + currentData.length + " bytes to " + packageName);
- mSecureCommsManager.sendSecureMessage(associationId, proto.getBytes());
+ mSecureCommsManager.sendSecureMessage(associationId, messageId, proto.getBytes());
}
}
/**
- * Process message and store it. If all the messages with the same parent id have been received,
- * return the message with combined message data. Otherwise, return null if there's still data
- * parts missing.
+ * Process the message and store it. If all the messages with the same parent id have been
+ * received, return the message with combined message data. Otherwise, return null if there's
+ * still data parts missing.
*/
- public CompanionMessageInfo processMessage(int messageId, int associationId, byte[] message) {
+ public CompanionMessageInfo onDecryptedMessageReceived(int messageId, int associationId,
+ byte[] message) {
ProtoInputStream proto = new ProtoInputStream(message);
try {
- int id = proto.readInt(CompanionMessage.ID);
- if (id == messageId) {
- // Read proto data
- long paginationToken = proto.start(CompanionMessage.PAGINATION_INFO);
- int parentId = proto.readInt(CompanionMessage.PaginationInfo.PARENT_ID);
- int page = proto.readInt(CompanionMessage.PaginationInfo.PAGE);
- int total = proto.readInt(CompanionMessage.PaginationInfo.TOTAL);
- proto.end(paginationToken);
- int type = proto.readInt(CompanionMessage.TYPE);
- byte[] data = proto.readBytes(CompanionMessage.DATA);
+ int id = 0;
+ int parentId = 0;
+ int page = 0;
+ int total = 0;
+ int type = CompanionMessage.UNKNOWN;
+ byte[] data = null;
+
+ // Read proto data
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) CompanionMessage.ID:
+ id = proto.readInt(CompanionMessage.ID);
+ break;
+ case (int) CompanionMessage.PAGINATION_INFO:
+ long paginationToken = proto.start(CompanionMessage.PAGINATION_INFO);
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) CompanionMessage.PaginationInfo.PARENT_ID:
+ parentId = proto.readInt(
+ CompanionMessage.PaginationInfo.PARENT_ID);
+ break;
+ case (int) CompanionMessage.PaginationInfo.PAGE:
+ page = proto.readInt(CompanionMessage.PaginationInfo.PAGE);
+ break;
+ case (int) CompanionMessage.PaginationInfo.TOTAL:
+ total = proto.readInt(CompanionMessage.PaginationInfo.TOTAL);
+ break;
+ default:
+ Slog.e(LOG_TAG, "Unexpected field id "
+ + proto.getFieldNumber() + " for PaginationInfo.");
+ break;
+ }
+ }
+ proto.end(paginationToken);
+ break;
+ case (int) CompanionMessage.TYPE:
+ type = proto.readInt(CompanionMessage.TYPE);
+ break;
+ case (int) CompanionMessage.DATA:
+ data = proto.readBytes(CompanionMessage.DATA);
+ break;
+ default:
+ Slog.e(LOG_TAG, "Unexpected field id " + proto.getFieldNumber()
+ + " for CompanionMessage.");
+ break;
+ }
+ }
+ if (id == messageId) {
CompanionMessageInfo messageInfo = new CompanionMessageInfo(id, page, total, type,
data);
// Add the message into mAssociationsMessagesMap
@@ -122,28 +178,35 @@ public class CompanionMessageProcessor {
mAssociationsMessagesMap.put(associationId, associationMessages);
// Check if all the messages with the same parentId are received.
if (childMessages.size() == total) {
+ Slog.i(LOG_TAG, "All [" + total + "] messages are received. Processing.");
+
childMessages.sort(Comparator.comparing(CompanionMessageInfo::getPage));
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (int i = 0; i < childMessages.size(); i++) {
stream.write(childMessages.get(i).getData());
}
mAssociationsMessagesMap.remove(parentId);
- return new CompanionMessageInfo(parentId, 0, total, type, stream.toByteArray());
+ mListener.onCompleteMessageReceived(
+ new CompanionMessageInfo(parentId, 0, total, type,
+ stream.toByteArray()));
+ } else {
+ Slog.i(LOG_TAG, "[" + childMessages.size() + "/" + total
+ + "] messages are received for parentId [" + parentId + "]");
}
} else {
Slog.e(LOG_TAG, "Message id mismatch.");
return null;
}
} catch (IOException e) {
- Slog.e(LOG_TAG, "Can't read proto message id: " + messageId + ", message: "
- + new String(message) + ".");
+ Slog.e(LOG_TAG, "Can't read proto from the message.");
return null;
}
return null;
}
/**
- * Find the next parent id. The parent and child ids are incremental.
+ * Find the next parent id from [1, Integer.MAX_VALUE].
+ * The parent and child ids are incremental.
*/
private int findNextParentId(int associationId, int totalMessageCount) {
int nextParentId = mNextParentId.getOrDefault(associationId, 1);
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index cafa78f7e2ab..d39fa2acd978 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -26,6 +26,7 @@ import static com.android.server.companion.Utils.prepareForIpc;
import static java.nio.charset.StandardCharsets.UTF_8;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.companion.AssociationInfo;
@@ -91,6 +92,7 @@ public class SystemDataTransferProcessor {
mAssociationStore = associationStore;
mSystemDataTransferRequestStore = systemDataTransferRequestStore;
mCompanionMessageProcessor = companionMessageProcessor;
+ mCompanionMessageProcessor.setListener(this::onCompleteMessageReceived);
}
/**
@@ -201,44 +203,36 @@ public class SystemDataTransferProcessor {
}
/**
- * Process message reported by the companion app.
+ * Process a complete decrypted message reported by the companion app.
*/
- public void processMessage(String packageName, int userId, int associationId,
- int messageId, byte[] message) {
- Slog.i(LOG_TAG, "Start processing message [" + messageId + "] from package ["
- + packageName + "] userId [" + userId + "] associationId [" + associationId + "]");
-
- AssociationInfo association = mAssociationStore.getAssociationById(associationId);
- association = PermissionsUtils.sanitizeWithCallerChecks(mContext, association);
- if (association == null) {
- throw new DeviceNotAssociatedException("Association "
- + associationId + " is not associated with the app " + packageName
- + " for user " + userId);
+ public void onCompleteMessageReceived(@NonNull CompanionMessageInfo completeMessage) {
+ switch (completeMessage.getType()) {
+ case CompanionMessage.PERMISSION_SYNC:
+ processPermissionSyncMessage(completeMessage);
+ break;
+ default:
+ Slog.e(LOG_TAG, "Unknown message type [" + completeMessage.getType()
+ + "]. Unable to process.");
}
+ }
- PermissionsUtils.enforceCallerIsSystemOr(userId, packageName);
-
- CompanionMessageInfo completeMessage = mCompanionMessageProcessor.processMessage(messageId,
- associationId, message);
- if (completeMessage != null) {
- if (completeMessage.getType() == CompanionMessage.PERMISSION_SYNC) {
- // Start applying permissions
- BackupHelper backupHelper = new BackupHelper(mContext, UserHandle.of(userId));
- try {
- XmlPullParser parser = Xml.newPullParser();
- ByteArrayInputStream stream = new ByteArrayInputStream(
- completeMessage.getData());
- parser.setInput(stream, UTF_8.name());
-
- backupHelper.restoreState(parser);
- } catch (IOException e) {
- Slog.e(LOG_TAG, "IOException reading message: "
- + new String(completeMessage.getData()));
- } catch (XmlPullParserException e) {
- Slog.e(LOG_TAG, "Error parsing message: "
- + new String(completeMessage.getData()));
- }
- }
+ private void processPermissionSyncMessage(CompanionMessageInfo messageInfo) {
+ Slog.i(LOG_TAG, "Applying permissions.");
+ // Start applying permissions
+ BackupHelper backupHelper = new BackupHelper(mContext, mContext.getUser());
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ ByteArrayInputStream stream = new ByteArrayInputStream(
+ messageInfo.getData());
+ parser.setInput(stream, UTF_8.name());
+
+ backupHelper.restoreState(parser);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "IOException reading message: "
+ + new String(messageInfo.getData()));
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Error parsing message: "
+ + new String(messageInfo.getData()));
}
}
diff --git a/services/companion/java/com/android/server/companion/securechannel/CompanionSecureCommunicationsManager.java b/services/companion/java/com/android/server/companion/securechannel/CompanionSecureCommunicationsManager.java
index 625360ebd417..16a74ecd2068 100644
--- a/services/companion/java/com/android/server/companion/securechannel/CompanionSecureCommunicationsManager.java
+++ b/services/companion/java/com/android/server/companion/securechannel/CompanionSecureCommunicationsManager.java
@@ -17,10 +17,12 @@
package com.android.server.companion.securechannel;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.companion.AssociationInfo;
import android.util.Base64;
import android.util.Log;
+import android.util.Slog;
import com.android.server.companion.AssociationStore;
import com.android.server.companion.CompanionApplicationController;
@@ -31,9 +33,18 @@ public class CompanionSecureCommunicationsManager {
static final String TAG = "CompanionDevice_SecureComms";
static final boolean DEBUG = false;
+ /** Listener for incoming decrypted messages. */
+ public interface Listener {
+ /** When an incoming message is decrypted. */
+ void onDecryptedMessageReceived(int messageId, int associationId, byte[] message);
+ }
+
private final AssociationStore mAssociationStore;
private final CompanionApplicationController mCompanionAppController;
+ @Nullable
+ private Listener mListener;
+
/** Constructor */
public CompanionSecureCommunicationsManager(AssociationStore associationStore,
CompanionApplicationController companionApplicationController) {
@@ -41,13 +52,18 @@ public class CompanionSecureCommunicationsManager {
mCompanionAppController = companionApplicationController;
}
+ public void setListener(@NonNull Listener listener) {
+ mListener = listener;
+ }
+
/**
* Send a data to the associated companion device via secure channel (establishing one if
* needed).
* @param associationId associationId of the "recipient" companion device.
+ * @param messageId id of the message
* @param message data to be sent securely.
*/
- public void sendSecureMessage(int associationId, @NonNull byte[] message) {
+ public void sendSecureMessage(int associationId, int messageId, @NonNull byte[] message) {
if (DEBUG) {
Log.d(TAG, "sendSecureMessage() associationId=" + associationId + "\n"
+ " message (Base64)=\"" + Base64.encodeToString(message, 0) + "\"");
@@ -62,13 +78,28 @@ public class CompanionSecureCommunicationsManager {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
+
+ // Bind to the app if it hasn't been bound.
if (!mCompanionAppController.isCompanionApplicationBound(userId, packageName)) {
- throw new IllegalStateException("u" + userId + "\\" + packageName + " is NOT bound");
+ Slog.d(TAG, "userId [" + userId + "] packageName [" + packageName
+ + "] is not bound. Binding it now to send a secure message.");
+ mCompanionAppController.bindCompanionApplication(userId, packageName,
+ association.isSelfManaged());
+
+ // TODO(b/202926196): implement: encrypt and pass on the companion application for
+ // transporting
+ mCompanionAppController.dispatchMessage(userId, packageName, associationId, messageId,
+ message);
+
+ Slog.d(TAG, "Unbinding userId [" + userId + "] packageName [" + packageName
+ + "]");
+ mCompanionAppController.unbindCompanionApplication(userId, packageName);
}
// TODO(b/202926196): implement: encrypt and pass on the companion application for
// transporting
- mCompanionAppController.dispatchMessage(userId, packageName, associationId, message);
+ mCompanionAppController.dispatchMessage(userId, packageName, associationId, messageId,
+ message);
}
/**
@@ -76,12 +107,15 @@ public class CompanionSecureCommunicationsManager {
* @param associationId associationId of the "sender" companion device.
* @param encryptedMessage data.
*/
- public void receiveSecureMessage(int associationId, @NonNull byte[] encryptedMessage) {
+ public void receiveSecureMessage(int messageId, int associationId,
+ @NonNull byte[] encryptedMessage) {
if (DEBUG) {
Log.d(TAG, "sendSecureMessage() associationId=" + associationId + "\n"
+ " message (Base64)=\"" + Base64.encodeToString(encryptedMessage, 0) + "\"");
}
- // TODO(b/202926196): implement: decrypt and dispatch.
+ // TODO(b/202926196): implement: decrypt and dispatch
+
+ mListener.onDecryptedMessageReceived(messageId, associationId, encryptedMessage);
}
}