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

Commit 5a367457 authored by Joe Antonetti's avatar Joe Antonetti
Browse files

Add API Stubs for RequestHandoff method

This change adds API stubs for requesting a handoff, per the spec outlined in go/handoff-design. Future CLs will implement this functionality and add CTS tests.

Flag: android.companion.enable_task_continuity
Test: Filed API Coverage Bug (listed below)
Bug: 400970610
API-Coverage-Bug: 419601871
Change-Id: I98d590c46e5a4fa8648e6cf335fcd9f1a95afcc5
parent d8c270d9
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -3472,7 +3472,18 @@ package android.companion.datatransfer.continuity {
  @FlaggedApi("android.companion.enable_task_continuity") public class TaskContinuityManager {
    method @NonNull public java.util.List<android.companion.datatransfer.continuity.RemoteTask> getRemoteTasks();
    method public void registerRemoteTaskListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.datatransfer.continuity.TaskContinuityManager.RemoteTaskListener);
    method public void requestHandoff(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.companion.datatransfer.continuity.TaskContinuityManager.HandoffRequestCallback);
    method public void unregisterRemoteTaskListener(@NonNull android.companion.datatransfer.continuity.TaskContinuityManager.RemoteTaskListener);
    field public static final int HANDOFF_REQUEST_RESULT_FAILURE_DEVICE_NOT_FOUND = 5; // 0x5
    field public static final int HANDOFF_REQUEST_RESULT_FAILURE_NO_DATA_PROVIDED_BY_TASK = 2; // 0x2
    field public static final int HANDOFF_REQUEST_RESULT_FAILURE_SENDER_LOST_CONNECTION = 3; // 0x3
    field public static final int HANDOFF_REQUEST_RESULT_FAILURE_TASK_NOT_FOUND = 1; // 0x1
    field public static final int HANDOFF_REQUEST_RESULT_FAILURE_TIMEOUT = 4; // 0x4
    field public static final int HANDOFF_REQUEST_RESULT_SUCCESS = 0; // 0x0
  }
  public static interface TaskContinuityManager.HandoffRequestCallback {
    method public void onHandoffRequestFinished(int, int, int);
  }
  public static interface TaskContinuityManager.RemoteTaskListener {
+26 −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 android.companion.datatransfer.continuity;

import android.companion.datatransfer.continuity.RemoteTask;

/**
 * {@hide}
 */
oneway interface IHandoffRequestCallback {
    void onHandoffRequestFinished(in int associationId, in int remoteTaskId, in int resultCode);
}
 No newline at end of file
+5 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.companion.datatransfer.continuity;

import android.companion.datatransfer.continuity.IHandoffRequestCallback;
import android.companion.datatransfer.continuity.IRemoteTaskListener;
import android.companion.datatransfer.continuity.RemoteTask;

@@ -28,5 +29,9 @@ interface ITaskContinuityManager {
    List<RemoteTask> getRemoteTasks();
    void registerRemoteTaskListener(IRemoteTaskListener listener);
    void unregisterRemoteTaskListener(IRemoteTaskListener listener);
    void requestHandoff(
        in int associationId,
        in int remoteTaskId,
        in IHandoffRequestCallback callback);

}
+130 −0
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package android.companion.datatransfer.continuity;

import android.companion.datatransfer.continuity.IHandoffRequestCallback;
import android.companion.datatransfer.continuity.IRemoteTaskListener;
import android.companion.datatransfer.continuity.RemoteTask;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.NonNull;
@@ -29,10 +31,13 @@ import android.util.ArrayMap;

import com.android.internal.annotations.GuardedBy;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * This class facilitates task continuity between devices owned by the same user.
@@ -51,6 +56,53 @@ public class TaskContinuityManager {

    private final RemoteTaskListenerHolder mListenerHolder;

    /** @hide */
    @IntDef(prefix = {"HANDOFF_REQUEST_RESULT"}, value = {
        HANDOFF_REQUEST_RESULT_SUCCESS,
        HANDOFF_REQUEST_RESULT_FAILURE_TASK_NOT_FOUND,
        HANDOFF_REQUEST_RESULT_FAILURE_NO_DATA_PROVIDED_BY_TASK,
        HANDOFF_REQUEST_RESULT_FAILURE_SENDER_LOST_CONNECTION,
        HANDOFF_REQUEST_RESULT_FAILURE_TIMEOUT,
        HANDOFF_REQUEST_RESULT_FAILURE_DEVICE_NOT_FOUND,
    })

    @Retention(RetentionPolicy.SOURCE)
    public @interface HandoffRequestResultCode {}

    /**
     * Indicate a request for handoff completed successfully.
     */
    public static final int HANDOFF_REQUEST_RESULT_SUCCESS = 0;

    /**
     * Indicates a request for handoff failed because a remote task with the specified ID was not
     * found on the remote device.
     */
    public static final int HANDOFF_REQUEST_RESULT_FAILURE_TASK_NOT_FOUND = 1;

    /**
     * Indicates a request for handoff failed because the remote task did not provide any data to
     * hand itself off to the current device.
     */
    public static final int HANDOFF_REQUEST_RESULT_FAILURE_NO_DATA_PROVIDED_BY_TASK = 2;

    /**
     * Indicates a request for handoff failed because the connection to the remote device was lost
     * before the request could be completed.
     */
    public static final int HANDOFF_REQUEST_RESULT_FAILURE_SENDER_LOST_CONNECTION = 3;

    /**
     * Indicates a request for handoff failed because the request timed out before it could be
     * completed.
     */
    public static final int HANDOFF_REQUEST_RESULT_FAILURE_TIMEOUT = 4;

    /**
     * Indicates a request for handoff failed because the remote device was not found.
     */
    public static final int HANDOFF_REQUEST_RESULT_FAILURE_DEVICE_NOT_FOUND = 5;

    /** @hide */
    public TaskContinuityManager(
        @NonNull Context context,
@@ -73,6 +125,24 @@ public class TaskContinuityManager {
        void onRemoteTasksChanged(@NonNull List<RemoteTask> remoteTasks);
    }

    /**
     * Callback to be invoked when a handoff request is completed.
     */
    public interface HandoffRequestCallback {

        /**
         * Invoked when a request to hand off a remote task has finished.
         *
         * @param associationId The ID of the association to which the remote device is connected.
         * @param remoteTaskId The ID of the task that was requested to be handed off.
         * @param resultCode The result code of the handoff request.
         */
        void onHandoffRequestFinished(
            int associationId,
            int remoteTaskId,
            @HandoffRequestResultCode int resultCode);
    }

    /**
     * Returns a list of tasks currently running on the remote devices owned by the user.
     */
@@ -97,6 +167,9 @@ public class TaskContinuityManager {
        @NonNull Executor executor,
        @NonNull RemoteTaskListener listener) {

        Objects.requireNonNull(executor);
        Objects.requireNonNull(listener);

        try {
            mListenerHolder.registerListener(executor, listener);
            // TODO: joeantonetti - Send an initial notification to the listener after it's
@@ -113,6 +186,8 @@ public class TaskContinuityManager {
     * @param listener The listener to be unregistered.
     */
    public void unregisterRemoteTaskListener(@NonNull RemoteTaskListener listener) {
        Objects.requireNonNull(listener);

        try {
            mListenerHolder.unregisterListener(listener);
        } catch (RemoteException e) {
@@ -120,6 +195,56 @@ public class TaskContinuityManager {
        }
    }

    /**
     * Requests a handoff of the specified remote task to the current device.
     *
     * @param associationId The ID of the association to which the remote device is connected. This
     *                      is the same ID returned by {@link RemoteTask#getDeviceId()}.
     * @param remoteTaskId The remote task to hand off.
     * @param executor The executor to be used to invoke the callback.
     * @param callback The callback to be invoked when the handoff request is finished.
     */
    public void requestHandoff(
        int associationId,
        int remoteTaskId,
        @NonNull Executor executor,
        @NonNull HandoffRequestCallback callback) {

        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);

        try {
            HandoffRequestCallbackHolder callbackHolder
                = new HandoffRequestCallbackHolder(executor, callback);

            mService.requestHandoff(associationId, remoteTaskId, callbackHolder);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    private final class HandoffRequestCallbackHolder extends IHandoffRequestCallback.Stub {
        private final Executor mExecutor;
        private final HandoffRequestCallback mCallback;

        HandoffRequestCallbackHolder(
            @NonNull Executor executor,
            @NonNull HandoffRequestCallback callback) {

            mExecutor = executor;
            mCallback = callback;
        }

        @Override
        public void onHandoffRequestFinished(
            int associationId,
            int remoteTaskId,
            @HandoffRequestResultCode int resultCode) throws RemoteException {
            mExecutor.execute(
                () -> mCallback.onHandoffRequestFinished(associationId, remoteTaskId, resultCode));
        }
    }

    /**
     * Helper class which manages registered listeners and proxies them behind a single
     * IRemoteTaskListener, which is lazily registered with ITaskContinuityManager if there is
@@ -145,6 +270,9 @@ public class TaskContinuityManager {
            @NonNull Executor executor,
            @NonNull RemoteTaskListener listener) throws RemoteException {

            Objects.requireNonNull(executor);
            Objects.requireNonNull(listener);

            synchronized(mListeners) {
                if (!mRegistered) {
                    mService.registerRemoteTaskListener(this);
@@ -163,6 +291,8 @@ public class TaskContinuityManager {
        public void unregisterListener(
            @NonNull RemoteTaskListener listener) throws RemoteException {

            Objects.requireNonNull(listener);

            synchronized(mListeners) {
                mListeners.remove(listener);
                if (mListeners.isEmpty() && mRegistered) {
+10 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.companion.datatransfer.continuity;

import android.annotation.NonNull;
import android.companion.CompanionDeviceManager;
import android.companion.datatransfer.continuity.IHandoffRequestCallback;
import android.companion.datatransfer.continuity.ITaskContinuityManager;
import android.companion.datatransfer.continuity.IRemoteTaskListener;
import android.companion.datatransfer.continuity.RemoteTask;
@@ -83,6 +84,15 @@ public final class TaskContinuityManagerService extends SystemService {
        @Override
        public void unregisterRemoteTaskListener(@NonNull IRemoteTaskListener listener) {
        }

        @Override
        public void requestHandoff(
            int associationId,
            int remoteTaskId,
            @NonNull IHandoffRequestCallback callback) {

            // TODO: joeantonetti - Implement this method.
        }
    }

    private void onTaskContinuityMessageReceived(