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

Commit 78628b69 authored by Joe Antonetti's avatar Joe Antonetti
Browse files

Remove Devices from RemoteTaskStore when the transport is lost

* Update ConnectedAssociationStore to expose full information on an association, so RemoteTaskStore can retrieve the device name.
* Subscribe RemoteTaskStore to ConnectedAssociationStore
* Remove entries from RemoteTaskStore when the device disconnects, as notified by ConnectedAssociationStore.

Flag: android.companion.enable_task_continuity
Test: Added unit tests
Bug: 400970610
Change-Id: If8d91943937e7026ba23a8e7ac24fd4f03ed26bb
parent 04b85b60
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_TASK_CONTINUITY;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
@@ -35,6 +36,7 @@ import com.android.server.companion.datatransfer.continuity.messages.TaskContinu
import com.android.server.companion.datatransfer.continuity.messages.TaskContinuityMessageData;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -97,12 +99,11 @@ class TaskBroadcaster
    }

    @Override
    public void onTransportConnected(int associationId) {
    public void onTransportConnected(AssociationInfo associationInfo) {
        Slog.v(
            TAG,
            "Transport connected for association id: " + associationId);

        sendDeviceConnectedMessage(associationId);
            "Transport connected for association id: " + associationInfo.getId());
        sendDeviceConnectedMessage(associationInfo.getId());
    }

    @Override
@@ -178,15 +179,15 @@ class TaskBroadcaster
    private void sendMessageToAllConnectedAssociations(
        TaskContinuityMessageData data) {

        Set<Integer> connectedAssociations
        Collection<AssociationInfo> connectedAssociations
            = mConnectedAssociationStore.getConnectedAssociations();

        Slog.v(
            TAG,
            "Sending message to " + connectedAssociations.size() + " associations.");

        for (Integer associationId : connectedAssociations) {
            sendMessage(associationId, data);
        for (AssociationInfo associationInfo : connectedAssociations) {
            sendMessage(associationInfo.getId(), data);
        }
    }

+2 −4
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ public final class TaskContinuityManagerService extends SystemService {
            mConnectedAssociationStore);

        mTaskContinuityMessageReceiver = new TaskContinuityMessageReceiver(context);
        mRemoteTaskStore = new RemoteTaskStore();
        mRemoteTaskStore = new RemoteTaskStore(mConnectedAssociationStore);
    }

    @Override
@@ -76,10 +76,8 @@ public final class TaskContinuityManagerService extends SystemService {

        switch (taskContinuityMessage.getData()) {
            case ContinuityDeviceConnected continuityDeviceConnected:
                // TODO: joeantonetti - Extract a readable device name and pass it to the store.
                mRemoteTaskStore.registerDevice(
                mRemoteTaskStore.setTasks(
                    associationId,
                    String.format("device-%d", associationId),
                    continuityDeviceConnected.getRemoteTasks());
                break;
            default:
+25 −17
Original line number Diff line number Diff line
@@ -20,26 +20,26 @@ import android.annotation.NonNull;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.os.Handler;
import android.util.Log;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

public class ConnectedAssociationStore {

    private static final String TAG = "ConnectedAssociationStore";

    private final CompanionDeviceManager mCompanionDeviceManager;
    private final Set<Integer> mConnectedAssociations = new HashSet<>();
    private final Map<Integer, AssociationInfo> mConnectedAssociations = new HashMap<>();
    private final List<Observer> mObservers = new ArrayList<>();

    public interface Observer {
        void onTransportConnected(int associationId);
        void onTransportConnected(AssociationInfo associationInfo);
        void onTransportDisconnected(int associationId);
    }

@@ -61,18 +61,22 @@ public class ConnectedAssociationStore {
        mObservers.remove(observer);
    }

    public Set<Integer> getConnectedAssociations() {
        return mConnectedAssociations;
    public Collection<AssociationInfo> getConnectedAssociations() {
        return mConnectedAssociations.values();
    }

    public AssociationInfo getConnectedAssociationById(int associationId) {
        return mConnectedAssociations.get(associationId);
    }

    private void onTransportsChanged(List<AssociationInfo> associationInfos) {
        Set<Integer> removedAssociations
            = new HashSet<>(mConnectedAssociations);
            = new HashSet<>(mConnectedAssociations.keySet());

        Set<Integer> addedAssociations = new HashSet<>();
        Set<AssociationInfo> addedAssociations = new HashSet<>();
        for (AssociationInfo associationInfo : associationInfos) {
            if (!mConnectedAssociations.contains(associationInfo.getId())) {
                addedAssociations.add(associationInfo.getId());
            if (!mConnectedAssociations.containsKey(associationInfo.getId())) {
                addedAssociations.add(associationInfo);
            }

            if (removedAssociations.contains(associationInfo.getId())) {
@@ -80,23 +84,27 @@ public class ConnectedAssociationStore {
            }
        }

        mConnectedAssociations.addAll(addedAssociations);
        mConnectedAssociations.removeAll(removedAssociations);

        for (Integer associationId : removedAssociations) {
            Log.i(
                TAG,
                "Transport disconnected for association: " + associationId);

            mConnectedAssociations.remove(associationId);

            for (Observer observer : mObservers) {
                observer.onTransportDisconnected(associationId);
            }
        }

        for (Integer associationId : addedAssociations) {
            Log.i(TAG, "Transport connected for association: " + associationId);
        for (AssociationInfo associationInfo : addedAssociations) {
            Log.i(
                TAG,
                "Transport connected for association: " + associationInfo.getId());

            mConnectedAssociations.put(associationInfo.getId(), associationInfo);

            for (Observer observer : mObservers) {
                observer.onTransportConnected(associationId);
                observer.onTransportConnected(associationInfo);
            }
        }
    }
+10 −4
Original line number Diff line number Diff line
@@ -27,16 +27,14 @@ import java.util.List;
class RemoteDeviceTaskList {
    private final int mAssociationId;
    private final String mDeviceName;
    private List<RemoteTaskInfo> mTasks;
    private final List<RemoteTaskInfo> mTasks = new ArrayList<>();

    RemoteDeviceTaskList(
        int associationId,
        String deviceName,
        List<RemoteTaskInfo> tasks) {
        String deviceName) {

        mAssociationId = associationId;
        mDeviceName = deviceName;
        mTasks = new ArrayList<>(tasks);
    }

    /**
@@ -61,6 +59,14 @@ class RemoteDeviceTaskList {
        mTasks.add(taskInfo);
    }

    /**
     * Sets the list of tasks currently available on the remote device.
     */
    void setTasks(List<RemoteTaskInfo> tasks) {
        mTasks.clear();
        mTasks.addAll(tasks);
    }

    /**
     * Gets the most recently used task on this device, or null if there are no
     * tasks.
+69 −16
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@
 */
package com.android.server.companion.datatransfer.continuity.tasks;

import android.companion.AssociationInfo;
import android.util.Slog;

import com.android.server.companion.datatransfer.continuity.connectivity.ConnectedAssociationStore;
import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskInfo;

import java.util.ArrayList;
@@ -22,28 +26,42 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RemoteTaskStore {
public class RemoteTaskStore implements ConnectedAssociationStore.Observer {

    private static final String TAG = "RemoteTaskStore";

    private final ConnectedAssociationStore mConnectedAssociationStore;
    private final Map<Integer, RemoteDeviceTaskList> mRemoteDeviceTaskLists
        = new HashMap<>();

    public RemoteTaskStore() {}
    public RemoteTaskStore(
        ConnectedAssociationStore connectedAssociationStore) {

        mConnectedAssociationStore = connectedAssociationStore;
        mConnectedAssociationStore.addObserver(this);
    }

    /**
     * Registers a device with the task store.
     * Sets the task list of the given association id to the given tasks.
     *
     * @param associationId The ID of the device.
     * @param deviceName The name of the device.
     * @param associationId The association id of the device.
     * @param tasks The list of tasks currently available on the device on first
     * connection.
     */
    public void registerDevice(
    public void setTasks(
        int associationId,
        String deviceName,
        List<RemoteTaskInfo> tasks) {
        synchronized (mRemoteDeviceTaskLists) {
            if (!mRemoteDeviceTaskLists.containsKey(associationId)) {
                Slog.e(
                    TAG,
                    "Attempted to set tasks for association: " + associationId + " which is not connected.");

        RemoteDeviceTaskList taskList
            = new RemoteDeviceTaskList(associationId, deviceName, tasks);
        mRemoteDeviceTaskLists.put(associationId, taskList);
                return;
            }

            mRemoteDeviceTaskLists.get(associationId).setTasks(tasks);
        }
    }

    /**
@@ -53,6 +71,7 @@ public class RemoteTaskStore {
     * store.
     */
    public List<RemoteTaskInfo> getMostRecentTasks() {
        synchronized (mRemoteDeviceTaskLists) {
            List<RemoteTaskInfo> mostRecentTasks = new ArrayList<>();
            for (RemoteDeviceTaskList taskList : mRemoteDeviceTaskLists.values()) {
                RemoteTaskInfo mostRecentTask = taskList.getMostRecentTask();
@@ -63,3 +82,37 @@ public class RemoteTaskStore {
            return mostRecentTasks;
        }
    }

    @Override
    public void onTransportConnected(AssociationInfo associationInfo) {
        synchronized (mRemoteDeviceTaskLists) {
            if (!mRemoteDeviceTaskLists.containsKey(associationInfo.getId())) {
                Slog.v(
                    TAG,
                    "Creating new RemoteDeviceTaskList for association: " + associationInfo.getId());

                RemoteDeviceTaskList taskList
                    = new RemoteDeviceTaskList(
                        associationInfo.getId(),
                        associationInfo.getDisplayName().toString());

                mRemoteDeviceTaskLists.put(associationInfo.getId(), taskList);
            } else {
                Slog.v(
                    TAG,
                    "Transport already connected for association: " + associationInfo.getId());
            }
        }
    }

    @Override
    public void onTransportDisconnected(int associationId) {
        synchronized (mRemoteDeviceTaskLists) {
            Slog.v(
                TAG,
                "Deleting RemoteDeviceTaskList for association: " + associationId);

            mRemoteDeviceTaskLists.remove(associationId);
        }
    }
}
 No newline at end of file
Loading