diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 3f6713f09ce2e..3f8fc5600eb11 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2784,6 +2784,9 @@ package android.companion.virtual { public final class VirtualDeviceManager { method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams); + field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2 + field public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; // 0x1 + field public static final int LAUNCH_SUCCESS = 0; // 0x0 } public static interface VirtualDeviceManager.ActivityListener { @@ -2791,11 +2794,6 @@ package android.companion.virtual { method public void onTopActivityChanged(int, @NonNull android.content.ComponentName); } - public static interface VirtualDeviceManager.LaunchCallback { - method public void onLaunchFailed(); - method public void onLaunchSuccess(); - } - public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable { method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); @@ -2804,7 +2802,7 @@ package android.companion.virtual { method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); - method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.LaunchCallback); + method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean); } diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 914b321b75061..3bb837317ad4d 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -17,13 +17,13 @@ package android.companion.virtual; import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.app.Activity; import android.app.PendingIntent; import android.companion.AssociationInfo; import android.companion.virtual.audio.VirtualAudioDevice; @@ -48,7 +48,12 @@ import android.os.ResultReceiver; import android.util.ArrayMap; import android.view.Surface; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.concurrent.Executor; +import java.util.function.IntConsumer; /** * System level service for managing virtual devices. @@ -70,6 +75,35 @@ public final class VirtualDeviceManager { | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = "LAUNCH_", + value = { + LAUNCH_SUCCESS, + LAUNCH_FAILURE_PENDING_INTENT_CANCELED, + LAUNCH_FAILURE_NO_ACTIVITY}) + @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) + public @interface PendingIntentLaunchStatus {} + + /** + * Status for {@link VirtualDevice#launchPendingIntent}, indicating that the launch was + * successful. + */ + public static final int LAUNCH_SUCCESS = 0; + + /** + * Status for {@link VirtualDevice#launchPendingIntent}, indicating that the launch failed + * because the pending intent was canceled. + */ + public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; + + /** + * Status for {@link VirtualDevice#launchPendingIntent}, indicating that the launch failed + * because no activity starts were detected as a result of calling the pending intent. + */ + public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; + private final IVirtualDeviceManager mService; private final Context mContext; @@ -173,16 +207,20 @@ public final class VirtualDeviceManager { * intent, the activity will be started on the virtual display using * {@link android.app.ActivityOptions#setLaunchDisplayId}. If the intent is a service or * broadcast intent, an attempt will be made to catch activities started as a result of - * sending the pending intent and move them to the given display. + * sending the pending intent and move them to the given display. When it completes, + * {@code listener} will be called with the status of whether the launch attempt is + * successful or not. * @param executor The executor to run {@code launchCallback} on. - * @param launchCallback Callback that is called when the pending intent launching is - * complete. + * @param listener Listener that is called when the pending intent launching is complete. + * The argument is {@link #LAUNCH_SUCCESS} if the launch successfully started an activity + * on the virtual display, or one of the {@code LAUNCH_FAILED} status explaining why it + * failed. */ public void launchPendingIntent( int displayId, @NonNull PendingIntent pendingIntent, @NonNull Executor executor, - @NonNull LaunchCallback launchCallback) { + @NonNull IntConsumer listener) { try { mVirtualDevice.launchPendingIntent( displayId, @@ -191,13 +229,7 @@ public final class VirtualDeviceManager { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { super.onReceiveResult(resultCode, resultData); - executor.execute(() -> { - if (resultCode == Activity.RESULT_OK) { - launchCallback.onLaunchSuccess(); - } else { - launchCallback.onLaunchFailed(); - } - }); + executor.execute(() -> listener.accept(resultCode)); } }); } catch (RemoteException e) { @@ -439,28 +471,13 @@ public final class VirtualDeviceManager { * {@link #addActivityListener}. * * @param listener The listener to remove. - * @see #addActivityListener(ActivityListener, Executor) + * @see #addActivityListener(Executor, ActivityListener) */ public void removeActivityListener(@NonNull ActivityListener listener) { mActivityListeners.remove(listener); } } - /** - * Callback for launching pending intents on the virtual device. - */ - public interface LaunchCallback { - /** - * Called when the pending intent launched successfully. - */ - void onLaunchSuccess(); - - /** - * Called when the pending intent failed to launch. - */ - void onLaunchFailed(); - } - /** * Listener for activity changes in this virtual device. */ diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 88ad62046b2b9..e4b839af8b2e8 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -33,6 +33,7 @@ import android.app.admin.DevicePolicyManager; import android.companion.AssociationInfo; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; +import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.audio.IAudioConfigChangedCallback; @@ -79,6 +80,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient { private static final String TAG = "VirtualDeviceImpl"; + + /** + * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. + */ + private static final long PENDING_TRAMPOLINE_TIMEOUT_MS = 5000; + private final Object mVirtualDeviceLock = new Object(); private final Context mContext; @@ -195,20 +202,27 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub if (pendingIntent.isActivity()) { try { sendPendingIntent(displayId, pendingIntent); - resultReceiver.send(Activity.RESULT_OK, null); + resultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); } catch (PendingIntent.CanceledException e) { Slog.w(TAG, "Pending intent canceled", e); - resultReceiver.send(Activity.RESULT_CANCELED, null); + resultReceiver.send( + VirtualDeviceManager.LAUNCH_FAILURE_PENDING_INTENT_CANCELED, null); } } else { PendingTrampoline pendingTrampoline = new PendingTrampoline(pendingIntent, resultReceiver, displayId); mPendingTrampolineCallback.startWaitingForPendingTrampoline(pendingTrampoline); + mContext.getMainThreadHandler().postDelayed(() -> { + pendingTrampoline.mResultReceiver.send( + VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); + mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline); + }, PENDING_TRAMPOLINE_TIMEOUT_MS); try { sendPendingIntent(displayId, pendingIntent); } catch (PendingIntent.CanceledException e) { Slog.w(TAG, "Pending intent canceled", e); - resultReceiver.send(Activity.RESULT_CANCELED, null); + resultReceiver.send( + VirtualDeviceManager.LAUNCH_FAILURE_PENDING_INTENT_CANCELED, null); mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline); } } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 96400c1414ec7..9acca8025920c 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -21,7 +21,6 @@ import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_S import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.app.Activity; import android.app.ActivityOptions; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; @@ -29,6 +28,7 @@ import android.companion.CompanionDeviceManager.OnAssociationsChangedListener; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceManager; +import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; import android.content.Context; import android.os.Handler; @@ -64,7 +64,7 @@ public class VirtualDeviceManagerService extends SystemService { private final Object mVirtualDeviceManagerLock = new Object(); private final VirtualDeviceManagerImpl mImpl; - private VirtualDeviceManagerInternal mLocalService; + private final VirtualDeviceManagerInternal mLocalService; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler); /** @@ -115,7 +115,7 @@ public class VirtualDeviceManagerService extends SystemService { if (pt == null) { return null; } - pt.mResultReceiver.send(Activity.RESULT_OK, null); + pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); ActivityOptions options = info.checkedOptions; if (options == null) { options = ActivityOptions.makeBasic(); @@ -314,7 +314,8 @@ public class VirtualDeviceManagerService extends SystemService { pendingTrampoline.mPendingIntent.getCreatorPackage(), pendingTrampoline); if (existing != null) { - existing.mResultReceiver.send(Activity.RESULT_CANCELED, null); + existing.mResultReceiver.send( + VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); } }