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

Commit 7ce70da5 authored by Joe Antonetti's avatar Joe Antonetti Committed by Android (Google) Code Review
Browse files

Merge "Create RemoteTaskStore" into main

parents 5891a5d4 295ef638
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import android.companion.datatransfer.continuity.ITaskContinuityManager;
import android.content.Context;
import android.util.Slog;

import com.android.server.companion.datatransfer.continuity.messages.ContinuityDeviceConnected;
import com.android.server.companion.datatransfer.continuity.messages.TaskContinuityMessage;
import com.android.server.companion.datatransfer.continuity.tasks.RemoteTaskStore;

import com.android.server.SystemService;

@@ -37,11 +39,13 @@ public final class TaskContinuityManagerService extends SystemService {
    private TaskContinuityManagerServiceImpl mTaskContinuityManagerService;
    private TaskBroadcaster mTaskBroadcaster;
    private TaskContinuityMessageReceiver mTaskContinuityMessageReceiver;
    private RemoteTaskStore mRemoteTaskStore;

    public TaskContinuityManagerService(Context context) {
        super(context);
        mTaskBroadcaster = new TaskBroadcaster(context);
        mTaskContinuityMessageReceiver = new TaskContinuityMessageReceiver(context);
        mRemoteTaskStore = new RemoteTaskStore();
    }

    @Override
@@ -61,5 +65,18 @@ public final class TaskContinuityManagerService extends SystemService {
        TaskContinuityMessage taskContinuityMessage) {

        Slog.v(TAG, "Received message from association id: " + associationId);

        switch (taskContinuityMessage.getData()) {
            case ContinuityDeviceConnected continuityDeviceConnected:
                // TODO: joeantonetti - Extract a readable device name and pass it to the store.
                mRemoteTaskStore.registerDevice(
                    associationId,
                    String.format("device-%d", associationId),
                    continuityDeviceConnected.getRemoteTasks());
                break;
            default:
                Slog.w(TAG, "Received unknown message from device: " + associationId);
                break;
        }
    }
}
+84 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.datatransfer.continuity.tasks;

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

import java.util.ArrayList;
import java.util.List;

/**
 * Tracks remote tasks currently available on a specific remote device.
 */
class RemoteDeviceTaskList {
    private final int mAssociationId;
    private final String mDeviceName;
    private List<RemoteTaskInfo> mTasks;

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

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

    /**
     * Returns the association ID of the remote device.
     */
    int getAssociationId() {
        return mAssociationId;
    }

    /**
     * Returns the device name of the remote device.
     */
    String getDeviceName() {
        return mDeviceName;
    }

    /**
     * Adds a task to the list of tasks currently available on the remote
     * device.
     */
    void addTask(RemoteTaskInfo taskInfo) {
        mTasks.add(taskInfo);
    }

    /**
     * Gets the most recently used task on this device, or null if there are no
     * tasks.
     */
    RemoteTaskInfo getMostRecentTask() {
        if (mTasks.isEmpty()) {
            return null;
        }

        RemoteTaskInfo mostRecentTask = mTasks.get(0);
        for (RemoteTaskInfo task : mTasks) {
            if (
                task.getLastUsedTimeMillis()
                    > mostRecentTask.getLastUsedTimeMillis()) {

                mostRecentTask = task;
            }
        }
        return mostRecentTask;
    }
}
 No newline at end of file
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.datatransfer.continuity.tasks;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RemoteTaskStore {
    private final Map<Integer, RemoteDeviceTaskList> mRemoteDeviceTaskLists
        = new HashMap<>();

    public RemoteTaskStore() {}

    /**
     * Registers a device with the task store.
     *
     * @param associationId The ID of the device.
     * @param deviceName The name of the device.
     * @param tasks The list of tasks currently available on the device on first
     * connection.
     */
    public void registerDevice(
        int associationId,
        String deviceName,
        List<RemoteTaskInfo> tasks) {

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

    /**
     * Returns the most recent tasks from all devices in the task store.
     *
     * @return A list of the most recent tasks from all devices in the task
     * store.
     */
    public List<RemoteTaskInfo> getMostRecentTasks() {
        List<RemoteTaskInfo> mostRecentTasks = new ArrayList<>();
        for (RemoteDeviceTaskList taskList : mRemoteDeviceTaskLists.values()) {
            RemoteTaskInfo mostRecentTask = taskList.getMostRecentTask();
            if (mostRecentTask != null) {
                mostRecentTasks.add(mostRecentTask);
            }
        }
        return mostRecentTasks;
    }
}
 No newline at end of file
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.datatransfer.continuity.tasks;

import static com.google.common.truth.Truth.assertThat;

import android.app.ActivityManager;
import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;

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

import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

@Presubmit
@RunWith(AndroidTestingRunner.class)
public class RemoteDeviceTaskListTest {

    @Test
    public void testConstructor_initializesCorrectly() {
        int associationId = 123;
        String deviceName = "device1";
        RemoteTaskInfo expectedTask = createNewRemoteTaskInfo("task1", 100);
        List<RemoteTaskInfo> initialTasks = Arrays.asList(expectedTask);
        RemoteDeviceTaskList taskList = new RemoteDeviceTaskList(
            associationId,
            deviceName,
            initialTasks);

        assertThat(taskList.getMostRecentTask()).isEqualTo(expectedTask);
        assertThat(taskList.getAssociationId()).isEqualTo(associationId);
        assertThat(taskList.getDeviceName()).isEqualTo(deviceName);
    }

    @Test
    public void testAddTask_updatesMostRecentTask() {
        RemoteDeviceTaskList taskList = new RemoteDeviceTaskList(
            0,
            "device name",
            new ArrayList<>());

        RemoteTaskInfo firstAddedTask = createNewRemoteTaskInfo("task2", 200);

        taskList.addTask(firstAddedTask);

        assertThat(taskList.getMostRecentTask()).isEqualTo(firstAddedTask);

        // Add another task with an older timestamp, verify it doesn't update
        // the most recent task.
        RemoteTaskInfo secondAddedTask = createNewRemoteTaskInfo("task1", 100);
        taskList.addTask(secondAddedTask);
        assertThat(taskList.getMostRecentTask()).isEqualTo(firstAddedTask);

        // Add another task with a newer timestamp, verifying it changes the
        // most recently used task.
        RemoteTaskInfo thirdAddedTask = createNewRemoteTaskInfo("task3", 300);
        taskList.addTask(thirdAddedTask);
        assertThat(taskList.getMostRecentTask()).isEqualTo(thirdAddedTask);
    }

    @Test
    public void testGetMostRecentTask_emptyList_returnsNull() {
        RemoteDeviceTaskList taskList = new RemoteDeviceTaskList(
            0,
            "device name",
            new ArrayList<>());

        assertThat(taskList.getMostRecentTask()).isNull();
    }

    @Test
    public void testGetMostRecentTask_multipleTasks_returnsMostRecent() {
        RemoteTaskInfo expectedTask = createNewRemoteTaskInfo("task2", 200);
        int associationId = 123;
        List<RemoteTaskInfo> initialTasks = Arrays.asList(
                createNewRemoteTaskInfo("task1", 100),
                expectedTask,
                createNewRemoteTaskInfo("task3", 150));

        RemoteDeviceTaskList taskList = new RemoteDeviceTaskList(
            associationId,
            "device name",
            initialTasks);

        assertThat(taskList.getMostRecentTask()).isEqualTo(expectedTask);
    }

    private RemoteTaskInfo createNewRemoteTaskInfo(
        String label,
        long lastUsedTimeMillis) {

        ActivityManager.RunningTaskInfo runningTaskInfo
            = new ActivityManager.RunningTaskInfo();

        runningTaskInfo.taskId = 1;
        runningTaskInfo.taskDescription
            = new ActivityManager.TaskDescription(label);
        runningTaskInfo.lastActiveTime = lastUsedTimeMillis;
        return new RemoteTaskInfo(runningTaskInfo);
    }
}
 No newline at end of file
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.datatransfer.continuity.tasks;

import static com.google.common.truth.Truth.assertThat;

import android.app.ActivityManager;
import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;

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

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

@Presubmit
@RunWith(AndroidTestingRunner.class)
public class RemoteTaskStoreTest {

    private RemoteTaskStore taskStore;

    @Before
    public void setUp() {
        taskStore = new RemoteTaskStore();
    }

    @Test
    public void testRegisterDevice_addsDeviceToStore() {
        RemoteTaskInfo expectedTask = createNewRemoteTaskInfo("task1", 100);

        taskStore.registerDevice(0, "device name", Arrays.asList(expectedTask));

        assertThat(taskStore.getMostRecentTasks()).containsExactly(expectedTask);
    }

    @Test
    public void testGetMostRecentTasks_emptyStore_returnsEmptyList() {
        assertThat(taskStore.getMostRecentTasks()).isEmpty();
    }

    @Test
    public void testGetMostRecentTasks_multipleDevices_returnsMostRecentFromEach() {
        RemoteTaskInfo expectedTaskFromDevice1 = createNewRemoteTaskInfo("task1", 100);
        RemoteTaskInfo expectedTaskFromDevice2 = createNewRemoteTaskInfo("task2", 200);

        taskStore.registerDevice(
            0,
            "device1",
            Arrays.asList(expectedTaskFromDevice1, createNewRemoteTaskInfo("task2", 1)));

        taskStore.registerDevice(
            1,
            "device2",
            Arrays.asList(expectedTaskFromDevice2, createNewRemoteTaskInfo("task2", 1)));

        assertThat(taskStore.getMostRecentTasks())
            .containsExactly(expectedTaskFromDevice1, expectedTaskFromDevice2);
    }

    @Test
    public void testGetMostRecentTasks_deviceWithNoTasks_returnsEmptyList() {
        taskStore.registerDevice(0, "device name", new ArrayList<>());

        assertThat(taskStore.getMostRecentTasks()).isEmpty();
    }

    private RemoteTaskInfo createNewRemoteTaskInfo(String label, long lastUsedTimeMillis) {
        ActivityManager.RunningTaskInfo runningTaskInfo = new ActivityManager.RunningTaskInfo();
        runningTaskInfo.taskId = 1;
        runningTaskInfo.taskDescription = new ActivityManager.TaskDescription(label);
        runningTaskInfo.lastActiveTime = lastUsedTimeMillis;
        return new RemoteTaskInfo(runningTaskInfo);
    }
}
 No newline at end of file