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

Commit 7675f2cc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Introduce CompanionSecureCommunicationsManager"

parents 4a0d85e1 da8f9e4c
Loading
Loading
Loading
Loading
+27 −2
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ import java.util.Map;
 * @see CompanionDeviceServiceConnector
 */
@SuppressLint("LongLogTag")
class CompanionApplicationController {
public class CompanionApplicationController {
    static final boolean DEBUG = false;
    private static final String TAG = "CompanionDevice_ApplicationController";

@@ -164,7 +164,10 @@ class CompanionApplicationController {
        }
    }

    boolean isCompanionApplicationBound(@UserIdInt int userId, @NonNull String packageName) {
    /**
     * @return whether the companion application is bound now.
     */
    public boolean isCompanionApplicationBound(@UserIdInt int userId, @NonNull String packageName) {
        synchronized (mBoundCompanionApplications) {
            return mBoundCompanionApplications.containsValueForPackage(userId, packageName);
        }
@@ -234,6 +237,28 @@ class CompanionApplicationController {
        primaryServiceConnector.postOnDeviceDisappeared(association);
    }

    /** Pass an encryped secure message to the companion application for transporting. */
    public void dispatchMessage(@UserIdInt int userId, @NonNull String packageName,
            int associationId, @NonNull byte[] message) {
        if (DEBUG) {
            Log.i(TAG, "dispatchMessage() u" + userId + "/" + packageName
                    + " associationId=" + associationId);
        }

        final CompanionDeviceServiceConnector primaryServiceConnector =
                getPrimaryServiceConnector(userId, packageName);
        if (primaryServiceConnector == null) {
            if (DEBUG) {
                Log.e(TAG, "dispatchMessage(): "
                        + "u" + userId + "/" + packageName + " is NOT bound.");
                Log.d(TAG, "Stacktrace", new Throwable());
            }
            return;
        }

        primaryServiceConnector.postOnMessageDispatchedFromSystem(associationId, message);
    }

    private void onPrimaryServiceBindingDied(@UserIdInt int userId, @NonNull String packageName) {
        if (DEBUG) Log.i(TAG, "onPrimaryServiceBindingDied() u" + userId + "/" + packageName);

+17 −8
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.text.BidiFormatter;
import android.util.ArraySet;
import android.util.Base64;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -102,6 +103,7 @@ import com.android.server.SystemService;
import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
import com.android.server.companion.securechannel.CompanionSecureCommunicationsManager;
import com.android.server.pm.UserManagerInternal;

import java.io.File;
@@ -137,6 +139,7 @@ public class CompanionDeviceManagerService extends SystemService {
    private SystemDataTransferProcessor mSystemDataTransferProcessor;
    private CompanionDevicePresenceMonitor mDevicePresenceMonitor;
    private CompanionApplicationController mCompanionAppController;
    private CompanionSecureCommunicationsManager mSecureCommsManager;

    private final ActivityManagerInternal mAmInternal;
    private final IAppOpsService mAppOpsManager;
@@ -173,6 +176,8 @@ public class CompanionDeviceManagerService extends SystemService {

    @Override
    public void onStart() {
        final Context context = getContext();

        mPersistentStore = new PersistentDataStore();

        loadAssociationsFromDisk();
@@ -185,10 +190,10 @@ public class CompanionDeviceManagerService extends SystemService {
                /* cdmService */this, mAssociationStore);
        mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
                mSystemDataTransferRequestStore);

        final Context context = getContext();
        mCompanionAppController = new CompanionApplicationController(
                context, mApplicationControllerCallback);
        mSecureCommsManager = new CompanionSecureCommunicationsManager(
                mAssociationStore, mCompanionAppController);

        // Publish "binder" service.
        final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl();
@@ -257,7 +262,7 @@ public class CompanionDeviceManagerService extends SystemService {
        if (DEBUG) Log.i(TAG, "onDevice_Appeared_Internal() id=" + associationId);

        final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
        if (DEBUG) Log.d(TAG, "  association=" + associationId);
        if (DEBUG) Log.d(TAG, "  association=" + association);

        if (!association.shouldBindWhenPresent()) return;

@@ -279,7 +284,7 @@ public class CompanionDeviceManagerService extends SystemService {
        if (DEBUG) Log.i(TAG, "onDevice_Disappeared_Internal() id=" + associationId);

        final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
        if (DEBUG) Log.d(TAG, "  association=" + associationId);
        if (DEBUG) Log.d(TAG, "  association=" + association);

        final int userId = association.getUserId();
        final String packageName = association.getPackageName();
@@ -603,9 +608,13 @@ public class CompanionDeviceManagerService extends SystemService {
        }

        @Override
        public void dispatchMessage(int messageId, int associationId, byte[] message)
                throws RemoteException {
            // TODO(b/199427116): implement.
        public void dispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
            if (DEBUG) {
                Log.i(TAG, "dispatchMessage() associationId=" + associationId + "\n"
                        + " message(Base64)=" + Base64.encodeToString(message, 0));
            }

            mSecureCommsManager.receiveSecureMessage(associationId, message);
        }

        @Override
@@ -743,7 +752,7 @@ public class CompanionDeviceManagerService extends SystemService {
                throws RemoteException {
            enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
            new CompanionDeviceShellCommand(
                    CompanionDeviceManagerService.this, mAssociationStore)
                    CompanionDeviceManagerService.this, mAssociationStore, mSecureCommsManager)
                    .exec(this, in, out, err, args, callback, resultReceiver);
        }

+11 −0
Original line number Diff line number Diff line
@@ -98,6 +98,17 @@ 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.
        post(companionService ->
                companionService.onMessageDispatchedFromSystem(
                        /* messageId*/ 0, associationId, message));
    }

    /**
     * Post "unbind" job, which will run *after* all previously posted jobs complete.
     *
+48 −14
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.server.companion;

import static java.nio.charset.StandardCharsets.UTF_8;

import android.companion.AssociationInfo;
import android.os.ShellCommand;
import android.util.Log;
import android.util.Slog;
import android.util.Base64;

import com.android.server.companion.securechannel.CompanionSecureCommunicationsManager;

import java.io.PrintWriter;
import java.util.List;
@@ -29,11 +32,14 @@ class CompanionDeviceShellCommand extends ShellCommand {

    private final CompanionDeviceManagerService mService;
    private final AssociationStore mAssociationStore;
    private final CompanionSecureCommunicationsManager mSecureCommsManager;

    CompanionDeviceShellCommand(CompanionDeviceManagerService service,
            AssociationStore associationStore) {
            AssociationStore associationStore,
            CompanionSecureCommunicationsManager secureCommsManager) {
        mService = service;
        mAssociationStore = associationStore;
        mSecureCommsManager = secureCommsManager;
    }

    @Override
@@ -42,7 +48,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
        try {
            switch (cmd) {
                case "list": {
                    final int userId = getNextArgInt();
                    final int userId = getNextIntArgRequired();
                    final List<AssociationInfo> associationsForUser =
                            mAssociationStore.getAssociationsForUser(userId);
                    for (AssociationInfo association : associationsForUser) {
@@ -55,7 +61,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
                break;

                case "associate": {
                    int userId = getNextArgInt();
                    int userId = getNextIntArgRequired();
                    String packageName = getNextArgRequired();
                    String address = getNextArgRequired();
                    mService.legacyCreateAssociation(userId, address, packageName, null);
@@ -63,7 +69,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
                break;

                case "disassociate": {
                    final int userId = getNextArgInt();
                    final int userId = getNextIntArgRequired();
                    final String packageName = getNextArgRequired();
                    final String address = getNextArgRequired();
                    final AssociationInfo association =
@@ -74,24 +80,50 @@ class CompanionDeviceShellCommand extends ShellCommand {
                }
                break;

                case "clear-association-memory-cache": {
                case "clear-association-memory-cache":
                    mService.persistState();
                    mService.loadAssociationsFromDisk();
                    break;

                case "send-secure-message":
                    final int associationId = getNextIntArgRequired();
                    final byte[] message;

                    // The message should be either a UTF-8 String or Base64-encoded data.
                    final boolean isBase64 = "--base64".equals(getNextOption());
                    if (isBase64) {
                        final String base64encodedMessage = getNextArgRequired();
                        message = Base64.decode(base64encodedMessage, 0);
                    } else {
                        // We treat the rest of the command as the message, which should contain at
                        // least one word (hence getNextArg_Required() below), but there may be
                        // more.
                        final StringBuilder sb = new StringBuilder(getNextArgRequired());
                        // Pick up the rest.
                        for (String word : peekRemainingArgs()) {
                            sb.append(" ").append(word);
                        }
                        // And now convert to byte[]...
                        message = sb.toString().getBytes(UTF_8);
                    }

                    mSecureCommsManager.sendSecureMessage(associationId, message);
                    break;

                default:
                    return handleDefaultCommands(cmd);
            }
            return 0;
        } catch (Throwable t) {
            Slog.e(TAG, "Error running a command: $ " + cmd, t);
            getErrPrintWriter().println(Log.getStackTraceString(t));
        } catch (Throwable e) {
            final PrintWriter errOut = getErrPrintWriter();
            errOut.println();
            errOut.println("Exception occurred while executing '" + cmd + "':");
            e.printStackTrace(errOut);
            return 1;
        }
        return 0;
    }

    private int getNextArgInt() {
    private int getNextIntArgRequired() {
        return Integer.parseInt(getNextArgRequired());
    }

@@ -107,6 +139,8 @@ class CompanionDeviceShellCommand extends ShellCommand {
        pw.println("      Create a new Association.");
        pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
        pw.println("      Remove an existing Association.");
        pw.println("  send-secure-message ASSOCIATION_ID [--base64] MESSAGE");
        pw.println("      Send a secure message to an associated companion device.");
        pw.println("  clear-association-memory-cache");
        pw.println("      Clear the in-memory association cache and reload all association "
                + "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
+87 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.companion.securechannel;

import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.companion.AssociationInfo;
import android.util.Base64;
import android.util.Log;

import com.android.server.companion.AssociationStore;
import com.android.server.companion.CompanionApplicationController;

/** Secure Comms Manager */
@SuppressLint("LongLogTag")
public class CompanionSecureCommunicationsManager {
    static final String TAG = "CompanionDevice_SecureComms";
    static final boolean DEBUG = false;

    private final AssociationStore mAssociationStore;
    private final CompanionApplicationController mCompanionAppController;

    /** Constructor */
    public CompanionSecureCommunicationsManager(AssociationStore associationStore,
            CompanionApplicationController companionApplicationController) {
        mAssociationStore = associationStore;
        mCompanionAppController = companionApplicationController;
    }

    /**
     * Send a data to the associated companion device via secure channel (establishing one if
     * needed).
     * @param associationId associationId of the "recipient" companion device.
     * @param message data to be sent securely.
     */
    public void sendSecureMessage(int associationId, @NonNull byte[] message) {
        if (DEBUG) {
            Log.d(TAG, "sendSecureMessage() associationId=" + associationId + "\n"
                    + "   message (Base64)=\"" + Base64.encodeToString(message, 0) + "\"");
        }

        final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
        if (association == null) {
            throw new IllegalArgumentException(
                    "Association with ID " + associationId + " does not exist");
        }
        if (DEBUG) Log.d(TAG, "  association=" + association);

        final int userId = association.getUserId();
        final String packageName = association.getPackageName();
        if (!mCompanionAppController.isCompanionApplicationBound(userId, packageName)) {
            throw new IllegalStateException("u" + userId + "\\" + packageName + " is NOT bound");
        }

        // TODO(b/202926196): implement: encrypt and pass on the companion application for
        //  transporting
        mCompanionAppController.dispatchMessage(userId, packageName, associationId, message);
    }

    /**
     * Decrypt and dispatch message received from an associated companion device.
     * @param associationId associationId of the "sender" companion device.
     * @param encryptedMessage data.
     */
    public void receiveSecureMessage(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.
    }
}
Loading