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

Commit 4d4b6c47 authored by Caitlin Cassidy's avatar Caitlin Cassidy
Browse files

[Media TTT] Don't use futures for determining whether a transfer has

succeeded or not.

After discussing with davidsamuelson@, we decided that using futures to
communicate across processes was error-prone. Specifically, on SysUI
we'll switch to a failure state if we don't get a response from the
future after a certain amount of time. However, it's possible that the
transfer that D2DI kicks off ends up succeeding after we show the
failure chip, so the user would get incorrect information.

To avoid this, we're just going to have D2DI notify us when the transfer
starts and when it succeeds or fails.

Bug: 203800643
Bug: 203800347
Test: verify `adb shell cmd statusbar media-ttt-chip-add-sender Tablet
TransferInitiated` stays loading forever and never switches to succeed
state.
Test: media.taptotransfer tests

Change-Id: I38521e9af463a2d8eafdea63f9cf7c32bbbea9c6
parent a0cf5423
Loading
Loading
Loading
Loading
+4 −13
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import android.content.Context;
import android.view.WindowManager;

import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
@@ -33,10 +31,8 @@ import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerR
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.concurrency.DelayableExecutor;

import java.util.Optional;
import java.util.concurrent.Executor;

import javax.inject.Named;

@@ -89,14 +85,11 @@ public interface MediaModule {
    static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
            MediaTttFlags mediaTttFlags,
            Context context,
            WindowManager windowManager,
            @Main Executor mainExecutor,
            @Background Executor backgroundExecutor) {
            WindowManager windowManager) {
        if (!mediaTttFlags.isMediaTttEnabled()) {
            return Optional.empty();
        }
        return Optional.of(new MediaTttChipControllerSender(
                context, windowManager, mainExecutor, backgroundExecutor));
        return Optional.of(new MediaTttChipControllerSender(context, windowManager));
    }

    /** */
@@ -120,8 +113,7 @@ public interface MediaModule {
            CommandRegistry commandRegistry,
            Context context,
            MediaTttChipControllerSender mediaTttChipControllerSender,
            MediaTttChipControllerReceiver mediaTttChipControllerReceiver,
            @Main DelayableExecutor mainExecutor) {
            MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
        if (!mediaTttFlags.isMediaTttEnabled()) {
            return Optional.empty();
        }
@@ -130,8 +122,7 @@ public interface MediaModule {
                        commandRegistry,
                        context,
                        mediaTttChipControllerSender,
                        mediaTttChipControllerReceiver,
                        mainExecutor));
                        mediaTttChipControllerReceiver));
    }

    /** Inject into MediaTttSenderService. */
+1 −10
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
@@ -42,9 +41,7 @@ import com.android.systemui.shared.mediattt.DeviceInfo
import com.android.systemui.shared.mediattt.IDeviceSenderCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
import java.util.concurrent.FutureTask
import javax.inject.Inject

/**
@@ -57,7 +54,6 @@ class MediaTttCommandLineHelper @Inject constructor(
    private val context: Context,
    private val mediaTttChipControllerSender: MediaTttChipControllerSender,
    private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
    @Main private val mainExecutor: DelayableExecutor,
) {
    private var senderCallback: IDeviceSenderCallback? = null
    private val senderServiceConnection = SenderServiceConnection()
@@ -101,17 +97,13 @@ class MediaTttCommandLineHelper @Inject constructor(
                // TODO(b/203800643): Migrate other commands to invoke the service instead of the
                //   controller.
                TRANSFER_INITIATED_COMMAND_NAME -> {
                    val futureTask = FutureTask { fakeUndoRunnable }
                    mediaTttChipControllerSender.displayChip(
                        TransferInitiated(
                            appIconDrawable,
                            APP_ICON_CONTENT_DESCRIPTION,
                            otherDeviceName,
                            futureTask
                            otherDeviceName
                        )
                    )
                    mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)

                }
                TRANSFER_SUCCEEDED_COMMAND_NAME -> {
                    mediaTttChipControllerSender.displayChip(
@@ -248,6 +240,5 @@ val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
@VisibleForTesting
val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!

private const val FUTURE_WAIT_TIME = 2000L
private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
private const val TAG = "MediaTapToTransferCli"
+2 −11
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.graphics.drawable.Drawable
import androidx.annotation.StringRes
import com.android.systemui.R
import com.android.systemui.media.taptotransfer.common.MediaTttChipState
import java.util.concurrent.Future

/**
 * A class that stores all the information necessary to display the media tap-to-transfer chip on
@@ -71,19 +70,11 @@ class MoveCloserToEndCast(
    otherDeviceName
)

/**
 * A state representing that a transfer has been initiated (but not completed).
 *
 * @property future a future that will be resolved when the transfer has either succeeded or failed.
 *   If the transfer succeeded, the future can optionally return an undo runnable (see
 *   [TransferSucceeded.undoRunnable]). [MediaTttChipControllerSender] is responsible for transitioning
 *   the chip to the [TransferSucceeded] state if the future resolves successfully.
 */
/** A state representing that a transfer has been initiated (but not completed). */
class TransferInitiated(
    appIconDrawable: Drawable,
    appIconContentDescription: String,
    otherDeviceName: String,
    val future: Future<Runnable?>
    otherDeviceName: String
) : ChipStateSender(
    appIconDrawable,
    appIconContentDescription,
+0 −48
Original line number Diff line number Diff line
@@ -23,11 +23,7 @@ import android.view.WindowManager
import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import javax.inject.Inject

/**
@@ -38,8 +34,6 @@ import javax.inject.Inject
class MediaTttChipControllerSender @Inject constructor(
    context: Context,
    windowManager: WindowManager,
    @Main private val mainExecutor: Executor,
    @Background private val backgroundExecutor: Executor,
) : MediaTttChipControllerCommon<ChipStateSender>(
    context, windowManager, R.layout.media_ttt_chip
) {
@@ -77,47 +71,5 @@ class MediaTttChipControllerSender @Inject constructor(
        val showFailure = chipState is TransferFailed
        currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
            if (showFailure) { View.VISIBLE } else { View.GONE }

        // Future handling
        if (chipState is TransferInitiated) {
            addFutureCallback(chipState)
        }
    }

    /**
     * Adds the appropriate callbacks to [chipState.future] so that we update the chip correctly
     * when the future resolves.
     */
    private fun addFutureCallback(chipState: TransferInitiated) {
        // Listen to the future on a background thread so we don't occupy the main thread while we
        // wait for it to complete.
        backgroundExecutor.execute {
            try {
                val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                // Make UI changes on the main thread
                mainExecutor.execute {
                    displayChip(
                        TransferSucceeded(
                            chipState.appIconDrawable,
                            chipState.appIconContentDescription,
                            chipState.otherDeviceName,
                            undoRunnable
                        )
                    )
    }
            } catch (ex: Exception) {
                mainExecutor.execute {
                    displayChip(
                        TransferFailed(
                            chipState.appIconDrawable,
                            chipState.appIconContentDescription,
                            chipState.otherDeviceName,
                        )
                    )
}
            }
        }
    }
}

private const val TRANSFER_TIMEOUT_SECONDS = 10L
+0 −3
Original line number Diff line number Diff line
@@ -26,11 +26,9 @@ import com.android.systemui.shared.mediattt.DeviceInfo
import com.android.systemui.shared.mediattt.IDeviceSenderCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -75,7 +73,6 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
                context,
                mediaTttChipControllerSender,
                mediaTttChipControllerReceiver,
                FakeExecutor(FakeSystemClock())
            )
    }

Loading