Merge "[GH] Import JVM annotations" into androidx-main
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt
index 72087b1..a2f8213 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt
@@ -77,7 +77,7 @@
 
         val navController = findNavController(R.id.nav_host_fragment_activity_main)
         val appBarConfiguration = AppBarConfiguration(
-            setOf(R.id.navigation_home, R.id.navigation_scanner, R.id.navigation_advertiser)
+            setOf(R.id.navigation_scanner, R.id.navigation_advertiser)
         )
         setupActionBarWithNavController(navController, appBarConfiguration)
         navView.setupWithNavController(navController)
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt
index 54d477c..91afd29 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/BluetoothLe.kt
@@ -23,15 +23,8 @@
 import android.bluetooth.BluetoothGattServerCallback
 import android.bluetooth.BluetoothGattService
 import android.bluetooth.BluetoothManager
-import android.bluetooth.le.AdvertiseCallback
-import android.bluetooth.le.AdvertiseData
-import android.bluetooth.le.AdvertiseSettings
-import android.bluetooth.le.ScanCallback
-import android.bluetooth.le.ScanResult
-import android.bluetooth.le.ScanSettings
 import android.content.Context
 import android.util.Log
-import java.util.UUID
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
@@ -48,92 +41,6 @@
     private val bluetoothManager =
         context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
 
-    // Permissions are handled by MainActivity requestBluetoothPermissions
-    @SuppressLint("MissingPermission")
-    fun scan(
-        settings: ScanSettings
-    ): Flow<ScanResult> =
-        callbackFlow {
-            val callback = object : ScanCallback() {
-                override fun onScanResult(callbackType: Int, result: ScanResult) {
-                    trySend(result)
-                }
-
-                override fun onScanFailed(errorCode: Int) {
-                    Log.d(TAG, "onScanFailed() called with: errorCode = $errorCode")
-                }
-            }
-
-            val bluetoothAdapter = bluetoothManager?.adapter
-            val bleScanner = bluetoothAdapter?.bluetoothLeScanner
-
-            bleScanner?.startScan(null, settings, callback)
-
-            awaitClose {
-                Log.d(TAG, "awaitClose() called")
-                bleScanner?.stopScan(callback)
-            }
-        }
-
-    // Permissions are handled by MainActivity requestBluetoothPermissions
-    @SuppressLint("MissingPermission")
-    fun advertise(
-        settings: AdvertiseSettings,
-        data: AdvertiseData
-    ): Flow<AdvertiseResult> =
-        callbackFlow {
-            val callback = object : AdvertiseCallback() {
-                override fun onStartFailure(errorCode: Int) {
-                    // TODO(ofy) Map to proper errorCodes
-                    Log.d(TAG, "onStartFailure() called with: errorCode = $errorCode")
-                    trySend(AdvertiseResult.ADVERTISE_FAILED_INTERNAL_ERROR)
-                }
-
-                override fun onStartSuccess(settingsInEffect: AdvertiseSettings?) {
-                    trySend(AdvertiseResult.ADVERTISE_STARTED)
-                }
-            }
-
-            val bluetoothAdapter = bluetoothManager?.adapter
-            val bleAdvertiser = bluetoothAdapter?.bluetoothLeAdvertiser
-
-            bleAdvertiser?.startAdvertising(settings, data, callback)
-
-            awaitClose {
-                Log.d(TAG, "awaitClose() called")
-                bleAdvertiser?.stopAdvertising(callback)
-            }
-        }
-
-    interface GattClientScope {
-
-        fun getServices(): List<BluetoothGattService>
-        fun getService(uuid: UUID): BluetoothGattService?
-
-        suspend fun readCharacteristic(characteristic: BluetoothGattCharacteristic):
-            Result<ByteArray>
-        suspend fun writeCharacteristic(
-            characteristic: BluetoothGattCharacteristic,
-            value: ByteArray,
-            writeType: Int
-        ): Result<Unit>
-        suspend fun readDescriptor(descriptor: BluetoothGattDescriptor): Result<ByteArray>
-        suspend fun writeDescriptor(
-            descriptor: BluetoothGattDescriptor,
-            value: ByteArray
-        ): Result<Unit>
-        fun subscribeToCharacteristic(characteristic: BluetoothGattCharacteristic): Flow<ByteArray>
-        suspend fun awaitClose(onClosed: () -> Unit)
-    }
-
-    suspend fun <R> connectGatt(
-        context: Context,
-        device: BluetoothDevice,
-        block: suspend GattClientScope.() -> R
-    ): R? {
-        return GattClientImpl().connect(context, device, block)
-    }
-
     @SuppressLint("MissingPermission")
     fun openGattServer(
         services: List<BluetoothGattService> = emptyList()
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt
deleted file mode 100644
index 4c61aa6..0000000
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/GattClientImpl.kt
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright 2023 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 androidx.bluetooth.integration.testapp.experimental
-
-import android.annotation.SuppressLint
-import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothGatt
-import android.bluetooth.BluetoothGatt.GATT_SUCCESS
-import android.bluetooth.BluetoothGattCallback
-import android.bluetooth.BluetoothGattCharacteristic
-import android.bluetooth.BluetoothGattDescriptor
-import android.bluetooth.BluetoothGattService
-import android.content.Context
-import android.util.Log
-import androidx.collection.arrayMapOf
-import java.util.UUID
-import kotlin.coroutines.cancellation.CancellationException
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.job
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-
-internal class GattClientImpl {
-
-    private companion object {
-        private const val TAG = "GattClientImpl"
-        private const val GATT_MAX_MTU = 517
-        private val CCCD_UID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
-    }
-
-    private sealed interface CallbackResult {
-        class OnCharacteristicRead(
-            val characteristic: BluetoothGattCharacteristic,
-            val value: ByteArray,
-            val status: Int
-        ) : CallbackResult
-
-        class OnCharacteristicWrite(
-            val characteristic: BluetoothGattCharacteristic,
-            val status: Int
-        ) : CallbackResult
-
-        class OnDescriptorRead(
-            val descriptor: BluetoothGattDescriptor,
-            val value: ByteArray,
-            val status: Int
-        ) : CallbackResult
-        class OnDescriptorWrite(
-            val descriptor: BluetoothGattDescriptor,
-            val status: Int
-        ) : CallbackResult
-    }
-
-    private interface SubscribeListener {
-        fun onCharacteristicNotification(value: ByteArray)
-        fun finish()
-    }
-
-    @SuppressLint("MissingPermission")
-    suspend fun <R> connect(
-        context: Context,
-        device: BluetoothDevice,
-        block: suspend BluetoothLe.GattClientScope.() -> R
-    ): R? = coroutineScope {
-        val connectResult = CompletableDeferred<Boolean>(parent = coroutineContext.job)
-        val finished = Job(parent = coroutineContext.job)
-        val callbackResultsFlow = MutableSharedFlow<CallbackResult>(
-            extraBufferCapacity = Int.MAX_VALUE)
-        val subscribeMap: MutableMap<BluetoothGattCharacteristic, SubscribeListener> = arrayMapOf()
-        val subscribeMutex = Mutex()
-
-        val callback = object : BluetoothGattCallback() {
-            override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
-                if (newState == BluetoothGatt.STATE_CONNECTED) {
-                    gatt?.requestMtu(GATT_MAX_MTU)
-                } else {
-                    connectResult.complete(false)
-                    // TODO(b/270492198): throw precise exception
-                    finished.completeExceptionally(IllegalStateException("connect failed"))
-                }
-            }
-
-            override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
-                Log.d(TAG, "onMtuChanged() called with: gatt = $gatt, mtu = $mtu, status = $status")
-                if (status == GATT_SUCCESS) {
-                    gatt?.discoverServices()
-                } else {
-                    connectResult.complete(false)
-                    // TODO(b/270492198): throw precise exception
-                    finished.completeExceptionally(IllegalStateException("mtu request failed"))
-                }
-            }
-
-            override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
-                connectResult.complete(status == GATT_SUCCESS)
-            }
-
-            override fun onCharacteristicRead(
-                gatt: BluetoothGatt,
-                characteristic: BluetoothGattCharacteristic,
-                value: ByteArray,
-                status: Int
-            ) {
-                callbackResultsFlow.tryEmit(
-                    CallbackResult.OnCharacteristicRead(characteristic, value, status))
-            }
-
-            override fun onCharacteristicWrite(
-                gatt: BluetoothGatt,
-                characteristic: BluetoothGattCharacteristic,
-                status: Int
-            ) {
-                callbackResultsFlow.tryEmit(
-                    CallbackResult.OnCharacteristicWrite(characteristic, status))
-            }
-
-            override fun onDescriptorRead(
-                gatt: BluetoothGatt,
-                descriptor: BluetoothGattDescriptor,
-                status: Int,
-                value: ByteArray
-            ) {
-                callbackResultsFlow.tryEmit(
-                    CallbackResult.OnDescriptorRead(descriptor, value, status))
-            }
-
-            override fun onDescriptorWrite(
-                gatt: BluetoothGatt,
-                descriptor: BluetoothGattDescriptor,
-                status: Int
-            ) {
-                callbackResultsFlow.tryEmit(
-                    CallbackResult.OnDescriptorWrite(descriptor, status))
-            }
-
-            override fun onCharacteristicChanged(
-                gatt: BluetoothGatt,
-                characteristic: BluetoothGattCharacteristic,
-                value: ByteArray
-            ) {
-                launch {
-                    subscribeMutex.withLock {
-                        subscribeMap[characteristic]?.onCharacteristicNotification(value)
-                    }
-                }
-            }
-        }
-        val bluetoothGatt = device.connectGatt(context, /*autoConnect=*/false, callback)
-
-        if (!connectResult.await()) {
-            Log.w(TAG, "Failed to connect to the remote GATT server")
-            return@coroutineScope null
-        }
-        val gattScope = object : BluetoothLe.GattClientScope {
-            val taskMutex = Mutex()
-            suspend fun<R> runTask(block: suspend () -> R): R {
-                taskMutex.withLock {
-                    return block()
-                }
-            }
-
-            override fun getServices(): List<BluetoothGattService> {
-                return bluetoothGatt.services
-            }
-
-            override fun getService(uuid: UUID): BluetoothGattService? {
-                return bluetoothGatt.getService(uuid)
-            }
-
-            override suspend fun readCharacteristic(characteristic: BluetoothGattCharacteristic):
-                Result<ByteArray> {
-                return runTask {
-                    bluetoothGatt.readCharacteristic(characteristic)
-                    val res = takeMatchingResult<CallbackResult.OnCharacteristicRead>(
-                        callbackResultsFlow) {
-                        it.characteristic == characteristic
-                    }
-
-                    if (res.status == GATT_SUCCESS) Result.success(res.value)
-                    else Result.failure(RuntimeException("fail"))
-                }
-            }
-
-            override suspend fun writeCharacteristic(
-                characteristic: BluetoothGattCharacteristic,
-                value: ByteArray,
-                writeType: Int
-            ): Result<Unit> {
-                return runTask {
-                    bluetoothGatt.writeCharacteristic(characteristic, value, writeType)
-                    val res = takeMatchingResult<CallbackResult.OnCharacteristicWrite>(
-                        callbackResultsFlow) {
-                        it.characteristic == characteristic
-                    }
-                    if (res.status == GATT_SUCCESS) Result.success(Unit)
-                    else Result.failure(RuntimeException("fail"))
-                }
-            }
-
-            override suspend fun readDescriptor(descriptor: BluetoothGattDescriptor):
-                Result<ByteArray> {
-                return runTask {
-                    bluetoothGatt.readDescriptor(descriptor)
-                    val res = takeMatchingResult<CallbackResult.OnDescriptorRead>(
-                        callbackResultsFlow) {
-                        it.descriptor == descriptor
-                    }
-
-                    if (res.status == GATT_SUCCESS) Result.success(res.value)
-                    else Result.failure(RuntimeException("fail"))
-                }
-            }
-
-            override suspend fun writeDescriptor(
-                descriptor: BluetoothGattDescriptor,
-                value: ByteArray
-            ): Result<Unit> {
-                return runTask {
-                    bluetoothGatt.writeDescriptor(descriptor, value)
-                    val res = takeMatchingResult<CallbackResult.OnDescriptorWrite>(
-                        callbackResultsFlow) {
-                        it.descriptor == descriptor
-                    }
-                    if (res.status == GATT_SUCCESS) Result.success(Unit)
-                    else Result.failure(RuntimeException("fail"))
-                }
-            }
-
-            override fun subscribeToCharacteristic(characteristic: BluetoothGattCharacteristic):
-                Flow<ByteArray> {
-                val cccd = characteristic.getDescriptor(CCCD_UID) ?: return emptyFlow()
-
-                return callbackFlow {
-                    val listener = object : SubscribeListener {
-                        override fun onCharacteristicNotification(value: ByteArray) {
-                            trySend(value)
-                        }
-                        override fun finish() {
-                            cancel("finished")
-                        }
-                    }
-                    if (!registerSubscribeListener(characteristic, listener)) {
-                        cancel("already subscribed")
-                    }
-
-                    runTask {
-                        bluetoothGatt.setCharacteristicNotification(characteristic, /*enable=*/true)
-                        bluetoothGatt.writeDescriptor(
-                            cccd,
-                            BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
-                        )
-                        val res = takeMatchingResult<CallbackResult.OnDescriptorWrite>(
-                            callbackResultsFlow) {
-                            it.descriptor == cccd
-                        }
-                        if (res.status != GATT_SUCCESS) {
-                            cancel(CancellationException("failed to set notification"))
-                        }
-                    }
-
-                    this.awaitClose {
-                        launch {
-                            unregisterSubscribeListener(characteristic)
-                        }
-                        bluetoothGatt.setCharacteristicNotification(characteristic,
-                            /*enable=*/false)
-                        bluetoothGatt.writeDescriptor(
-                            cccd,
-                            BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
-                        )
-                    }
-                }
-            }
-
-            override suspend fun awaitClose(onClosed: () -> Unit) {
-                try {
-                    // Wait for queued tasks done
-                    taskMutex.withLock {
-                        subscribeMutex.withLock {
-                            subscribeMap.values.forEach { it.finish() }
-                        }
-                    }
-                } finally {
-                    onClosed()
-                }
-            }
-
-            private suspend fun registerSubscribeListener(
-                characteristic: BluetoothGattCharacteristic,
-                callback: SubscribeListener
-            ): Boolean {
-                subscribeMutex.withLock {
-                    if (subscribeMap.containsKey(characteristic)) {
-                        return false
-                    }
-                    subscribeMap[characteristic] = callback
-                    return true
-                }
-            }
-
-            private suspend fun unregisterSubscribeListener(
-                characteristic: BluetoothGattCharacteristic
-            ) {
-                subscribeMutex.withLock {
-                    subscribeMap.remove(characteristic)
-                }
-            }
-        }
-        gattScope.block()
-    }
-
-    private suspend inline fun<reified R : CallbackResult> takeMatchingResult(
-        flow: SharedFlow<CallbackResult>,
-        crossinline predicate: (R) -> Boolean
-    ): R {
-        return flow.filter { it is R && predicate(it) }.first() as R
-    }
-}
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt
deleted file mode 100644
index bb72d7c..0000000
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeFragment.kt
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright 2023 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 androidx.bluetooth.integration.testapp.ui.home
-
-import android.annotation.SuppressLint
-import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY
-import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ
-import android.bluetooth.le.AdvertiseData
-import android.bluetooth.le.AdvertiseSettings
-import android.bluetooth.le.ScanResult
-import android.bluetooth.le.ScanSettings
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import androidx.bluetooth.integration.testapp.R
-import androidx.bluetooth.integration.testapp.databinding.FragmentHomeBinding
-import androidx.bluetooth.integration.testapp.experimental.AdvertiseResult
-import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
-import androidx.bluetooth.integration.testapp.experimental.GattServerCallback
-import androidx.bluetooth.integration.testapp.ui.common.ScanResultAdapter
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
-
-class HomeFragment : Fragment() {
-
-    companion object {
-        private const val TAG = "HomeFragment"
-    }
-
-    private var scanResultAdapter: ScanResultAdapter? = null
-
-    private lateinit var bluetoothLe: BluetoothLe
-
-    private val viewModel: HomeViewModel by viewModels()
-
-    private var _binding: FragmentHomeBinding? = null
-    private val binding get() = _binding!!
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
-        _binding = FragmentHomeBinding.inflate(inflater, container, false)
-        return binding.root
-    }
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-
-        bluetoothLe = BluetoothLe(requireContext())
-
-        scanResultAdapter = ScanResultAdapter { scanResult -> onClickScanResult(scanResult) }
-        binding.recyclerView.adapter = scanResultAdapter
-
-        binding.buttonScan.setOnClickListener {
-            if (scanJob?.isActive == true) {
-                scanJob?.cancel()
-                binding.buttonScan.text = getString(R.string.scan_using_androidx_bluetooth)
-            } else {
-                startScan()
-            }
-        }
-
-        binding.switchAdvertise.setOnCheckedChangeListener { _, isChecked ->
-            if (isChecked) startAdvertise()
-            else advertiseJob?.cancel()
-        }
-
-        binding.switchGattServer.setOnCheckedChangeListener { _, isChecked ->
-            if (isChecked) openGattServer()
-            else gattServerJob?.cancel()
-        }
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-        scanJob?.cancel()
-        advertiseJob?.cancel()
-        gattServerJob?.cancel()
-    }
-
-    private val scanScope = CoroutineScope(Dispatchers.Main + Job())
-    private var scanJob: Job? = null
-
-    private val connectScope = CoroutineScope(Dispatchers.Default + Job())
-    private var connectJob: Job? = null
-
-    private fun startScan() {
-        Log.d(TAG, "startScan() called")
-
-        val scanSettings = ScanSettings.Builder()
-            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
-            .build()
-
-        scanJob = scanScope.launch {
-            Toast.makeText(context, getString(R.string.scan_start_message), Toast.LENGTH_SHORT)
-                .show()
-
-            binding.buttonScan.text = getString(R.string.stop_scanning)
-
-            bluetoothLe.scan(scanSettings)
-                .collect {
-                    Log.d(TAG, "ScanResult collected: $it")
-
-                    if (it.scanRecord?.serviceUuids?.isEmpty() == false)
-                        viewModel.scanResults[it.device.address] = it
-                    scanResultAdapter?.submitList(viewModel.scanResults.values.toMutableList())
-                    scanResultAdapter?.notifyItemInserted(viewModel.scanResults.size)
-                }
-        }
-    }
-
-    private fun onClickScanResult(scanResult: ScanResult) {
-        scanJob?.cancel()
-        connectJob?.cancel()
-        connectJob = connectScope.launch {
-            bluetoothLe.connectGatt(requireContext(), scanResult.device) {
-                for (srv in getServices()) {
-                    for (char in srv.characteristics) {
-                        if (char.properties.and(PROPERTY_READ) == 0) continue
-                        launch {
-                            val value = readCharacteristic(char).getOrNull()
-                            if (value != null) {
-                                Log.d(TAG, "Successfully read characteristic value=$value")
-                            }
-                        }
-                        launch {
-                            if (char.properties.and(PROPERTY_NOTIFY) != 0) {
-                                val value = subscribeToCharacteristic(char).first()
-                                Log.d(TAG, "Successfully get characteristic value=$value")
-                            }
-                        }
-                    }
-                }
-                awaitClose {
-                    Log.d(TAG, "GATT client is closed")
-                    connectJob = null
-                }
-            }
-        }
-    }
-
-    private val advertiseScope = CoroutineScope(Dispatchers.Main + Job())
-    private var advertiseJob: Job? = null
-
-    // Permissions are handled by MainActivity requestBluetoothPermissions
-    @SuppressLint("MissingPermission")
-    private fun startAdvertise() {
-        Log.d(TAG, "startAdvertise() called")
-
-        val advertiseSettings = AdvertiseSettings.Builder()
-            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
-            .setTimeout(0)
-            .build()
-
-        val advertiseData = AdvertiseData.Builder()
-            .setIncludeDeviceName(true)
-            .build()
-
-        advertiseJob = advertiseScope.launch {
-            bluetoothLe.advertise(advertiseSettings, advertiseData)
-                .collect {
-                    Log.d(TAG, "advertiseResult received: $it")
-
-                    when (it) {
-                        AdvertiseResult.ADVERTISE_STARTED -> {
-                            Toast.makeText(
-                                context,
-                                getString(R.string.advertise_start_message), Toast.LENGTH_SHORT
-                            )
-                                .show()
-                        }
-                        AdvertiseResult.ADVERTISE_FAILED_ALREADY_STARTED -> {
-                            Log.d(
-                                TAG, "advertise onStartFailure() called with: " +
-                                    "${AdvertiseResult.ADVERTISE_FAILED_ALREADY_STARTED}"
-                            )
-                        }
-                        AdvertiseResult.ADVERTISE_FAILED_DATA_TOO_LARGE -> {
-                            Log.d(
-                                TAG, "advertise onStartFailure() called with: " +
-                                    "${AdvertiseResult.ADVERTISE_FAILED_DATA_TOO_LARGE}"
-                            )
-                        }
-                        AdvertiseResult.ADVERTISE_FAILED_FEATURE_UNSUPPORTED -> {
-                            Log.d(
-                                TAG, "advertise onStartFailure() called with: " +
-                                    "${AdvertiseResult.ADVERTISE_FAILED_FEATURE_UNSUPPORTED}"
-                            )
-                        }
-                        AdvertiseResult.ADVERTISE_FAILED_INTERNAL_ERROR -> {
-                            Log.d(
-                                TAG, "advertise onStartFailure() called with: " +
-                                    "${AdvertiseResult.ADVERTISE_FAILED_INTERNAL_ERROR}"
-                            )
-                        }
-                        AdvertiseResult.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS -> {
-                            Log.d(
-                                TAG, "advertise onStartFailure() called with: " +
-                                    "${AdvertiseResult.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS}"
-                            )
-                        }
-                    }
-                }
-        }
-    }
-
-    private val gattServerScope = CoroutineScope(Dispatchers.Main + Job())
-    private var gattServerJob: Job? = null
-
-    // Permissions are handled by MainActivity requestBluetoothPermissions
-    @SuppressLint("MissingPermission")
-    private fun openGattServer() {
-        Log.d(TAG, "openGattServer() called")
-
-        gattServerJob = gattServerScope.launch {
-            bluetoothLe.openGattServer().collect { gattServerCallback ->
-                when (gattServerCallback) {
-                    is GattServerCallback.OnCharacteristicReadRequest -> {
-                        val onCharacteristicReadRequest:
-                            GattServerCallback.OnCharacteristicReadRequest = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onCharacteristicReadRequest = $onCharacteristicReadRequest"
-                        )
-                    }
-                    is GattServerCallback.OnCharacteristicWriteRequest -> {
-                        val onCharacteristicWriteRequest:
-                            GattServerCallback.OnCharacteristicWriteRequest = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onCharacteristicWriteRequest = $onCharacteristicWriteRequest"
-                        )
-                    }
-                    is GattServerCallback.OnConnectionStateChange -> {
-                        val onConnectionStateChange:
-                            GattServerCallback.OnConnectionStateChange = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onConnectionStateChange = $onConnectionStateChange"
-                        )
-                    }
-                    is GattServerCallback.OnDescriptorReadRequest -> {
-                        val onDescriptorReadRequest:
-                            GattServerCallback.OnDescriptorReadRequest = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onDescriptorReadRequest = $onDescriptorReadRequest"
-                        )
-                    }
-                    is GattServerCallback.OnDescriptorWriteRequest -> {
-                        val onDescriptorWriteRequest:
-                            GattServerCallback.OnDescriptorWriteRequest = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onDescriptorWriteRequest = $onDescriptorWriteRequest"
-                        )
-                    }
-                    is GattServerCallback.OnExecuteWrite -> {
-                        val onExecuteWrite:
-                            GattServerCallback.OnExecuteWrite = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onExecuteWrite = $onExecuteWrite"
-                        )
-                    }
-                    is GattServerCallback.OnMtuChanged -> {
-                        val onMtuChanged:
-                            GattServerCallback.OnMtuChanged = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onMtuChanged = $onMtuChanged"
-                        )
-                    }
-                    is GattServerCallback.OnNotificationSent -> {
-                        val onNotificationSent:
-                            GattServerCallback.OnNotificationSent = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onNotificationSent = $onNotificationSent"
-                        )
-                    }
-                    is GattServerCallback.OnPhyRead -> {
-                        val onPhyRead:
-                            GattServerCallback.OnPhyRead = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onPhyRead = $onPhyRead"
-                        )
-                    }
-                    is GattServerCallback.OnPhyUpdate -> {
-                        val onPhyUpdate:
-                            GattServerCallback.OnPhyUpdate = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onPhyUpdate = $onPhyUpdate"
-                        )
-                    }
-                    is GattServerCallback.OnServiceAdded -> {
-                        val onServiceAdded:
-                            GattServerCallback.OnServiceAdded = gattServerCallback
-                        Log.d(
-                            TAG,
-                            "openGattServer() called with: " +
-                                "onServiceAdded = $onServiceAdded"
-                        )
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt
deleted file mode 100644
index 88b1c1c..0000000
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/home/HomeViewModel.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2023 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 androidx.bluetooth.integration.testapp.ui.home
-
-import android.bluetooth.le.ScanResult
-import androidx.lifecycle.ViewModel
-
-class HomeViewModel : ViewModel() {
-
-    private companion object {
-        private const val TAG = "HomeViewModel"
-    }
-
-    val scanResults = mutableMapOf<String, ScanResult>()
-}
diff --git a/bluetooth/integration-tests/testapp/src/main/res/drawable/ic_bluetooth_24.xml b/bluetooth/integration-tests/testapp/src/main/res/drawable/ic_bluetooth_24.xml
deleted file mode 100644
index 34e9311..0000000
--- a/bluetooth/integration-tests/testapp/src/main/res/drawable/ic_bluetooth_24.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp" android:tint="#000000"
-    android:viewportHeight="24" android:viewportWidth="24"
-    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="@android:color/white" android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
-</vector>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_home.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_home.xml
deleted file mode 100644
index 9b242ce..0000000
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_home.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 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.
-  -->
-<androidx.constraintlayout.widget.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".ui.home.HomeFragment">
-
-    <Button
-        android:id="@+id/button_scan"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:text="@string/scan_using_androidx_bluetooth"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <androidx.appcompat.widget.SwitchCompat
-        android:id="@+id/switch_advertise"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:text="@string/advertise_using_androidx_bluetooth"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/button_scan" />
-
-    <androidx.appcompat.widget.SwitchCompat
-        android:id="@+id/switch_gatt_server"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:text="@string/open_gatt_server_using_androidx_bluetooth"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/switch_advertise" />
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/recycler_view"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        app:layoutManager="LinearLayoutManager"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toBottomOf="@+id/switch_gatt_server"
-        tools:itemCount="3"
-        tools:listitem="@layout/scan_result_item" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/menu/bottom_nav_menu.xml b/bluetooth/integration-tests/testapp/src/main/res/menu/bottom_nav_menu.xml
index 68f6353..d3c5b8f 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/menu/bottom_nav_menu.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/menu/bottom_nav_menu.xml
@@ -17,11 +17,6 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item
-        android:id="@+id/navigation_home"
-        android:icon="@drawable/ic_bluetooth_24"
-        android:title="@string/title_home" />
-
-    <item
         android:id="@+id/navigation_scanner"
         android:icon="@drawable/baseline_bluetooth_searching_24"
         android:title="@string/title_scanner" />
diff --git a/bluetooth/integration-tests/testapp/src/main/res/navigation/nav_graph.xml b/bluetooth/integration-tests/testapp/src/main/res/navigation/nav_graph.xml
index b3c74a1..488e0f06 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/navigation/nav_graph.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/navigation/nav_graph.xml
@@ -18,24 +18,18 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/nav_graph"
-    app:startDestination="@id/navigation_home">
-
-    <fragment
-        android:id="@+id/navigation_home"
-        android:name="androidx.bluetooth.integration.testapp.ui.home.HomeFragment"
-        android:label="@string/title_home"
-        tools:layout="@layout/fragment_home" />
+    app:startDestination="@id/navigation_scanner">
 
     <fragment
         android:id="@+id/navigation_scanner"
         android:name="androidx.bluetooth.integration.testapp.ui.scanner.ScannerFragment"
         android:label="@string/title_scanner"
-        tools:layout="@layout/fragment_home" />
+        tools:layout="@layout/fragment_scanner" />
 
     <fragment
         android:id="@+id/navigation_advertiser"
         android:name="androidx.bluetooth.integration.testapp.ui.advertiser.AdvertiserFragment"
         android:label="@string/title_advertiser"
-        tools:layout="@layout/fragment_home" />
+        tools:layout="@layout/fragment_advertiser" />
 
 </navigation>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index 7c47b75..06dffe7 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -17,7 +17,6 @@
 <resources>
     <string name="app_name">AndroidX Bluetooth Test App</string>
 
-    <string name="title_home">AndroidX Bluetooth</string>
     <string name="title_scanner">Scanner</string>
     <string name="title_advertiser">Advertiser</string>
 
@@ -70,13 +69,4 @@
     <string name="gatt_server">Gatt Server</string>
     <string name="open_gatt_server">Open Gatt Server</string>
     <string name="stop_gatt_server">Stop Gatt Server</string>
-
-    <string name="scan_using_androidx_bluetooth">Scan using AndroidX Bluetooth APIs</string>
-    <string name="scan_start_message">Scan started. Results are in Logcat</string>
-
-    <string name="advertise_using_androidx_bluetooth">Advertise using AndroidX Bluetooth APIs</string>
-    <string name="advertise_start_message">Advertise started</string>
-
-    <string name="open_gatt_server_using_androidx_bluetooth">Open GATT Server using AndroidX Bluetooth APIs</string>
-    <string name="gatt_server_open">GATT Server open</string>
 </resources>
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
index 0b63a70..16d7e8d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
@@ -104,6 +104,7 @@
                 task.referenceApi.set(checkApiRelease!!.flatMap { it.referenceApi })
                 task.baselines.set(checkApiRelease!!.flatMap { it.baselines })
                 task.api.set(builtApiLocation)
+                task.version.set(version)
                 task.dependencyClasspath = javaCompileInputs.dependencyClasspath
                 task.bootClasspath = javaCompileInputs.bootClasspath
                 task.k2UastEnabled.set(extension.metalavaK2UastEnabled)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
index 9e82b95..9e91631 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
@@ -16,6 +16,7 @@
 
 package androidx.build.metalava
 
+import androidx.build.Version
 import androidx.build.checkapi.ApiBaselinesLocation
 import androidx.build.checkapi.ApiLocation
 import java.io.File
@@ -92,6 +93,10 @@
     @get:Input
     abstract val baselines: Property<ApiBaselinesLocation>
 
+    // Version for the current API surface.
+    @get:Input
+    abstract val version: Property<Version>
+
     @[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
     fun getTaskInputs(): List<File> {
         val referenceApiLocation = referenceApi.get()
@@ -115,18 +120,23 @@
     fun exec() {
         check(bootClasspath.files.isNotEmpty()) { "Android boot classpath not set." }
 
+        val apiLocation = api.get()
+        val referenceApiLocation = referenceApi.get()
+        val freezeApis = shouldFreezeApis(referenceApiLocation.version(), version.get())
         updateBaseline(
-            api.get().publicApiFile,
-            referenceApi.get().publicApiFile,
+            apiLocation.publicApiFile,
+            referenceApiLocation.publicApiFile,
             baselines.get().publicApiFile,
-            false
+            false,
+            freezeApis
         )
-        if (referenceApi.get().restrictedApiFile.exists()) {
+        if (referenceApiLocation.restrictedApiFile.exists()) {
             updateBaseline(
-                api.get().restrictedApiFile,
-                referenceApi.get().restrictedApiFile,
+                apiLocation.restrictedApiFile,
+                referenceApiLocation.restrictedApiFile,
                 baselines.get().restrictedApiFile,
-                true
+                true,
+                freezeApis
             )
         }
     }
@@ -137,7 +147,8 @@
         api: File,
         prevApi: File,
         baselineFile: File,
-        processRestrictedApis: Boolean
+        processRestrictedApis: Boolean,
+        freezeApis: Boolean,
     ) {
         val args = getCommonBaselineUpdateArgs(
             bootClasspath,
@@ -152,6 +163,12 @@
             "--source-files",
             api.toString()
         )
+        if (freezeApis) {
+            args += listOf(
+                "--error-category",
+                "Compatibility"
+            )
+        }
         if (processRestrictedApis) {
             args += listOf(
                 "--show-annotation",
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
index dba781b..6c21ada 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
@@ -49,6 +49,7 @@
 import androidx.camera.core.impl.UseCaseConfigFactory;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
+import androidx.camera.core.internal.compat.workaround.CaptureFailedRetryEnabler;
 import androidx.camera.testing.CoreAppTestUtil;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraCaptureResult;
@@ -536,6 +537,9 @@
 
         // Act.
         // Complete the picture taken, then new flash mode should be applied.
+        CaptureFailedRetryEnabler retryEnabler = new CaptureFailedRetryEnabler();
+        // Because of retry in some devices, we may need to notify capture failures multiple times.
+        addExtraFailureNotificationsForRetry(fakeCameraControl, retryEnabler.getRetryCount());
         fakeCameraControl.notifyAllRequestsOnCaptureFailed();
 
         // Assert.
@@ -543,6 +547,16 @@
         assertThat(fakeCameraControl.getFlashMode()).isEqualTo(ImageCapture.FLASH_MODE_ON);
     }
 
+    private void addExtraFailureNotificationsForRetry(FakeCameraControl cameraControl,
+            int retryCount) {
+        if (retryCount > 0) {
+            cameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
+                addExtraFailureNotificationsForRetry(cameraControl, retryCount - 1);
+                cameraControl.notifyAllRequestsOnCaptureFailed();
+            });
+        }
+    }
+
     @Test
     public void correctViewPortRectInResolutionInfo_withCropAspectRatioSetting() {
         ImageCapture imageCapture = new ImageCapture.Builder()
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java
index 58ff19d..4903752 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java
@@ -149,13 +149,19 @@
             // Fail silently if the request has been aborted.
             return;
         }
-        if (mTakePictureRequest.decrementRetryCounter()) {
-            mRetryControl.retryRequest(mTakePictureRequest);
-        } else {
+
+        boolean isRetryAllowed = mTakePictureRequest.decrementRetryCounter();
+        if (!isRetryAllowed) {
             onFailure(imageCaptureException);
         }
         markComplete();
         mCaptureCompleter.setException(imageCaptureException);
+
+        if (isRetryAllowed) {
+            // retry after all the cleaning up works are done via mCaptureCompleter.setException,
+            // e.g. removing previous request from CaptureNode, SingleBundlingNode etc.
+            mRetryControl.retryRequest(mTakePictureRequest);
+        }
     }
 
     @MainThread
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureManager.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureManager.java
index 3ba10d1..e2c6ae5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureManager.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureManager.java
@@ -37,6 +37,7 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCaptureException;
 import androidx.camera.core.ImageProxy;
+import androidx.camera.core.Logger;
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 import androidx.camera.core.impl.utils.futures.Futures;
 import androidx.core.util.Pair;
@@ -120,8 +121,11 @@
     @Override
     public void retryRequest(@NonNull TakePictureRequest request) {
         checkMainThread();
+        Logger.d(TAG, "Add a new request for retrying.");
         // Insert the request to the front of the queue.
         mNewRequests.addFirst(request);
+        // Try to issue the newly added request in case condition allows.
+        issueNextRequest();
     }
 
     /**
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeImageCaptureControl.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeImageCaptureControl.kt
index d0b5089..85043d9 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeImageCaptureControl.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeImageCaptureControl.kt
@@ -39,11 +39,17 @@
     // Flip this flag to return a custom result using pendingResultCompleter.
     var shouldUsePendingResult = false
     lateinit var pendingResultCompleter: CallbackToFutureAdapter.Completer<Void>
-    var pendingResult = CallbackToFutureAdapter.getFuture { completer ->
+    var pendingResult = createPendingResult()
+
+    private fun createPendingResult() = CallbackToFutureAdapter.getFuture { completer ->
         pendingResultCompleter = completer
         "FakeImageCaptureControl's pendingResult"
     }
 
+    fun resetPendingResult() {
+        pendingResult = createPendingResult()
+    }
+
     override fun lockFlashMode() {
         actions.add(Action.LOCK_FLASH)
     }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt
index 2a6cf328..2f8d74f 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt
@@ -24,10 +24,14 @@
 import androidx.camera.core.ImageCapture.ERROR_CAPTURE_FAILED
 import androidx.camera.core.ImageCapture.OutputFileResults
 import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.imagecapture.FakeImageCaptureControl.Action.SUBMIT_REQUESTS
 import androidx.camera.core.impl.CaptureConfig
+import androidx.camera.core.internal.compat.quirk.CaptureFailedRetryQuirk
+import androidx.camera.core.internal.compat.quirk.DeviceQuirks
 import androidx.camera.testing.fakes.FakeImageInfo
 import androidx.camera.testing.fakes.FakeImageProxy
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,6 +39,7 @@
 import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowBuild
 
 /**
  * Unit tests for [TakePictureManager].
@@ -133,7 +138,7 @@
         // Assert: one request is sent.
         assertThat(imageCaptureControl.actions).containsExactly(
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
         ).inOrder()
         // Both request are aborted.
@@ -184,7 +189,7 @@
         // Assert: only one request is sent.
         assertThat(imageCaptureControl.actions).containsExactly(
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
         ).inOrder()
 
@@ -195,10 +200,10 @@
         // Assert: 2nd request is sent too.
         assertThat(imageCaptureControl.actions).containsExactly(
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
         ).inOrder()
     }
@@ -275,7 +280,7 @@
         // Assert:
         assertThat(imageCaptureControl.actions).containsExactly(
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
         ).inOrder()
         assertThat(imageCaptureControl.latestCaptureConfigs).isEqualTo(response1)
@@ -288,10 +293,10 @@
         // Assert: imageCaptureControl was invoked in the exact given order.
         assertThat(imageCaptureControl.actions).containsExactly(
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
         ).inOrder()
         assertThat(imageCaptureControl.latestCaptureConfigs).isEqualTo(response2)
@@ -399,7 +404,7 @@
         assertThat(takePictureManager.mNewRequests.size).isEqualTo(0)
         assertThat(imageCaptureControl.actions).containsExactly(
             FakeImageCaptureControl.Action.LOCK_FLASH,
-            FakeImageCaptureControl.Action.SUBMIT_REQUESTS,
+            SUBMIT_REQUESTS,
             FakeImageCaptureControl.Action.UNLOCK_FLASH,
         ).inOrder()
     }
@@ -443,4 +448,74 @@
         // Assert. new request can be issued after the capture failure of the first request
         takePictureManager.offerRequest(request2)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun requestFailure_failureReportedIfQuirkDisabled() {
+        // Arrange: use the real ImagePipeline implementation to do the test
+        takePictureManager.mImagePipeline =
+            ImagePipeline(Utils.createEmptyImageCaptureConfig(), Size(640, 480))
+
+        // Create a request and offer it to the manager.
+        imageCaptureControl.shouldUsePendingResult = true
+        val request = FakeTakePictureRequest(FakeTakePictureRequest.Type.IN_MEMORY)
+        takePictureManager.offerRequest(request)
+
+        // Act: make the request fail once.
+        imageCaptureControl.pendingResultCompleter.setException(
+            ImageCaptureException(
+                ERROR_CAPTURE_FAILED, "", null
+            )
+        )
+        shadowOf(getMainLooper()).idle()
+
+        // Assert: failure exception received and no more retry possible.
+        assertThat((request.exceptionReceived as ImageCaptureException).imageCaptureError)
+            .isEqualTo(ERROR_CAPTURE_FAILED)
+        assertThat(request.remainingRetries).isEqualTo(0)
+        // Only 1 request submitted to camera
+        assertThat(imageCaptureControl.actions.count { it == SUBMIT_REQUESTS }).isEqualTo(1)
+    }
+
+    @Test
+    fun requestFailure_retriedIfQuirkEnabled() {
+        // Arrange: enable retry related quirk.
+        ShadowBuild.setBrand("SAMSUNG")
+        ShadowBuild.setModel("SM-G981U1")
+
+        val captureFailedRetryQuirk = DeviceQuirks.get(CaptureFailedRetryQuirk::class.java)
+
+        assertWithMessage("CaptureFailedRetryQuirk not enabled!")
+            .that(captureFailedRetryQuirk).isNotNull()
+
+        // Use the real ImagePipeline implementation to do the test
+        takePictureManager.mImagePipeline =
+            ImagePipeline(Utils.createEmptyImageCaptureConfig(), Size(640, 480))
+
+        // Create a request and offer it to the manager.
+        imageCaptureControl.shouldUsePendingResult = true
+        val request = FakeTakePictureRequest(FakeTakePictureRequest.Type.IN_MEMORY)
+        takePictureManager.offerRequest(request)
+
+        // Act: make the request fail once and then successful if retried later.
+        imageCaptureControl.pendingResultCompleter.setException(
+            ImageCaptureException(
+                ERROR_CAPTURE_FAILED, "", null
+            )
+        )
+        imageCaptureControl.resetPendingResult()
+
+        // complete the new capture successfully
+        imageCaptureControl.pendingResultCompleter.set(null)
+        shadowOf(getMainLooper()).idle()
+
+        // Assert: retry count decremented without any failure.
+        assertThat(request.remainingRetries).isEqualTo(captureFailedRetryQuirk!!.retryCount - 1)
+        assertThat(request.exceptionReceived).isNull()
+        // 2 requests submitted to camera
+        assertThat(imageCaptureControl.actions.count { it == SUBMIT_REQUESTS }).isEqualTo(2)
+
+        // Clean-up brands and models set for enabling quirk.
+        ShadowBuild.setBrand("")
+        ShadowBuild.setModel("")
+    }
+}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/internal/audio/AudioSourceTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/internal/audio/AudioSourceTest.kt
index 47dadac..eeaf74b 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/internal/audio/AudioSourceTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/internal/audio/AudioSourceTest.kt
@@ -33,6 +33,7 @@
 import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit.NANOSECONDS
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
@@ -62,6 +63,7 @@
         }
     }
 
+    @Ignore("b/289918974")
     @Test
     fun canStartAndStopAudioSource() {
         // Arrange.
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
index 23b0547..5a638f2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
@@ -36,6 +36,7 @@
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
@@ -54,6 +55,7 @@
 import kotlin.math.abs
 import kotlin.math.absoluteValue
 import kotlin.test.assertTrue
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Test
@@ -384,7 +386,6 @@
 
         // Assert: Check we're settled.
         rule.runOnIdle {
-            assertThat(pagerState.currentPage).isEqualTo(5)
             assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f)
         }
 
@@ -395,6 +396,7 @@
         rule.runOnIdle { assertThat(focusItems).contains("page=5-item=3") }
 
         // Act: Move focus in inner scrollable
+        val previousPage = pagerState.currentPage
         rule.runOnIdle {
             assertTrue {
                 if (vertical) {
@@ -408,7 +410,7 @@
         // Assert: Check we actually scrolled, but didn't move pages.
         rule.runOnIdle {
             assertThat(focusItems).contains("page=5-item=4")
-            assertThat(pagerState.currentPage).isEqualTo(5)
+            assertThat(pagerState.currentPage).isEqualTo(previousPage)
             assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f)
         }
 
@@ -428,11 +430,63 @@
 
         // Assert: Check we moved pages.
         rule.runOnIdle {
-            assertThat(pagerState.currentPage).isEqualTo(6)
+            assertThat(focusItems).contains("page=6-item=0")
             assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f)
         }
     }
 
+    @Test
+    fun focusableContentInPage_focusMoveShouldNotLeavePagesInIntermediateState() {
+        lateinit var pagerFocusRequester: FocusRequester
+
+        createPager(
+            modifier = Modifier.fillMaxSize(),
+            pageCount = { DefaultPageCount },
+            initialPage = 3
+        ) { page ->
+            val focusRequester = remember {
+                FocusRequester().apply {
+                    if (page == 5) pagerFocusRequester = this
+                }
+            }
+
+            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                Box(
+                    modifier = Modifier
+                        .size(64.dp)
+                        .focusRequester(focusRequester)
+                        .focusable()
+                )
+            }
+        }
+
+        // Assert: Pager is settled
+        assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f)
+        assertThat(pagerState.currentPage).isEqualTo(3)
+
+        // Scroll to a page
+        rule.runOnIdle {
+            scope.launch {
+                pagerState.scrollToPage(5)
+            }
+        }
+
+        // Assert: Pager is settled
+        assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f)
+        assertThat(pagerState.currentPage).isEqualTo(5)
+
+        // Act: Request focus.
+        rule.runOnIdle {
+            pagerFocusRequester.requestFocus()
+        }
+
+        // Assert: Pager is settled
+        rule.runOnIdle {
+            assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f)
+            assertThat(pagerState.currentPage).isEqualTo(5)
+        }
+    }
+
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index ffb2dea..9f49b62 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -135,6 +135,8 @@
         orientation == Orientation.Vertical
     )
 
+    val pagerBringIntoViewScroller = remember(state) { PagerBringIntoViewScroller(state) }
+
     LazyLayout(
         modifier = modifier
             .then(state.remeasurementModifier)
@@ -167,7 +169,7 @@
                 state = state,
                 overscrollEffect = overscrollEffect,
                 enabled = userScrollEnabled,
-                bringIntoViewScroller = PagerBringIntoViewScroller
+                bringIntoViewScroller = pagerBringIntoViewScroller
             )
             .dragDirectionDetector(state)
             .nestedScroll(pageNestedScrollConnection),
@@ -286,26 +288,37 @@
     }
 
 @OptIn(ExperimentalFoundationApi::class)
-private val PagerBringIntoViewScroller = object : BringIntoViewScroller {
+private class PagerBringIntoViewScroller(val pagerState: PagerState) : BringIntoViewScroller {
 
     override val scrollAnimationSpec: AnimationSpec<Float> = spring()
 
+    /**
+     * [calculateScrollDistance] for Pager behaves differently than in a normal list. We must
+     * always respect the snapped pages over bringing a child into view. The logic here will
+     * behave like so:
+     *
+     * 1) If a child is outside of the view, start bringing it into view.
+     * 2) If a child's trailing edge is outside of the page bounds and the child is smaller than
+     * the page, scroll until the trailing edge is in view.
+     * 3) Once a child is fully in view, if it is smaller than the page, scroll until the page is
+     * settled.
+     * 4) If the child is larger than the page, scroll until it is partially in view and continue
+     * scrolling until the page is settled.
+     */
     override fun calculateScrollDistance(offset: Float, size: Float, containerSize: Float): Float {
-        val trailingEdge = offset + size
-        val leadingEdge = offset
-
-        val sizeOfItemRequestingFocus = (trailingEdge - leadingEdge).absoluteValue
-        val childSmallerThanParent = sizeOfItemRequestingFocus <= containerSize
-        val initialTargetForLeadingEdge = 0.0f
-        val spaceAvailableToShowItem = containerSize - initialTargetForLeadingEdge
-
-        val targetForLeadingEdge =
-            if (childSmallerThanParent && spaceAvailableToShowItem < sizeOfItemRequestingFocus) {
-                containerSize - sizeOfItemRequestingFocus
+        return if (offset >= containerSize || offset < 0) {
+            offset
+        } else {
+            if (size <= containerSize && (offset + size) > containerSize) {
+                offset // bring into view
             } else {
-                initialTargetForLeadingEdge
+                // are we in a settled position?
+                if (pagerState.currentPageOffsetFraction.absoluteValue == 0.0f) {
+                    0f
+                } else {
+                    offset
+                }
             }
-
-        return leadingEdge - targetForLeadingEdge
+        }
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt
index 764e75d..d382069 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/TestUtils.kt
@@ -298,6 +298,7 @@
         updateCoordinator(coordinator)
         if (coordinator.isAttached) {
             markAsAttached()
+            runAttachLifecycle()
         }
     }
 
@@ -307,6 +308,7 @@
             coordinator.isAttached = false
         }
         if (isAttached) {
+            runDetachLifecycle()
             markAsDetached()
         }
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeAttachOrderTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeAttachOrderTest.kt
index c9a48d3..7bcca13 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeAttachOrderTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/ModifierNodeAttachOrderTest.kt
@@ -17,7 +17,10 @@
 package androidx.compose.ui.node
 
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.ReusableContentHost
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -299,4 +302,40 @@
             log.clear()
         }
     }
+
+    // Regression test for b/289461011
+    @Test
+    fun reusable_nodes_in_movable_content() {
+        var active by mutableStateOf(true)
+        var inBox by mutableStateOf(true)
+        val content = movableContentOf {
+            ReusableContentHost(active = active) {
+                BasicText("Hello World")
+            }
+        }
+
+        rule.setContent {
+            if (inBox) {
+                Box {
+                    content()
+                }
+            } else {
+                content()
+            }
+        }
+
+        rule.runOnIdle {
+            active = false
+        }
+
+        rule.runOnIdle {
+            inBox = false
+        }
+
+        rule.runOnIdle {
+            active = true
+        }
+
+        rule.waitForIdle()
+    }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
index 5616a28..fefc50a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
@@ -221,6 +221,8 @@
             private set
         internal var insertedNodeAwaitingAttachForInvalidation = false
         internal var updatedNodeAwaitingAttachForInvalidation = false
+        private var onAttachRunExpected = false
+        private var onDetachRunExpected = false
         /**
          * Indicates that the node is attached to a [androidx.compose.ui.layout.Layout] which is
          * part of the UI tree.
@@ -262,21 +264,34 @@
             check(!isAttached) { "node attached multiple times" }
             check(coordinator != null) { "attach invoked on a node without a coordinator" }
             isAttached = true
+            onAttachRunExpected = true
         }
 
         internal open fun runAttachLifecycle() {
             check(isAttached) { "Must run markAsAttached() prior to runAttachLifecycle" }
+            check(onAttachRunExpected) { "Must run runAttachLifecycle() only once after " +
+                "markAsAttached()"
+            }
+            onAttachRunExpected = false
             onAttach()
+            onDetachRunExpected = true
         }
 
         internal open fun runDetachLifecycle() {
             check(isAttached) { "node detached multiple times" }
             check(coordinator != null) { "detach invoked on a node without a coordinator" }
+            check(onDetachRunExpected) {
+                "Must run runDetachLifecycle() once after runAttachLifecycle() and before " +
+                    "markAsDetached()"
+            }
+            onDetachRunExpected = false
             onDetach()
         }
 
         internal open fun markAsDetached() {
             check(isAttached) { "Cannot detach a node that is not attached" }
+            check(!onAttachRunExpected) { "Must run runAttachLifecycle() before markAsDetached()" }
+            check(!onDetachRunExpected) { "Must run runDetachLifecycle() before markAsDetached()" }
             isAttached = false
 
             scope?.let {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index fc26143..2fa7171 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -474,11 +474,15 @@
             // is a virtual lookahead root
             lookaheadRoot = _foldedParent?.lookaheadRoot ?: lookaheadRoot
         }
-        nodes.markAsAttached()
+        if (!deactivated) {
+            nodes.markAsAttached()
+        }
         _foldedChildren.forEach { child ->
             child.attach(owner)
         }
-        nodes.runAttachLifecycle()
+        if (!deactivated) {
+            nodes.runAttachLifecycle()
+        }
 
         invalidateMeasurements()
         parent?.invalidateMeasurements()
@@ -487,7 +491,9 @@
         onAttach?.invoke(owner)
 
         layoutDelegate.updateParentData()
-        invalidateFocusOnAttach()
+        if (!deactivated) {
+            invalidateFocusOnAttach()
+        }
     }
 
     /**
@@ -1317,6 +1323,7 @@
     private var deactivated = false
 
     override fun onReuse() {
+        require(isAttached) { "onReuse is only expected on attached node" }
         interopViewFactoryHolder?.onReuse()
         if (deactivated) {
             deactivated = false
diff --git a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
index 3b7b2d8..f1e265b 100644
--- a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
@@ -324,7 +324,7 @@
                 equalTo(accessibilityNodeInfoCompat.unwrap().getExtraRenderingInfo()));
     }
 
-    @SdkSuppress(minSdkVersion = 33)
+    @SdkSuppress(minSdkVersion = 19)
     @SmallTest
     @Test
     public void testSetGetTextSelectable() {
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 6e63749..f6e288e 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -96,8 +96,8 @@
      * </p>
      * <p class="note">
      * <strong>Note:</strong> Views which support these actions should invoke
-     * {@link View#setImportantForAccessibility(int)} with
-     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_YES} to ensure an
+     * {@link ViewCompat#setImportantForAccessibility(View, int)} with
+     * {@link ViewCompat#IMPORTANT_FOR_ACCESSIBILITY_YES} to ensure an
      * {@link android.accessibilityservice.AccessibilityService} can discover the set of supported
      * actions.
      * </p>
@@ -1397,6 +1397,7 @@
     private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x00000004;
     private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x00000008;
     private static final int BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS = 1 << 5;
+    private static final int BOOLEAN_PROPERTY_TEXT_SELECTABLE = 1 << 23;
     private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;
 
     private final AccessibilityNodeInfo mInfo;
@@ -2833,7 +2834,11 @@
 
     /**
      * Gets if the node supports granular scrolling.
-     *
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>Api &lt; 19: Returns false.</li>
+     * </ul>
      * @return True if all scroll actions that could support
      * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise.
      */
@@ -2849,7 +2854,11 @@
      *   {@link android.accessibilityservice.AccessibilityService}.
      *   This class is made immutable before being delivered to an AccessibilityService.
      * </p>
-     *
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>Api &lt; 19: No-op.</li>
+     * </ul>
      * @param granularScrollingSupported True if the node supports granular scrolling, false
      *                                  otherwise.
      *
@@ -2867,11 +2876,12 @@
      *     Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
      *     also be selectable. But not all UIs will populate this field, so services should consider
      *     'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
-     *  Compatibility:
-     *  <ul>
-     *      <li>Api &lt; 33: Returns false.</li>
-     *  </ul>
      * </p>
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>Api &lt; 19: Returns false.</li>
+     * </ul>
      *
      * @see #isEditable
      * @return True if the node has selectable text.
@@ -2880,7 +2890,7 @@
         if (Build.VERSION.SDK_INT >= 33) {
             return Api33Impl.isTextSelectable(mInfo);
         } else {
-            return false;
+            return getBooleanProperty(BOOLEAN_PROPERTY_TEXT_SELECTABLE);
         }
     }
 
@@ -2890,10 +2900,12 @@
      *   <strong>Note:</strong> Cannot be called from an
      *   {@link android.accessibilityservice.AccessibilityService}.
      *   This class is made immutable before being delivered to an AccessibilityService.
-     *  Compatibility:
-     *  <ul>
-     *      <li>Api &lt; 33: Does not operate.</li>
-     *  </ul>
+     * </p>
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>Api &lt; 19: Does not operate.</li>
+     * </ul>
      * </p>
      *
      * @param selectableText True if the node has selectable text, false otherwise.
@@ -2903,6 +2915,8 @@
     public void setTextSelectable(boolean selectableText) {
         if (Build.VERSION.SDK_INT >= 33) {
             Api33Impl.setTextSelectable(mInfo, selectableText);
+        } else {
+            setBooleanProperty(BOOLEAN_PROPERTY_TEXT_SELECTABLE, selectableText);
         }
     }
 
@@ -3167,8 +3181,8 @@
      * than 19.
      */
     public @Nullable CharSequence getStateDescription() {
-        if (BuildCompat.isAtLeastR()) {
-            return mInfo.getStateDescription();
+        if (Build.VERSION.SDK_INT >= 30) {
+            return  Api30Impl.getStateDescription(mInfo);
         } else if (Build.VERSION.SDK_INT >= 19) {
             return Api19Impl.getExtras(mInfo).getCharSequence(STATE_DESCRIPTION_KEY);
         }
@@ -3202,8 +3216,8 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setStateDescription(@Nullable CharSequence stateDescription) {
-        if (BuildCompat.isAtLeastR()) {
-            mInfo.setStateDescription(stateDescription);
+        if (Build.VERSION.SDK_INT >= 30) {
+            Api30Impl.setStateDescription(mInfo, stateDescription);
         } else if (Build.VERSION.SDK_INT >= 19) {
             Api19Impl.getExtras(mInfo).putCharSequence(STATE_DESCRIPTION_KEY, stateDescription);
         }
@@ -3215,10 +3229,9 @@
      * @return the unique id or null if android version smaller
      * than 19.
      */
-    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
     public @Nullable String getUniqueId() {
-        if (BuildCompat.isAtLeastT()) {
-            return mInfo.getUniqueId();
+        if (Build.VERSION.SDK_INT >= 33) {
+            return Api33Impl.getUniqueId(mInfo);
         } else if (Build.VERSION.SDK_INT >= 19) {
             return Api19Impl.getExtras(mInfo).getString(UNIQUE_ID_KEY);
         }
@@ -3236,10 +3249,9 @@
      * @param uniqueId the unique id of this node.
      * @throws IllegalStateException If called from an AccessibilityService.
      */
-    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
     public void setUniqueId(@Nullable String uniqueId) {
-        if (BuildCompat.isAtLeastT()) {
-            mInfo.setUniqueId(uniqueId);
+        if (Build.VERSION.SDK_INT >= 33) {
+            Api33Impl.setUniqueId(mInfo, uniqueId);
         } else if (Build.VERSION.SDK_INT >= 19) {
             Api19Impl.getExtras(mInfo).putString(UNIQUE_ID_KEY, uniqueId);
         }
@@ -4338,7 +4350,7 @@
      * Returns whether node represents a heading.
      * <p><strong>Note:</strong> Returns {@code true} if either {@link #setHeading(boolean)}
      * marks this node as a heading or if the node has a {@link CollectionItemInfoCompat} that marks
-     * it as such, to accomodate apps that use the now-deprecated API.</p>
+     * it as such, to accommodate apps that use the now-deprecated API.</p>
      *
      * @return {@code true} if the node is a heading, {@code false} otherwise.
      */
@@ -4594,8 +4606,12 @@
         builder.append("; packageName: ").append(getPackageName());
         builder.append("; className: ").append(getClassName());
         builder.append("; text: ").append(getText());
+        builder.append("; error: ").append(getError());
+        builder.append("; maxTextLength: ").append(getMaxTextLength());
+        builder.append("; stateDescription: ").append(getStateDescription());
         builder.append("; contentDescription: ").append(getContentDescription());
-        builder.append("; viewId: ").append(getViewIdResourceName());
+        builder.append("; tooltipText: ").append(getTooltipText());
+        builder.append("; viewIdResName: ").append(getViewIdResourceName());
         builder.append("; uniqueId: ").append(getUniqueId());
 
         builder.append("; checkable: ").append(isCheckable());
@@ -4605,9 +4621,14 @@
         builder.append("; selected: ").append(isSelected());
         builder.append("; clickable: ").append(isClickable());
         builder.append("; longClickable: ").append(isLongClickable());
+        builder.append("; contextClickable: ").append(isContextClickable());
         builder.append("; enabled: ").append(isEnabled());
         builder.append("; password: ").append(isPassword());
         builder.append("; scrollable: " + isScrollable());
+        builder.append("; granularScrollingSupported: ").append(isGranularScrollingSupported());
+        builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
+        builder.append("; visible: ").append(isVisibleToUser());
+        builder.append("; isTextSelectable: ").append(isTextSelectable());
 
         builder.append("; [");
         if (Build.VERSION.SDK_INT >= 21) {
@@ -4748,6 +4769,24 @@
         }
     }
 
+    @RequiresApi(30)
+    private static class Api30Impl {
+        private Api30Impl() {
+            // This class is non instantiable.
+        }
+
+        @DoNotInline
+        public static void setStateDescription(AccessibilityNodeInfo info,
+                CharSequence stateDescription) {
+            info.setStateDescription(stateDescription);
+        }
+
+        @DoNotInline
+        public static CharSequence getStateDescription(AccessibilityNodeInfo info) {
+            return info.getStateDescription();
+        }
+    }
+
     @RequiresApi(33)
     private static class Api33Impl {
         private Api33Impl() {
@@ -4769,6 +4808,16 @@
         public static void setTextSelectable(AccessibilityNodeInfo info, boolean selectable) {
             info.setTextSelectable(selectable);
         }
+
+        @DoNotInline
+        public static String getUniqueId(AccessibilityNodeInfo info) {
+            return info.getUniqueId();
+        }
+
+        @DoNotInline
+        public static void setUniqueId(AccessibilityNodeInfo info, String uniqueId) {
+            info.setUniqueId(uniqueId);
+        }
     }
 
     @RequiresApi(19)
diff --git a/health/connect/connect-client/lint-baseline.xml b/health/connect/connect-client/lint-baseline.xml
index dfb1ec9..d0e77cb 100644
--- a/health/connect/connect-client/lint-baseline.xml
+++ b/health/connect/connect-client/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="BanSynchronizedMethods"
@@ -11,42 +11,6 @@
     </issue>
 
     <issue
-        id="PrereleaseSdkCoreDependency"
-        message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
-        errorLine1="            if (BuildCompat.isAtLeastU()) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
-    </issue>
-
-    <issue
-        id="PrereleaseSdkCoreDependency"
-        message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
-        errorLine1="            if (BuildCompat.isAtLeastU()) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
-    </issue>
-
-    <issue
-        id="PrereleaseSdkCoreDependency"
-        message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
-        errorLine1="            if (BuildCompat.isAtLeastU()) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
-    </issue>
-
-    <issue
-        id="PrereleaseSdkCoreDependency"
-        message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
-        errorLine1="            if (BuildCompat.isAtLeastU()) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
-    </issue>
-
-    <issue
         id="RequireUnstableAidlAnnotation"
         message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
         errorLine1="parcelable AggregateDataRequest;"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java
index c5b8a8e..597a6c1 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java
@@ -16,8 +16,6 @@
 
 /**
  * Helps with conversions to the platform record and API objects.
- *
- * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 package androidx.health.connect.client.impl.platform.records;
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java
index f8b9cb7..a743964 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java
@@ -16,8 +16,6 @@
 
 /**
  * Helps with conversions to the platform record and API objects.
- *
- * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 package androidx.health.connect.client.impl.platform.response;
diff --git a/libraryversions.toml b/libraryversions.toml
index 386c109..3c22280 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -89,7 +89,7 @@
 LOADER = "1.2.0-alpha01"
 MEDIA = "1.7.0-alpha02"
 MEDIA2 = "1.3.0-alpha01"
-MEDIAROUTER = "1.6.0-alpha05"
+MEDIAROUTER = "1.6.0-beta01"
 METRICS = "1.0.0-alpha05"
 NAVIGATION = "2.7.0-beta02"
 PAGING = "3.3.0-alpha01"
diff --git a/mediarouter/mediarouter-testing/api/1.6.0-beta01.txt b/mediarouter/mediarouter-testing/api/1.6.0-beta01.txt
new file mode 100644
index 0000000..14e1df6
--- /dev/null
+++ b/mediarouter/mediarouter-testing/api/1.6.0-beta01.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.mediarouter.testing {
+
+  public class MediaRouterTestHelper {
+    method @MainThread public static void resetMediaRouter();
+  }
+
+}
+
diff --git a/mediarouter/mediarouter-testing/api/res-1.6.0-beta01.txt b/mediarouter/mediarouter-testing/api/res-1.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mediarouter/mediarouter-testing/api/res-1.6.0-beta01.txt
diff --git a/mediarouter/mediarouter-testing/api/restricted_1.6.0-beta01.txt b/mediarouter/mediarouter-testing/api/restricted_1.6.0-beta01.txt
new file mode 100644
index 0000000..14e1df6
--- /dev/null
+++ b/mediarouter/mediarouter-testing/api/restricted_1.6.0-beta01.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.mediarouter.testing {
+
+  public class MediaRouterTestHelper {
+    method @MainThread public static void resetMediaRouter();
+  }
+
+}
+
diff --git a/mediarouter/mediarouter/api/1.6.0-beta01.txt b/mediarouter/mediarouter/api/1.6.0-beta01.txt
new file mode 100644
index 0000000..00e0b0ae6
--- /dev/null
+++ b/mediarouter/mediarouter/api/1.6.0-beta01.txt
@@ -0,0 +1,621 @@
+// Signature format: 4.0
+package androidx.mediarouter.app {
+
+  public class MediaRouteActionProvider extends androidx.core.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method @Deprecated public void enableDynamicGroup();
+    method public androidx.mediarouter.app.MediaRouteDialogFactory getDialogFactory();
+    method public androidx.mediarouter.app.MediaRouteButton? getMediaRouteButton();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public androidx.mediarouter.app.MediaRouteButton onCreateMediaRouteButton();
+    method @Deprecated public void setAlwaysVisible(boolean);
+    method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet?);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet?, int);
+    method @Deprecated public void enableDynamicGroup();
+    method public androidx.mediarouter.app.MediaRouteDialogFactory getDialogFactory();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method @Deprecated public void setAlwaysVisible(boolean);
+    method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable?);
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends androidx.appcompat.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<androidx.mediarouter.media.MediaRouter.RouteInfo!>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public androidx.mediarouter.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle?);
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends androidx.appcompat.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View? getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getMediaSession();
+    method public androidx.mediarouter.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View? onCreateMediaControlView(android.os.Bundle?);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public androidx.mediarouter.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle?);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static androidx.mediarouter.app.MediaRouteDialogFactory getDefault();
+    method public androidx.mediarouter.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public androidx.mediarouter.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends androidx.fragment.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public androidx.mediarouter.media.MediaRouter getMediaRouter();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public androidx.mediarouter.media.MediaRouter.Callback? onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public final class SystemOutputSwitcherDialogController {
+    method public static boolean showDialog(android.content.Context);
+  }
+
+}
+
+package androidx.mediarouter.media {
+
+  public final class MediaControlIntent {
+    field public static final String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaItemStatus? fromBundle(android.os.Bundle?);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle? getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(androidx.mediarouter.media.MediaItemStatus);
+    ctor public MediaItemStatus.Builder(int);
+    method public androidx.mediarouter.media.MediaItemStatus build();
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setContentDuration(long);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setContentPosition(long);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setExtras(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static androidx.mediarouter.media.MediaRouteDescriptor? fromBundle(android.os.Bundle?);
+    method public java.util.Set<java.lang.String!> getAllowedPackages();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter!> getControlFilters();
+    method public java.util.Set<java.lang.String!> getDeduplicationIds();
+    method public String? getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle? getExtras();
+    method public android.net.Uri? getIconUri();
+    method public String getId();
+    method public String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender? getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method @Deprecated public boolean isConnecting();
+    method public boolean isDynamicGroupRoute();
+    method public boolean isEnabled();
+    method public boolean isValid();
+    method public boolean isVisibilityPublic();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(androidx.mediarouter.media.MediaRouteDescriptor);
+    ctor public MediaRouteDescriptor.Builder(String, String);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter!>);
+    method public androidx.mediarouter.media.MediaRouteDescriptor build();
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder clearControlFilters();
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method @Deprecated public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeduplicationIds(java.util.Set<java.lang.String!>);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDescription(String?);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setId(String);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setIsDynamicGroupRoute(boolean);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setName(String);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender?);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityPublic();
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityRestricted(java.util.Set<java.lang.String!>);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(androidx.mediarouter.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaRouteDiscoveryRequest? fromBundle(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final androidx.mediarouter.media.MediaRouteProviderDescriptor? getDescriptor();
+    method public final androidx.mediarouter.media.MediaRouteDiscoveryRequest? getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final androidx.mediarouter.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController? onCreateDynamicGroupRouteController(String);
+    method public androidx.mediarouter.media.MediaRouteProvider.RouteController? onCreateRouteController(String);
+    method public void onDiscoveryRequestChanged(androidx.mediarouter.media.MediaRouteDiscoveryRequest?);
+    method public final void setCallback(androidx.mediarouter.media.MediaRouteProvider.Callback?);
+    method public final void setDescriptor(androidx.mediarouter.media.MediaRouteProviderDescriptor?);
+    method public final void setDiscoveryRequest(androidx.mediarouter.media.MediaRouteDiscoveryRequest?);
+  }
+
+  public abstract static class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(androidx.mediarouter.media.MediaRouteProvider, androidx.mediarouter.media.MediaRouteProviderDescriptor?);
+  }
+
+  public abstract static class MediaRouteProvider.DynamicGroupRouteController extends androidx.mediarouter.media.MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.DynamicGroupRouteController();
+    method public String? getGroupableSelectionTitle();
+    method public String? getTransferableSectionTitle();
+    method public final void notifyDynamicRoutesChanged(androidx.mediarouter.media.MediaRouteDescriptor, java.util.Collection<androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor!>);
+    method @Deprecated public final void notifyDynamicRoutesChanged(java.util.Collection<androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor!>);
+    method public abstract void onAddMemberRoute(String);
+    method public abstract void onRemoveMemberRoute(String);
+    method public abstract void onUpdateMemberRoutes(java.util.List<java.lang.String!>?);
+  }
+
+  public static final class MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor {
+    method public androidx.mediarouter.media.MediaRouteDescriptor getRouteDescriptor();
+    method public int getSelectionState();
+    method public boolean isGroupable();
+    method public boolean isTransferable();
+    method public boolean isUnselectable();
+    field public static final int SELECTED = 3; // 0x3
+    field public static final int SELECTING = 2; // 0x2
+    field public static final int UNSELECTED = 1; // 0x1
+    field public static final int UNSELECTING = 0; // 0x0
+  }
+
+  public static final class MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder {
+    ctor public MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder(androidx.mediarouter.media.MediaRouteDescriptor);
+    ctor public MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder(androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor build();
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setIsGroupable(boolean);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setIsTransferable(boolean);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setIsUnselectable(boolean);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setSelectionState(int);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public String getPackageName();
+  }
+
+  public abstract static class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, androidx.mediarouter.media.MediaRouter.ControlRequestCallback?);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method @Deprecated public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaRouteProviderDescriptor? fromBundle(android.os.Bundle?);
+    method public java.util.List<androidx.mediarouter.media.MediaRouteDescriptor!> getRoutes();
+    method public boolean isValid();
+    method public boolean supportsDynamicGroupRoute();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(androidx.mediarouter.media.MediaRouteProviderDescriptor);
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor.Builder addRoute(androidx.mediarouter.media.MediaRouteDescriptor);
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<androidx.mediarouter.media.MediaRouteDescriptor!>);
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor build();
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor.Builder setSupportsDynamicGroupRoute(boolean);
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public androidx.mediarouter.media.MediaRouteProvider? getMediaRouteProvider();
+    method public android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.mediarouter.media.MediaRouteProvider? onCreateMediaRouteProvider();
+    field public static final String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(androidx.mediarouter.media.MediaRouteSelector);
+    method public static androidx.mediarouter.media.MediaRouteSelector? fromBundle(android.os.Bundle?);
+    method public java.util.List<java.lang.String!> getControlCategories();
+    method public boolean hasControlCategory(String?);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter!>?);
+    field public static final androidx.mediarouter.media.MediaRouteSelector! EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(androidx.mediarouter.media.MediaRouteSelector);
+    method public androidx.mediarouter.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String!>);
+    method public androidx.mediarouter.media.MediaRouteSelector.Builder addControlCategory(String);
+    method public androidx.mediarouter.media.MediaRouteSelector.Builder addSelector(androidx.mediarouter.media.MediaRouteSelector);
+    method public androidx.mediarouter.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback);
+    method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback, int);
+    method @MainThread public void addProvider(androidx.mediarouter.media.MediaRouteProvider);
+    method @Deprecated @MainThread public void addRemoteControlClient(Object);
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo? getBluetoothRoute();
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo getDefaultRoute();
+    method @MainThread public static androidx.mediarouter.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getMediaSessionToken();
+    method @MainThread public java.util.List<androidx.mediarouter.media.MediaRouter.ProviderInfo!> getProviders();
+    method @MainThread public androidx.mediarouter.media.MediaRouterParams? getRouterParams();
+    method @MainThread public java.util.List<androidx.mediarouter.media.MediaRouter.RouteInfo!> getRoutes();
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo getSelectedRoute();
+    method @MainThread public boolean isRouteAvailable(androidx.mediarouter.media.MediaRouteSelector, int);
+    method @MainThread public void removeCallback(androidx.mediarouter.media.MediaRouter.Callback);
+    method @MainThread public void removeProvider(androidx.mediarouter.media.MediaRouteProvider);
+    method @MainThread public void removeRemoteControlClient(Object);
+    method @MainThread public void selectRoute(androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method @MainThread public void setMediaSession(Object?);
+    method @MainThread public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat?);
+    method @MainThread public void setOnPrepareTransferListener(androidx.mediarouter.media.MediaRouter.OnPrepareTransferListener?);
+    method @MainThread public void setRouteListingPreference(androidx.mediarouter.media.RouteListingPreference?);
+    method @MainThread public void setRouterParams(androidx.mediarouter.media.MediaRouterParams?);
+    method @MainThread public void unselect(int);
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo updateSelectedRoute(androidx.mediarouter.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method @Deprecated public void onRouteSelected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo, int);
+    method public void onRouteSelected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo, int, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method @Deprecated public void onRouteUnselected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+  }
+
+  public abstract static class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(String?, android.os.Bundle?);
+    method public void onResult(android.os.Bundle?);
+  }
+
+  public static interface MediaRouter.OnPrepareTransferListener {
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>? onPrepareTransfer(androidx.mediarouter.media.MediaRouter.RouteInfo, androidx.mediarouter.media.MediaRouter.RouteInfo);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public String getPackageName();
+    method @MainThread public androidx.mediarouter.media.MediaRouteProvider getProviderInstance();
+    method @MainThread public java.util.List<androidx.mediarouter.media.MediaRouter.RouteInfo!> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter!> getControlFilters();
+    method public String? getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle? getExtras();
+    method public android.net.Uri? getIconUri();
+    method public String getId();
+    method public String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method @MainThread public android.view.Display? getPresentationDisplay();
+    method public androidx.mediarouter.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender? getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method @MainThread public boolean isBluetooth();
+    method @Deprecated public boolean isConnecting();
+    method @MainThread public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method @MainThread public boolean isSelected();
+    method @MainThread public boolean matchesSelector(androidx.mediarouter.media.MediaRouteSelector);
+    method @MainThread public void requestSetVolume(int);
+    method @MainThread public void requestUpdateVolume(int);
+    method @MainThread public void select();
+    method @MainThread public void sendControlRequest(android.content.Intent, androidx.mediarouter.media.MediaRouter.ControlRequestCallback?);
+    method @MainThread public boolean supportsControlAction(String, String);
+    method @MainThread public boolean supportsControlCategory(String);
+    method @MainThread public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_AUDIO_VIDEO_RECEIVER = 4; // 0x4
+    field public static final int DEVICE_TYPE_CAR = 9; // 0x9
+    field public static final int DEVICE_TYPE_COMPUTER = 7; // 0x7
+    field public static final int DEVICE_TYPE_GAME_CONSOLE = 8; // 0x8
+    field public static final int DEVICE_TYPE_GROUP = 1000; // 0x3e8
+    field public static final int DEVICE_TYPE_SMARTWATCH = 10; // 0xa
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TABLET = 5; // 0x5
+    field public static final int DEVICE_TYPE_TABLET_DOCKED = 6; // 0x6
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public class MediaRouterParams {
+    method public int getDialogType();
+    method public boolean isMediaTransferReceiverEnabled();
+    method public boolean isOutputSwitcherEnabled();
+    method public boolean isTransferToLocalEnabled();
+    field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
+    field public static final int DIALOG_TYPE_DYNAMIC_GROUP = 2; // 0x2
+    field public static final String ENABLE_GROUP_VOLUME_UX = "androidx.mediarouter.media.MediaRouterParams.ENABLE_GROUP_VOLUME_UX";
+  }
+
+  public static final class MediaRouterParams.Builder {
+    ctor public MediaRouterParams.Builder();
+    ctor public MediaRouterParams.Builder(androidx.mediarouter.media.MediaRouterParams);
+    method public androidx.mediarouter.media.MediaRouterParams build();
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setDialogType(int);
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverEnabled(boolean);
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setOutputSwitcherEnabled(boolean);
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setTransferToLocalEnabled(boolean);
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaSessionStatus? fromBundle(android.os.Bundle?);
+    method public android.os.Bundle? getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(androidx.mediarouter.media.MediaSessionStatus);
+    ctor public MediaSessionStatus.Builder(int);
+    method public androidx.mediarouter.media.MediaSessionStatus build();
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setExtras(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setSessionState(int);
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaTransferReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaTransferReceiver();
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void enqueue(android.net.Uri, String?, android.os.Bundle?, long, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public String? getSessionId();
+    method public void getSessionStatus(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void getStatus(String, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void play(android.net.Uri, String?, android.os.Bundle?, long, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public void release();
+    method public void remove(String, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public void resume(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void seek(String, long, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public void sendMessage(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void setOnMessageReceivedListener(androidx.mediarouter.media.RemotePlaybackClient.OnMessageReceivedListener?);
+    method public void setSessionId(String?);
+    method public void setStatusCallback(androidx.mediarouter.media.RemotePlaybackClient.StatusCallback?);
+    method public void startSession(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void stop(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+  }
+
+  public abstract static class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(String?, int, android.os.Bundle?);
+  }
+
+  public abstract static class RemotePlaybackClient.ItemActionCallback extends androidx.mediarouter.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, String, androidx.mediarouter.media.MediaSessionStatus?, String, androidx.mediarouter.media.MediaItemStatus);
+  }
+
+  public static interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public void onMessageReceived(String, android.os.Bundle?);
+  }
+
+  public abstract static class RemotePlaybackClient.SessionActionCallback extends androidx.mediarouter.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, String, androidx.mediarouter.media.MediaSessionStatus?);
+  }
+
+  public abstract static class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle?, String, androidx.mediarouter.media.MediaSessionStatus?, String, androidx.mediarouter.media.MediaItemStatus);
+    method public void onSessionChanged(String?);
+    method public void onSessionStatusChanged(android.os.Bundle?, String, androidx.mediarouter.media.MediaSessionStatus?);
+  }
+
+  public final class RouteListingPreference {
+    method public java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!> getItems();
+    method public android.content.ComponentName? getLinkedItemComponentName();
+    method public boolean getUseSystemOrdering();
+    field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
+    field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
+  }
+
+  public static final class RouteListingPreference.Builder {
+    ctor public RouteListingPreference.Builder();
+    method public androidx.mediarouter.media.RouteListingPreference build();
+    method public androidx.mediarouter.media.RouteListingPreference.Builder setItems(java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!>);
+    method public androidx.mediarouter.media.RouteListingPreference.Builder setLinkedItemComponentName(android.content.ComponentName?);
+    method public androidx.mediarouter.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
+  }
+
+  public static final class RouteListingPreference.Item {
+    method public CharSequence? getCustomSubtextMessage();
+    method public int getFlags();
+    method public String getRouteId();
+    method public int getSelectionBehavior();
+    method public int getSubText();
+    field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
+    field public static final int FLAG_ONGOING_SESSION_MANAGED = 2; // 0x2
+    field public static final int FLAG_SUGGESTED = 4; // 0x4
+    field public static final int SELECTION_BEHAVIOR_GO_TO_APP = 2; // 0x2
+    field public static final int SELECTION_BEHAVIOR_NONE = 0; // 0x0
+    field public static final int SELECTION_BEHAVIOR_TRANSFER = 1; // 0x1
+    field public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 4; // 0x4
+    field public static final int SUBTEXT_CUSTOM = 10000; // 0x2710
+    field public static final int SUBTEXT_DEVICE_LOW_POWER = 5; // 0x5
+    field public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 3; // 0x3
+    field public static final int SUBTEXT_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int SUBTEXT_NONE = 0; // 0x0
+    field public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 2; // 0x2
+    field public static final int SUBTEXT_TRACK_UNSUPPORTED = 7; // 0x7
+    field public static final int SUBTEXT_UNAUTHORIZED = 6; // 0x6
+  }
+
+  public static final class RouteListingPreference.Item.Builder {
+    ctor public RouteListingPreference.Item.Builder(String);
+    method public androidx.mediarouter.media.RouteListingPreference.Item build();
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setCustomSubtextMessage(CharSequence?);
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setFlags(int);
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSelectionBehavior(int);
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSubText(int);
+  }
+
+}
+
diff --git a/mediarouter/mediarouter/api/res-1.6.0-beta01.txt b/mediarouter/mediarouter/api/res-1.6.0-beta01.txt
new file mode 100644
index 0000000..620c3fe
--- /dev/null
+++ b/mediarouter/mediarouter/api/res-1.6.0-beta01.txt
@@ -0,0 +1,4 @@
+dimen mediarouter_chooser_list_item_padding_bottom
+dimen mediarouter_chooser_list_item_padding_end
+dimen mediarouter_chooser_list_item_padding_start
+dimen mediarouter_chooser_list_item_padding_top
diff --git a/mediarouter/mediarouter/api/restricted_1.6.0-beta01.txt b/mediarouter/mediarouter/api/restricted_1.6.0-beta01.txt
new file mode 100644
index 0000000..00e0b0ae6
--- /dev/null
+++ b/mediarouter/mediarouter/api/restricted_1.6.0-beta01.txt
@@ -0,0 +1,621 @@
+// Signature format: 4.0
+package androidx.mediarouter.app {
+
+  public class MediaRouteActionProvider extends androidx.core.view.ActionProvider {
+    ctor public MediaRouteActionProvider(android.content.Context);
+    method @Deprecated public void enableDynamicGroup();
+    method public androidx.mediarouter.app.MediaRouteDialogFactory getDialogFactory();
+    method public androidx.mediarouter.app.MediaRouteButton? getMediaRouteButton();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public android.view.View onCreateActionView();
+    method public androidx.mediarouter.app.MediaRouteButton onCreateMediaRouteButton();
+    method @Deprecated public void setAlwaysVisible(boolean);
+    method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteButton extends android.view.View {
+    ctor public MediaRouteButton(android.content.Context);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet?);
+    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet?, int);
+    method @Deprecated public void enableDynamicGroup();
+    method public androidx.mediarouter.app.MediaRouteDialogFactory getDialogFactory();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method @Deprecated public void setAlwaysVisible(boolean);
+    method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
+    method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable?);
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+    method public boolean showDialog();
+  }
+
+  public class MediaRouteChooserDialog extends androidx.appcompat.app.AppCompatDialog {
+    ctor public MediaRouteChooserDialog(android.content.Context);
+    ctor public MediaRouteChooserDialog(android.content.Context, int);
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public boolean onFilterRoute(androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onFilterRoutes(java.util.List<androidx.mediarouter.media.MediaRouter.RouteInfo!>);
+    method public void refreshRoutes();
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteChooserDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public MediaRouteChooserDialogFragment();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public androidx.mediarouter.app.MediaRouteChooserDialog onCreateChooserDialog(android.content.Context, android.os.Bundle?);
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public class MediaRouteControllerDialog extends androidx.appcompat.app.AlertDialog {
+    ctor public MediaRouteControllerDialog(android.content.Context);
+    ctor public MediaRouteControllerDialog(android.content.Context, int);
+    method public android.view.View? getMediaControlView();
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getMediaSession();
+    method public androidx.mediarouter.media.MediaRouter.RouteInfo getRoute();
+    method public boolean isVolumeControlEnabled();
+    method public android.view.View? onCreateMediaControlView(android.os.Bundle?);
+    method public void setVolumeControlEnabled(boolean);
+  }
+
+  public class MediaRouteControllerDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public MediaRouteControllerDialogFragment();
+    method public androidx.mediarouter.app.MediaRouteControllerDialog onCreateControllerDialog(android.content.Context, android.os.Bundle?);
+  }
+
+  public class MediaRouteDialogFactory {
+    ctor public MediaRouteDialogFactory();
+    method public static androidx.mediarouter.app.MediaRouteDialogFactory getDefault();
+    method public androidx.mediarouter.app.MediaRouteChooserDialogFragment onCreateChooserDialogFragment();
+    method public androidx.mediarouter.app.MediaRouteControllerDialogFragment onCreateControllerDialogFragment();
+  }
+
+  public class MediaRouteDiscoveryFragment extends androidx.fragment.app.Fragment {
+    ctor public MediaRouteDiscoveryFragment();
+    method public androidx.mediarouter.media.MediaRouter getMediaRouter();
+    method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
+    method public androidx.mediarouter.media.MediaRouter.Callback? onCreateCallback();
+    method public int onPrepareCallbackFlags();
+    method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
+  }
+
+  public final class SystemOutputSwitcherDialogController {
+    method public static boolean showDialog(android.content.Context);
+  }
+
+}
+
+package androidx.mediarouter.media {
+
+  public final class MediaControlIntent {
+    field public static final String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
+    field public static final String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
+    field public static final String ACTION_GET_SESSION_STATUS = "android.media.intent.action.GET_SESSION_STATUS";
+    field public static final String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
+    field public static final String ACTION_PAUSE = "android.media.intent.action.PAUSE";
+    field public static final String ACTION_PLAY = "android.media.intent.action.PLAY";
+    field public static final String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+    field public static final String ACTION_RESUME = "android.media.intent.action.RESUME";
+    field public static final String ACTION_SEEK = "android.media.intent.action.SEEK";
+    field public static final String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
+    field public static final String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
+    field public static final String ACTION_STOP = "android.media.intent.action.STOP";
+    field public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    field public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    field public static final String CATEGORY_REMOTE_PLAYBACK = "android.media.intent.category.REMOTE_PLAYBACK";
+    field public static final int ERROR_INVALID_ITEM_ID = 3; // 0x3
+    field public static final int ERROR_INVALID_SESSION_ID = 2; // 0x2
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 1; // 0x1
+    field public static final String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
+    field public static final String EXTRA_ITEM_CONTENT_POSITION = "android.media.intent.extra.ITEM_POSITION";
+    field public static final String EXTRA_ITEM_HTTP_HEADERS = "android.media.intent.extra.HTTP_HEADERS";
+    field public static final String EXTRA_ITEM_ID = "android.media.intent.extra.ITEM_ID";
+    field public static final String EXTRA_ITEM_METADATA = "android.media.intent.extra.ITEM_METADATA";
+    field public static final String EXTRA_ITEM_STATUS = "android.media.intent.extra.ITEM_STATUS";
+    field public static final String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
+    field public static final String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
+    field public static final String EXTRA_MESSAGE_RECEIVER = "android.media.intent.extra.MESSAGE_RECEIVER";
+    field public static final String EXTRA_SESSION_ID = "android.media.intent.extra.SESSION_ID";
+    field public static final String EXTRA_SESSION_STATUS = "android.media.intent.extra.SESSION_STATUS";
+    field public static final String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
+  }
+
+  public final class MediaItemMetadata {
+    field public static final String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
+    field public static final String KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
+    field public static final String KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public final class MediaItemStatus {
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaItemStatus? fromBundle(android.os.Bundle?);
+    method public long getContentDuration();
+    method public long getContentPosition();
+    method public android.os.Bundle? getExtras();
+    method public int getPlaybackState();
+    method public long getTimestamp();
+    field public static final String EXTRA_HTTP_RESPONSE_HEADERS = "android.media.status.extra.HTTP_RESPONSE_HEADERS";
+    field public static final String EXTRA_HTTP_STATUS_CODE = "android.media.status.extra.HTTP_STATUS_CODE";
+    field public static final int PLAYBACK_STATE_BUFFERING = 3; // 0x3
+    field public static final int PLAYBACK_STATE_CANCELED = 5; // 0x5
+    field public static final int PLAYBACK_STATE_ERROR = 7; // 0x7
+    field public static final int PLAYBACK_STATE_FINISHED = 4; // 0x4
+    field public static final int PLAYBACK_STATE_INVALIDATED = 6; // 0x6
+    field public static final int PLAYBACK_STATE_PAUSED = 2; // 0x2
+    field public static final int PLAYBACK_STATE_PENDING = 0; // 0x0
+    field public static final int PLAYBACK_STATE_PLAYING = 1; // 0x1
+  }
+
+  public static final class MediaItemStatus.Builder {
+    ctor public MediaItemStatus.Builder(androidx.mediarouter.media.MediaItemStatus);
+    ctor public MediaItemStatus.Builder(int);
+    method public androidx.mediarouter.media.MediaItemStatus build();
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setContentDuration(long);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setContentPosition(long);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setExtras(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setPlaybackState(int);
+    method public androidx.mediarouter.media.MediaItemStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaRouteDescriptor {
+    method public android.os.Bundle asBundle();
+    method public boolean canDisconnectAndKeepPlaying();
+    method public static androidx.mediarouter.media.MediaRouteDescriptor? fromBundle(android.os.Bundle?);
+    method public java.util.Set<java.lang.String!> getAllowedPackages();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter!> getControlFilters();
+    method public java.util.Set<java.lang.String!> getDeduplicationIds();
+    method public String? getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle? getExtras();
+    method public android.net.Uri? getIconUri();
+    method public String getId();
+    method public String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method public int getPresentationDisplayId();
+    method public android.content.IntentSender? getSettingsActivity();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method @Deprecated public boolean isConnecting();
+    method public boolean isDynamicGroupRoute();
+    method public boolean isEnabled();
+    method public boolean isValid();
+    method public boolean isVisibilityPublic();
+  }
+
+  public static final class MediaRouteDescriptor.Builder {
+    ctor public MediaRouteDescriptor.Builder(androidx.mediarouter.media.MediaRouteDescriptor);
+    ctor public MediaRouteDescriptor.Builder(String, String);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder addControlFilter(android.content.IntentFilter);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder addControlFilters(java.util.Collection<android.content.IntentFilter!>);
+    method public androidx.mediarouter.media.MediaRouteDescriptor build();
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder clearControlFilters();
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
+    method @Deprecated public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnecting(boolean);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnectionState(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeduplicationIds(java.util.Set<java.lang.String!>);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDescription(String?);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeviceType(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setEnabled(boolean);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setExtras(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setIconUri(android.net.Uri);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setId(String);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setIsDynamicGroupRoute(boolean);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setName(String);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPlaybackStream(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPlaybackType(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender?);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityPublic();
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityRestricted(java.util.Set<java.lang.String!>);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolume(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
+    method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeMax(int);
+  }
+
+  public final class MediaRouteDiscoveryRequest {
+    ctor public MediaRouteDiscoveryRequest(androidx.mediarouter.media.MediaRouteSelector, boolean);
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaRouteDiscoveryRequest? fromBundle(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaRouteSelector getSelector();
+    method public boolean isActiveScan();
+    method public boolean isValid();
+  }
+
+  public abstract class MediaRouteProvider {
+    ctor public MediaRouteProvider(android.content.Context);
+    method public final android.content.Context getContext();
+    method public final androidx.mediarouter.media.MediaRouteProviderDescriptor? getDescriptor();
+    method public final androidx.mediarouter.media.MediaRouteDiscoveryRequest? getDiscoveryRequest();
+    method public final android.os.Handler getHandler();
+    method public final androidx.mediarouter.media.MediaRouteProvider.ProviderMetadata getMetadata();
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController? onCreateDynamicGroupRouteController(String);
+    method public androidx.mediarouter.media.MediaRouteProvider.RouteController? onCreateRouteController(String);
+    method public void onDiscoveryRequestChanged(androidx.mediarouter.media.MediaRouteDiscoveryRequest?);
+    method public final void setCallback(androidx.mediarouter.media.MediaRouteProvider.Callback?);
+    method public final void setDescriptor(androidx.mediarouter.media.MediaRouteProviderDescriptor?);
+    method public final void setDiscoveryRequest(androidx.mediarouter.media.MediaRouteDiscoveryRequest?);
+  }
+
+  public abstract static class MediaRouteProvider.Callback {
+    ctor public MediaRouteProvider.Callback();
+    method public void onDescriptorChanged(androidx.mediarouter.media.MediaRouteProvider, androidx.mediarouter.media.MediaRouteProviderDescriptor?);
+  }
+
+  public abstract static class MediaRouteProvider.DynamicGroupRouteController extends androidx.mediarouter.media.MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.DynamicGroupRouteController();
+    method public String? getGroupableSelectionTitle();
+    method public String? getTransferableSectionTitle();
+    method public final void notifyDynamicRoutesChanged(androidx.mediarouter.media.MediaRouteDescriptor, java.util.Collection<androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor!>);
+    method @Deprecated public final void notifyDynamicRoutesChanged(java.util.Collection<androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor!>);
+    method public abstract void onAddMemberRoute(String);
+    method public abstract void onRemoveMemberRoute(String);
+    method public abstract void onUpdateMemberRoutes(java.util.List<java.lang.String!>?);
+  }
+
+  public static final class MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor {
+    method public androidx.mediarouter.media.MediaRouteDescriptor getRouteDescriptor();
+    method public int getSelectionState();
+    method public boolean isGroupable();
+    method public boolean isTransferable();
+    method public boolean isUnselectable();
+    field public static final int SELECTED = 3; // 0x3
+    field public static final int SELECTING = 2; // 0x2
+    field public static final int UNSELECTED = 1; // 0x1
+    field public static final int UNSELECTING = 0; // 0x0
+  }
+
+  public static final class MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder {
+    ctor public MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder(androidx.mediarouter.media.MediaRouteDescriptor);
+    ctor public MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder(androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor build();
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setIsGroupable(boolean);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setIsTransferable(boolean);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setIsUnselectable(boolean);
+    method public androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor.Builder setSelectionState(int);
+  }
+
+  public static final class MediaRouteProvider.ProviderMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public String getPackageName();
+  }
+
+  public abstract static class MediaRouteProvider.RouteController {
+    ctor public MediaRouteProvider.RouteController();
+    method public boolean onControlRequest(android.content.Intent, androidx.mediarouter.media.MediaRouter.ControlRequestCallback?);
+    method public void onRelease();
+    method public void onSelect();
+    method public void onSetVolume(int);
+    method @Deprecated public void onUnselect();
+    method public void onUnselect(int);
+    method public void onUpdateVolume(int);
+  }
+
+  public final class MediaRouteProviderDescriptor {
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaRouteProviderDescriptor? fromBundle(android.os.Bundle?);
+    method public java.util.List<androidx.mediarouter.media.MediaRouteDescriptor!> getRoutes();
+    method public boolean isValid();
+    method public boolean supportsDynamicGroupRoute();
+  }
+
+  public static final class MediaRouteProviderDescriptor.Builder {
+    ctor public MediaRouteProviderDescriptor.Builder();
+    ctor public MediaRouteProviderDescriptor.Builder(androidx.mediarouter.media.MediaRouteProviderDescriptor);
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor.Builder addRoute(androidx.mediarouter.media.MediaRouteDescriptor);
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor.Builder addRoutes(java.util.Collection<androidx.mediarouter.media.MediaRouteDescriptor!>);
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor build();
+    method public androidx.mediarouter.media.MediaRouteProviderDescriptor.Builder setSupportsDynamicGroupRoute(boolean);
+  }
+
+  public abstract class MediaRouteProviderService extends android.app.Service {
+    ctor public MediaRouteProviderService();
+    method public androidx.mediarouter.media.MediaRouteProvider? getMediaRouteProvider();
+    method public android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.mediarouter.media.MediaRouteProvider? onCreateMediaRouteProvider();
+    field public static final String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
+  }
+
+  public final class MediaRouteSelector {
+    method public android.os.Bundle asBundle();
+    method public boolean contains(androidx.mediarouter.media.MediaRouteSelector);
+    method public static androidx.mediarouter.media.MediaRouteSelector? fromBundle(android.os.Bundle?);
+    method public java.util.List<java.lang.String!> getControlCategories();
+    method public boolean hasControlCategory(String?);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public boolean matchesControlFilters(java.util.List<android.content.IntentFilter!>?);
+    field public static final androidx.mediarouter.media.MediaRouteSelector! EMPTY;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    ctor public MediaRouteSelector.Builder(androidx.mediarouter.media.MediaRouteSelector);
+    method public androidx.mediarouter.media.MediaRouteSelector.Builder addControlCategories(java.util.Collection<java.lang.String!>);
+    method public androidx.mediarouter.media.MediaRouteSelector.Builder addControlCategory(String);
+    method public androidx.mediarouter.media.MediaRouteSelector.Builder addSelector(androidx.mediarouter.media.MediaRouteSelector);
+    method public androidx.mediarouter.media.MediaRouteSelector build();
+  }
+
+  public final class MediaRouter {
+    method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback);
+    method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback, int);
+    method @MainThread public void addProvider(androidx.mediarouter.media.MediaRouteProvider);
+    method @Deprecated @MainThread public void addRemoteControlClient(Object);
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo? getBluetoothRoute();
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo getDefaultRoute();
+    method @MainThread public static androidx.mediarouter.media.MediaRouter getInstance(android.content.Context);
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getMediaSessionToken();
+    method @MainThread public java.util.List<androidx.mediarouter.media.MediaRouter.ProviderInfo!> getProviders();
+    method @MainThread public androidx.mediarouter.media.MediaRouterParams? getRouterParams();
+    method @MainThread public java.util.List<androidx.mediarouter.media.MediaRouter.RouteInfo!> getRoutes();
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo getSelectedRoute();
+    method @MainThread public boolean isRouteAvailable(androidx.mediarouter.media.MediaRouteSelector, int);
+    method @MainThread public void removeCallback(androidx.mediarouter.media.MediaRouter.Callback);
+    method @MainThread public void removeProvider(androidx.mediarouter.media.MediaRouteProvider);
+    method @MainThread public void removeRemoteControlClient(Object);
+    method @MainThread public void selectRoute(androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method @MainThread public void setMediaSession(Object?);
+    method @MainThread public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat?);
+    method @MainThread public void setOnPrepareTransferListener(androidx.mediarouter.media.MediaRouter.OnPrepareTransferListener?);
+    method @MainThread public void setRouteListingPreference(androidx.mediarouter.media.RouteListingPreference?);
+    method @MainThread public void setRouterParams(androidx.mediarouter.media.MediaRouterParams?);
+    method @MainThread public void unselect(int);
+    method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo updateSelectedRoute(androidx.mediarouter.media.MediaRouteSelector);
+    field public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1; // 0x1
+    field public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 2; // 0x2
+    field public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 8; // 0x8
+    field public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 4; // 0x4
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
+    field public static final int UNSELECT_REASON_DISCONNECTED = 1; // 0x1
+    field public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; // 0x3
+    field public static final int UNSELECT_REASON_STOPPED = 2; // 0x2
+    field public static final int UNSELECT_REASON_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
+    method public void onProviderAdded(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.ProviderInfo);
+    method public void onProviderChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.ProviderInfo);
+    method public void onProviderRemoved(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.ProviderInfo);
+    method public void onRouteAdded(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRoutePresentationDisplayChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteRemoved(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method @Deprecated public void onRouteSelected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteSelected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo, int);
+    method public void onRouteSelected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo, int, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method @Deprecated public void onRouteUnselected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo, int);
+    method public void onRouteVolumeChanged(androidx.mediarouter.media.MediaRouter, androidx.mediarouter.media.MediaRouter.RouteInfo);
+  }
+
+  public abstract static class MediaRouter.ControlRequestCallback {
+    ctor public MediaRouter.ControlRequestCallback();
+    method public void onError(String?, android.os.Bundle?);
+    method public void onResult(android.os.Bundle?);
+  }
+
+  public static interface MediaRouter.OnPrepareTransferListener {
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>? onPrepareTransfer(androidx.mediarouter.media.MediaRouter.RouteInfo, androidx.mediarouter.media.MediaRouter.RouteInfo);
+  }
+
+  public static final class MediaRouter.ProviderInfo {
+    method public android.content.ComponentName getComponentName();
+    method public String getPackageName();
+    method @MainThread public androidx.mediarouter.media.MediaRouteProvider getProviderInstance();
+    method @MainThread public java.util.List<androidx.mediarouter.media.MediaRouter.RouteInfo!> getRoutes();
+  }
+
+  public static class MediaRouter.RouteInfo {
+    method public boolean canDisconnect();
+    method public int getConnectionState();
+    method public java.util.List<android.content.IntentFilter!> getControlFilters();
+    method public String? getDescription();
+    method public int getDeviceType();
+    method public android.os.Bundle? getExtras();
+    method public android.net.Uri? getIconUri();
+    method public String getId();
+    method public String getName();
+    method public int getPlaybackStream();
+    method public int getPlaybackType();
+    method @MainThread public android.view.Display? getPresentationDisplay();
+    method public androidx.mediarouter.media.MediaRouter.ProviderInfo getProvider();
+    method public android.content.IntentSender? getSettingsIntent();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method @MainThread public boolean isBluetooth();
+    method @Deprecated public boolean isConnecting();
+    method @MainThread public boolean isDefault();
+    method public boolean isDeviceSpeaker();
+    method public boolean isEnabled();
+    method @MainThread public boolean isSelected();
+    method @MainThread public boolean matchesSelector(androidx.mediarouter.media.MediaRouteSelector);
+    method @MainThread public void requestSetVolume(int);
+    method @MainThread public void requestUpdateVolume(int);
+    method @MainThread public void select();
+    method @MainThread public void sendControlRequest(android.content.Intent, androidx.mediarouter.media.MediaRouter.ControlRequestCallback?);
+    method @MainThread public boolean supportsControlAction(String, String);
+    method @MainThread public boolean supportsControlCategory(String);
+    method @MainThread public boolean supportsControlRequest(android.content.Intent);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DEVICE_TYPE_AUDIO_VIDEO_RECEIVER = 4; // 0x4
+    field public static final int DEVICE_TYPE_CAR = 9; // 0x9
+    field public static final int DEVICE_TYPE_COMPUTER = 7; // 0x7
+    field public static final int DEVICE_TYPE_GAME_CONSOLE = 8; // 0x8
+    field public static final int DEVICE_TYPE_GROUP = 1000; // 0x3e8
+    field public static final int DEVICE_TYPE_SMARTWATCH = 10; // 0xa
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TABLET = 5; // 0x5
+    field public static final int DEVICE_TYPE_TABLET_DOCKED = 6; // 0x6
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
+    field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public class MediaRouterParams {
+    method public int getDialogType();
+    method public boolean isMediaTransferReceiverEnabled();
+    method public boolean isOutputSwitcherEnabled();
+    method public boolean isTransferToLocalEnabled();
+    field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
+    field public static final int DIALOG_TYPE_DYNAMIC_GROUP = 2; // 0x2
+    field public static final String ENABLE_GROUP_VOLUME_UX = "androidx.mediarouter.media.MediaRouterParams.ENABLE_GROUP_VOLUME_UX";
+  }
+
+  public static final class MediaRouterParams.Builder {
+    ctor public MediaRouterParams.Builder();
+    ctor public MediaRouterParams.Builder(androidx.mediarouter.media.MediaRouterParams);
+    method public androidx.mediarouter.media.MediaRouterParams build();
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setDialogType(int);
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverEnabled(boolean);
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setOutputSwitcherEnabled(boolean);
+    method public androidx.mediarouter.media.MediaRouterParams.Builder setTransferToLocalEnabled(boolean);
+  }
+
+  public final class MediaSessionStatus {
+    method public android.os.Bundle asBundle();
+    method public static androidx.mediarouter.media.MediaSessionStatus? fromBundle(android.os.Bundle?);
+    method public android.os.Bundle? getExtras();
+    method public int getSessionState();
+    method public long getTimestamp();
+    method public boolean isQueuePaused();
+    field public static final int SESSION_STATE_ACTIVE = 0; // 0x0
+    field public static final int SESSION_STATE_ENDED = 1; // 0x1
+    field public static final int SESSION_STATE_INVALIDATED = 2; // 0x2
+  }
+
+  public static final class MediaSessionStatus.Builder {
+    ctor public MediaSessionStatus.Builder(androidx.mediarouter.media.MediaSessionStatus);
+    ctor public MediaSessionStatus.Builder(int);
+    method public androidx.mediarouter.media.MediaSessionStatus build();
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setExtras(android.os.Bundle?);
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setQueuePaused(boolean);
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setSessionState(int);
+    method public androidx.mediarouter.media.MediaSessionStatus.Builder setTimestamp(long);
+  }
+
+  public final class MediaTransferReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaTransferReceiver();
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
+  public class RemotePlaybackClient {
+    ctor public RemotePlaybackClient(android.content.Context, androidx.mediarouter.media.MediaRouter.RouteInfo);
+    method public void endSession(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void enqueue(android.net.Uri, String?, android.os.Bundle?, long, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public String? getSessionId();
+    method public void getSessionStatus(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void getStatus(String, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public boolean hasSession();
+    method public boolean isMessagingSupported();
+    method public boolean isQueuingSupported();
+    method public boolean isRemotePlaybackSupported();
+    method public boolean isSessionManagementSupported();
+    method public void pause(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void play(android.net.Uri, String?, android.os.Bundle?, long, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public void release();
+    method public void remove(String, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public void resume(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void seek(String, long, android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.ItemActionCallback?);
+    method public void sendMessage(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void setOnMessageReceivedListener(androidx.mediarouter.media.RemotePlaybackClient.OnMessageReceivedListener?);
+    method public void setSessionId(String?);
+    method public void setStatusCallback(androidx.mediarouter.media.RemotePlaybackClient.StatusCallback?);
+    method public void startSession(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+    method public void stop(android.os.Bundle?, androidx.mediarouter.media.RemotePlaybackClient.SessionActionCallback?);
+  }
+
+  public abstract static class RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ActionCallback();
+    method public void onError(String?, int, android.os.Bundle?);
+  }
+
+  public abstract static class RemotePlaybackClient.ItemActionCallback extends androidx.mediarouter.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.ItemActionCallback();
+    method public void onResult(android.os.Bundle, String, androidx.mediarouter.media.MediaSessionStatus?, String, androidx.mediarouter.media.MediaItemStatus);
+  }
+
+  public static interface RemotePlaybackClient.OnMessageReceivedListener {
+    method public void onMessageReceived(String, android.os.Bundle?);
+  }
+
+  public abstract static class RemotePlaybackClient.SessionActionCallback extends androidx.mediarouter.media.RemotePlaybackClient.ActionCallback {
+    ctor public RemotePlaybackClient.SessionActionCallback();
+    method public void onResult(android.os.Bundle, String, androidx.mediarouter.media.MediaSessionStatus?);
+  }
+
+  public abstract static class RemotePlaybackClient.StatusCallback {
+    ctor public RemotePlaybackClient.StatusCallback();
+    method public void onItemStatusChanged(android.os.Bundle?, String, androidx.mediarouter.media.MediaSessionStatus?, String, androidx.mediarouter.media.MediaItemStatus);
+    method public void onSessionChanged(String?);
+    method public void onSessionStatusChanged(android.os.Bundle?, String, androidx.mediarouter.media.MediaSessionStatus?);
+  }
+
+  public final class RouteListingPreference {
+    method public java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!> getItems();
+    method public android.content.ComponentName? getLinkedItemComponentName();
+    method public boolean getUseSystemOrdering();
+    field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
+    field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
+  }
+
+  public static final class RouteListingPreference.Builder {
+    ctor public RouteListingPreference.Builder();
+    method public androidx.mediarouter.media.RouteListingPreference build();
+    method public androidx.mediarouter.media.RouteListingPreference.Builder setItems(java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!>);
+    method public androidx.mediarouter.media.RouteListingPreference.Builder setLinkedItemComponentName(android.content.ComponentName?);
+    method public androidx.mediarouter.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
+  }
+
+  public static final class RouteListingPreference.Item {
+    method public CharSequence? getCustomSubtextMessage();
+    method public int getFlags();
+    method public String getRouteId();
+    method public int getSelectionBehavior();
+    method public int getSubText();
+    field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
+    field public static final int FLAG_ONGOING_SESSION_MANAGED = 2; // 0x2
+    field public static final int FLAG_SUGGESTED = 4; // 0x4
+    field public static final int SELECTION_BEHAVIOR_GO_TO_APP = 2; // 0x2
+    field public static final int SELECTION_BEHAVIOR_NONE = 0; // 0x0
+    field public static final int SELECTION_BEHAVIOR_TRANSFER = 1; // 0x1
+    field public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 4; // 0x4
+    field public static final int SUBTEXT_CUSTOM = 10000; // 0x2710
+    field public static final int SUBTEXT_DEVICE_LOW_POWER = 5; // 0x5
+    field public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 3; // 0x3
+    field public static final int SUBTEXT_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int SUBTEXT_NONE = 0; // 0x0
+    field public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 2; // 0x2
+    field public static final int SUBTEXT_TRACK_UNSUPPORTED = 7; // 0x7
+    field public static final int SUBTEXT_UNAUTHORIZED = 6; // 0x6
+  }
+
+  public static final class RouteListingPreference.Item.Builder {
+    ctor public RouteListingPreference.Item.Builder(String);
+    method public androidx.mediarouter.media.RouteListingPreference.Item build();
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setCustomSubtextMessage(CharSequence?);
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setFlags(int);
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSelectionBehavior(int);
+    method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSubText(int);
+  }
+
+}
+
diff --git a/navigation/navigation-common/api/2.7.0-beta02.txt b/navigation/navigation-common/api/2.7.0-beta02.txt
index 51a77a6..95166e9 100644
--- a/navigation/navigation-common/api/2.7.0-beta02.txt
+++ b/navigation/navigation-common/api/2.7.0-beta02.txt
@@ -128,6 +128,10 @@
     property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
     property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
     property public androidx.lifecycle.ViewModelStore viewModelStore;
+    field public static final androidx.navigation.NavBackStackEntry.Companion Companion;
+  }
+
+  public static final class NavBackStackEntry.Companion {
   }
 
   public final class NavDeepLink {
diff --git a/navigation/navigation-common/api/current.txt b/navigation/navigation-common/api/current.txt
index 51a77a6..95166e9 100644
--- a/navigation/navigation-common/api/current.txt
+++ b/navigation/navigation-common/api/current.txt
@@ -128,6 +128,10 @@
     property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
     property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
     property public androidx.lifecycle.ViewModelStore viewModelStore;
+    field public static final androidx.navigation.NavBackStackEntry.Companion Companion;
+  }
+
+  public static final class NavBackStackEntry.Companion {
   }
 
   public final class NavDeepLink {
diff --git a/navigation/navigation-common/api/restricted_2.7.0-beta02.txt b/navigation/navigation-common/api/restricted_2.7.0-beta02.txt
index 51a77a6..95166e9 100644
--- a/navigation/navigation-common/api/restricted_2.7.0-beta02.txt
+++ b/navigation/navigation-common/api/restricted_2.7.0-beta02.txt
@@ -128,6 +128,10 @@
     property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
     property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
     property public androidx.lifecycle.ViewModelStore viewModelStore;
+    field public static final androidx.navigation.NavBackStackEntry.Companion Companion;
+  }
+
+  public static final class NavBackStackEntry.Companion {
   }
 
   public final class NavDeepLink {
diff --git a/navigation/navigation-common/api/restricted_current.txt b/navigation/navigation-common/api/restricted_current.txt
index 51a77a6..95166e9 100644
--- a/navigation/navigation-common/api/restricted_current.txt
+++ b/navigation/navigation-common/api/restricted_current.txt
@@ -128,6 +128,10 @@
     property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
     property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
     property public androidx.lifecycle.ViewModelStore viewModelStore;
+    field public static final androidx.navigation.NavBackStackEntry.Companion Companion;
+  }
+
+  public static final class NavBackStackEntry.Companion {
   }
 
   public final class NavDeepLink {
diff --git a/navigation/navigation-common/lint-baseline.xml b/navigation/navigation-common/lint-baseline.xml
deleted file mode 100644
index af90ea4..0000000
--- a/navigation/navigation-common/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
-
-    <issue
-        id="BanHideAnnotation"
-        message="@hide is not allowed in Javadoc"
-        errorLine1="    public companion object {"
-        errorLine2="                     ~~~~~~">
-        <location
-            file="src/main/java/androidx/navigation/NavBackStackEntry.kt"/>
-    </issue>
-
-</issues>
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt
index 0cf2b96..0d605ee 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt
@@ -84,10 +84,6 @@
         maxLifecycle = entry.maxLifecycle
     }
 
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public companion object {
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public fun create(
diff --git a/paging/paging-common/src/commonMain/kotlin/androidx/paging/HintHandler.kt b/paging/paging-common/src/commonMain/kotlin/androidx/paging/HintHandler.kt
index 536688d..76f77a0 100644
--- a/paging/paging-common/src/commonMain/kotlin/androidx/paging/HintHandler.kt
+++ b/paging/paging-common/src/commonMain/kotlin/androidx/paging/HintHandler.kt
@@ -22,7 +22,7 @@
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
 import co.touchlab.stately.concurrency.Lock
-import kotlin.concurrent.withLock
+import co.touchlab.stately.concurrency.withLock
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
diff --git a/paging/paging-common/src/commonMain/kotlin/androidx/paging/InvalidateCallbackTracker.kt b/paging/paging-common/src/commonMain/kotlin/androidx/paging/InvalidateCallbackTracker.kt
index fdeb52f..e23d525 100644
--- a/paging/paging-common/src/commonMain/kotlin/androidx/paging/InvalidateCallbackTracker.kt
+++ b/paging/paging-common/src/commonMain/kotlin/androidx/paging/InvalidateCallbackTracker.kt
@@ -18,7 +18,7 @@
 
 import androidx.annotation.VisibleForTesting
 import co.touchlab.stately.concurrency.Lock
-import kotlin.concurrent.withLock
+import co.touchlab.stately.concurrency.withLock
 
 /**
  * Helper class for thread-safe invalidation callback tracking + triggering on registration.
@@ -74,7 +74,7 @@
     internal fun invalidate(): Boolean {
         if (invalid) return false
 
-        var callbacksToInvoke: List<T>?
+        var callbacksToInvoke: List<T>? = null
         lock.withLock {
             if (invalid) return false
 
diff --git a/paging/paging-common/src/commonMain/kotlin/androidx/paging/PagingDataDiffer.kt b/paging/paging-common/src/commonMain/kotlin/androidx/paging/PagingDataDiffer.kt
index 448c904..41e3d5f 100644
--- a/paging/paging-common/src/commonMain/kotlin/androidx/paging/PagingDataDiffer.kt
+++ b/paging/paging-common/src/commonMain/kotlin/androidx/paging/PagingDataDiffer.kt
@@ -17,7 +17,6 @@
 package androidx.paging
 
 import androidx.annotation.IntRange
-import androidx.annotation.MainThread
 import androidx.annotation.RestrictTo
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
diff --git a/paging/paging-common/src/commonMain/kotlin/androidx/paging/RemoteMediatorAccessor.kt b/paging/paging-common/src/commonMain/kotlin/androidx/paging/RemoteMediatorAccessor.kt
index b6f5b1a..f2725a1 100644
--- a/paging/paging-common/src/commonMain/kotlin/androidx/paging/RemoteMediatorAccessor.kt
+++ b/paging/paging-common/src/commonMain/kotlin/androidx/paging/RemoteMediatorAccessor.kt
@@ -21,7 +21,7 @@
 import androidx.paging.AccessorState.BlockState.UNBLOCKED
 import androidx.paging.RemoteMediator.MediatorResult
 import co.touchlab.stately.concurrency.Lock
-import kotlin.concurrent.withLock
+import co.touchlab.stately.concurrency.withLock
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt b/paging/paging-common/src/commonMain/kotlin/androidx/paging/annotation.kt
similarity index 66%
rename from bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt
rename to paging/paging-common/src/commonMain/kotlin/androidx/paging/annotation.kt
index f2fb27d..5d73136 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt
+++ b/paging/paging-common/src/commonMain/kotlin/androidx/paging/annotation.kt
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-package androidx.bluetooth.integration.testapp.experimental
+package androidx.paging
 
-enum class AdvertiseResult {
-    ADVERTISE_STARTED,
-    ADVERTISE_FAILED_ALREADY_STARTED,
-    ADVERTISE_FAILED_DATA_TOO_LARGE,
-    ADVERTISE_FAILED_FEATURE_UNSUPPORTED,
-    ADVERTISE_FAILED_INTERNAL_ERROR,
-    ADVERTISE_FAILED_TOO_MANY_ADVERTISERS
-}
+public expect annotation class MainThread()
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt b/paging/paging-common/src/jvmMain/kotlin/androidx/paging/MainThread.kt
similarity index 66%
copy from bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt
copy to paging/paging-common/src/jvmMain/kotlin/androidx/paging/MainThread.kt
index f2fb27d..7705b0d 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt
+++ b/paging/paging-common/src/jvmMain/kotlin/androidx/paging/MainThread.kt
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-package androidx.bluetooth.integration.testapp.experimental
+package androidx.paging
 
-enum class AdvertiseResult {
-    ADVERTISE_STARTED,
-    ADVERTISE_FAILED_ALREADY_STARTED,
-    ADVERTISE_FAILED_DATA_TOO_LARGE,
-    ADVERTISE_FAILED_FEATURE_UNSUPPORTED,
-    ADVERTISE_FAILED_INTERNAL_ERROR,
-    ADVERTISE_FAILED_TOO_MANY_ADVERTISERS
-}
+public actual typealias MainThread = androidx.annotation.MainThread
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt b/paging/paging-common/src/nativeMain/kotlin/androidx/paging/annotation.kt
similarity index 66%
copy from bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt
copy to paging/paging-common/src/nativeMain/kotlin/androidx/paging/annotation.kt
index f2fb27d..ce1d1a6 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/experimental/AdvertiseResult.kt
+++ b/paging/paging-common/src/nativeMain/kotlin/androidx/paging/annotation.kt
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-package androidx.bluetooth.integration.testapp.experimental
+package androidx.paging
 
-enum class AdvertiseResult {
-    ADVERTISE_STARTED,
-    ADVERTISE_FAILED_ALREADY_STARTED,
-    ADVERTISE_FAILED_DATA_TOO_LARGE,
-    ADVERTISE_FAILED_FEATURE_UNSUPPORTED,
-    ADVERTISE_FAILED_INTERNAL_ERROR,
-    ADVERTISE_FAILED_TOO_MANY_ADVERTISERS
-}
+public actual annotation class MainThread()
diff --git a/preference/preference/lint-baseline.xml b/preference/preference/lint-baseline.xml
index 4b4ccf8..217ddcd 100644
--- a/preference/preference/lint-baseline.xml
+++ b/preference/preference/lint-baseline.xml
@@ -1,14 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.1.0-alpha07">
-
-    <issue
-        id="BanHideAnnotation"
-        message="@hide is not allowed in Javadoc"
-        errorLine1="    public static PreferenceViewHolder createInstanceForTests(@NonNull View itemView) {"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/preference/PreferenceViewHolder.java"/>
-    </issue>
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="BanThreadSleep"
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java b/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
index 95635c5..e3129a8 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
@@ -25,6 +25,7 @@
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.RecyclerView;
@@ -60,7 +61,7 @@
         }
     }
 
-    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
     @VisibleForTesting
     @NonNull
     public static PreferenceViewHolder createInstanceForTests(@NonNull View itemView) {
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
index 82a15d5..2808373 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
@@ -167,6 +167,20 @@
     }
 
     @Test
+    fun testExecSQLWithBindArgs() {
+        mDatabase.openHelper.writableDatabase.execSQL(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                "VALUES (?,?)",
+            arrayOf("3", "Description")
+        )
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                "VALUES (?,?)",
+            listOf("3", "Description")
+        )
+    }
+
+    @Test
     fun testNullBindArgument() {
         mDatabase.openHelper.writableDatabase.query(
             SimpleSQLiteQuery(
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt
index d6a4107..0f1ec17 100644
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt
@@ -136,11 +136,10 @@
     // and it can't be renamed.
     @Suppress("AcronymName")
     override fun execSQL(sql: String, bindArgs: Array<out Any?>) {
-        val inputArguments = mutableListOf<Any>()
-        inputArguments.addAll(listOf(bindArgs))
+        val inputArguments = buildList { addAll(bindArgs) }
         queryCallbackExecutor.execute {
             queryCallback.onQuery(sql, inputArguments)
         }
-        delegate.execSQL(sql, arrayOf(inputArguments))
+        delegate.execSQL(sql, inputArguments.toTypedArray())
     }
 }
diff --git a/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt b/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt
index 820d26a..d4c24aa 100644
--- a/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt
+++ b/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteDatabase.kt
@@ -1,4 +1,3 @@
-@file:JvmName("SupportSQLiteDatabaseKt")
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
diff --git a/test/uiautomator/uiautomator/lint-baseline.xml b/test/uiautomator/uiautomator/lint-baseline.xml
index a62bf81..6476f95 100644
--- a/test/uiautomator/uiautomator/lint-baseline.xml
+++ b/test/uiautomator/uiautomator/lint-baseline.xml
@@ -1,14 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
-
-    <issue
-        id="BanHideAnnotation"
-        message="@hide is not allowed in Javadoc"
-        errorLine1="    public String executeShellCommand(@NonNull String cmd) throws IOException {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="BanUncheckedReflection"
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index dc72558..8dbc93d 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -48,6 +48,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.test.uiautomator.util.Traces;
 import androidx.test.uiautomator.util.Traces.Section;
 
@@ -1115,8 +1116,9 @@
      * @param cmd the command to run
      * @return the standard output of the command
      * @throws IOException
-     * @hide legacy hidden method, kept for compatibility with existing tests.
+     * Legacy hidden method, kept for compatibility with existing tests.
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
     @RequiresApi(21)
     @NonNull
     public String executeShellCommand(@NonNull String cmd) throws IOException {
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/SwipeToDismissBoxTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/SwipeToDismissBoxTest.kt
index dfcca4b..956835f 100644
--- a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/SwipeToDismissBoxTest.kt
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/SwipeToDismissBoxTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusable
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -36,6 +37,8 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
@@ -53,6 +56,8 @@
 import androidx.compose.ui.test.swipeRight
 import java.lang.Math.sin
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 
@@ -419,6 +424,58 @@
         }
     }
 
+    @OptIn(ExperimentalWearFoundationApi::class)
+    @Test
+    fun partial_swipe_maintains_focus() {
+        var focusedBackground by mutableStateOf(false)
+        var focusedContent by mutableStateOf(false)
+
+        rule.setContent {
+            val state = rememberSwipeToDismissBoxState()
+            SwipeToDismissBox(
+                state = state,
+                modifier = Modifier.testTag(TEST_TAG),
+            ) { isBackground ->
+                if (isBackground) {
+                    val focusRequester = rememberActiveFocusRequester()
+                    BasicText(
+                        BACKGROUND_MESSAGE,
+                        Modifier
+                            .onFocusChanged { focusedBackground = it.isFocused }
+                            .focusRequester(focusRequester)
+                            .focusable())
+                } else {
+                    val focusRequester = rememberActiveFocusRequester()
+                    BasicText(
+                        CONTENT_MESSAGE,
+                        Modifier
+                            .onFocusChanged { focusedContent = it.isFocused }
+                            .focusRequester(focusRequester)
+                            .focusable())
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertTrue(focusedContent)
+            assertFalse(focusedBackground)
+        }
+
+        // Click down and drag across 1/4 of the screen to start a swipe,
+        // but don't release the finger, so that the screen can be inspected
+        // (note that swipeRight would release the finger and does not pause time midway).
+        rule.onNodeWithTag(TEST_TAG).performTouchInput {
+            down(Offset(x = 0f, y = height / 2f))
+            moveTo(Offset(x = width / 4f, y = height / 2f))
+        }
+
+        // We started showing the background, but focus hasn't changed.
+        rule.runOnIdle {
+            assertTrue(focusedContent)
+            assertFalse(focusedBackground)
+        }
+    }
+
     private fun testBothDirectionScroll(
         initialTouch: Long,
         duration: Long,
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToDismissBox.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToDismissBox.kt
index b776940..f4e9c56 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToDismissBox.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToDismissBox.kt
@@ -203,13 +203,15 @@
                 if (!isBackground ||
                     (userSwipeEnabled && (state.swipeableState.offset?.roundToInt() ?: 0) > 0)
                 ) {
-                    Box(contentModifier) {
-                        // We use the repeat loop above and call content at this location
-                        // for both background and foreground so that any persistence
-                        // within the content composable has the same call stack which is used
-                        // as part of the hash identity for saveable state.
-                        content(isBackground)
-                        Box(modifier = scrimModifier)
+                    HierarchicalFocusCoordinator(requiresFocus = { !isBackground }) {
+                        Box(contentModifier) {
+                            // We use the repeat loop above and call content at this location
+                            // for both background and foreground so that any persistence
+                            // within the content composable has the same call stack which is used
+                            // as part of the hash identity for saveable state.
+                            content(isBackground)
+                            Box(modifier = scrimModifier)
+                        }
                     }
                 }
             }
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index 0323d67..d546a2e 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -16,13 +16,17 @@
 
 package androidx.wear.compose.material3.demos
 
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AccountCircle
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.material3.Button
 import androidx.wear.compose.material3.ButtonDefaults
@@ -227,4 +231,159 @@
             )
         }
     }
+}
+
+@Composable
+fun MultilineButtonDemo() {
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize(),
+        horizontalAlignment = Alignment.CenterHorizontally,
+    ) {
+        item {
+            ListHeader {
+                Text("3 line label")
+            }
+        }
+        item {
+            MultilineButton(enabled = true)
+        }
+        item {
+            MultilineButton(enabled = false)
+        }
+        item {
+            MultilineButton(enabled = true, icon = { StandardIcon() })
+        }
+        item {
+            MultilineButton(enabled = false, icon = { StandardIcon() })
+        }
+        item {
+            ListHeader {
+                Text("5 line button")
+            }
+        }
+        item {
+            Multiline3SlotButton(enabled = true)
+        }
+        item {
+            Multiline3SlotButton(enabled = false)
+        }
+        item {
+            Multiline3SlotButton(enabled = true, icon = { StandardIcon() })
+        }
+        item {
+            Multiline3SlotButton(enabled = false, icon = { StandardIcon() })
+        }
+    }
+}
+
+@Composable
+fun AvatarButtonDemo() {
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize(),
+        horizontalAlignment = Alignment.CenterHorizontally,
+    ) {
+        item {
+            ListHeader {
+                Text("Label + Avatar")
+            }
+        }
+        item {
+            AvatarButton(enabled = true)
+        }
+        item {
+            AvatarButton(enabled = false)
+        }
+        item {
+            ListHeader {
+                Text("Primary/Secondary + Avatar")
+            }
+        }
+        item {
+            Avatar3SlotButton(enabled = true)
+        }
+        item {
+            Avatar3SlotButton(enabled = false)
+        }
+    }
+}
+
+@Composable
+private fun AvatarButton(enabled: Boolean) =
+    MultilineButton(
+        enabled = enabled, icon = { AvatarIcon() }, label = { Text("Primary text") }
+    )
+
+@Composable
+private fun Avatar3SlotButton(enabled: Boolean) =
+    Multiline3SlotButton(
+        enabled = enabled,
+        icon = { AvatarIcon() },
+        label = { Text("Primary text") },
+        secondaryLabel = { Text("Secondary label") }
+    )
+
+@Composable
+private fun MultilineButton(
+    enabled: Boolean,
+    icon: (@Composable BoxScope.() -> Unit)? = null,
+    label: @Composable RowScope.() -> Unit = {
+        Text(
+            text = "Multiline label that include a lot of text and stretches to third line",
+            maxLines = 3,
+            overflow = TextOverflow.Ellipsis,
+        )
+    },
+) {
+    Button(
+        onClick = { /* Do something */ },
+        icon = icon,
+        label = label,
+        enabled = enabled
+    )
+}
+
+@Composable
+private fun Multiline3SlotButton(
+    enabled: Boolean,
+    icon: (@Composable BoxScope.() -> Unit)? = null,
+    label: @Composable RowScope.() -> Unit = {
+        Text(
+            text = "Multiline label that include a lot of text and stretches to third line",
+            maxLines = 3,
+            overflow = TextOverflow.Ellipsis,
+        )
+    },
+    secondaryLabel: @Composable RowScope.() -> Unit = {
+        Text(
+            text = "Secondary label over two lines",
+            maxLines = 2,
+            overflow = TextOverflow.Ellipsis,
+        )
+    },
+) {
+    Button(
+        onClick = { /* Do something */ },
+        icon = icon,
+        label = label,
+        secondaryLabel = secondaryLabel,
+        enabled = enabled
+    )
+}
+
+@Composable
+private fun StandardIcon() {
+    Icon(
+        Icons.Filled.Favorite,
+        contentDescription = "Favorite icon",
+        modifier = Modifier.size(ButtonDefaults.IconSize)
+    )
+}
+
+@Composable
+private fun AvatarIcon() {
+    Icon(
+        Icons.Filled.AccountCircle,
+        contentDescription = "Account",
+        modifier = Modifier.size(ButtonDefaults.LargeIconSize)
+    )
 }
\ No newline at end of file
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt
index 4ad6082..b17cb00 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt
@@ -42,7 +42,6 @@
 import androidx.wear.compose.material3.InlineSlider
 import androidx.wear.compose.material3.InlineSliderColors
 import androidx.wear.compose.material3.InlineSliderDefaults
-import androidx.wear.compose.material3.ListHeader
 import androidx.wear.compose.material3.Text
 import androidx.wear.compose.material3.samples.InlineSliderSample
 import androidx.wear.compose.material3.samples.InlineSliderSegmentedSample
@@ -99,9 +98,7 @@
         autoCentering = AutoCenteringParams(itemIndex = 0)
     ) {
         item {
-            ListHeader {
-                Text("Enabled Slider, value = $enabledValue")
-            }
+            Text("Enabled Slider, value = $enabledValue")
         }
         item {
             DefaultInlineSlider(
@@ -114,9 +111,7 @@
             )
         }
         item {
-            ListHeader {
-                Text("Disabled Slider, value = $disabledValue")
-            }
+            Text("Disabled Slider, value = $disabledValue")
         }
         item {
             DefaultInlineSlider(
@@ -146,9 +141,7 @@
         autoCentering = AutoCenteringParams(itemIndex = 0)
     ) {
         item {
-            ListHeader {
-                Text("No segments, value = $valueWithoutSegments")
-            }
+            Text("No segments, value = $valueWithoutSegments")
         }
         item {
             DefaultInlineSlider(
@@ -158,9 +151,7 @@
                 onValueChange = { valueWithoutSegments = it })
         }
         item {
-            ListHeader {
-                Text("With segments, value = $valueWithSegments")
-            }
+            Text("With segments, value = $valueWithSegments")
         }
         item {
             DefaultInlineSlider(
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index acb77e0..d4df8fc 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -52,7 +52,13 @@
                 },
                 ComposableDemo("Child Button") {
                     ChildButtonDemo()
-                }
+                },
+                ComposableDemo("Multiline Button") {
+                    MultilineButtonDemo()
+                },
+                ComposableDemo("Avatar Button") {
+                    AvatarButtonDemo()
+                },
             )
         ),
         DemoCategory(
diff --git a/wear/compose/compose-navigation/api/current.txt b/wear/compose/compose-navigation/api/current.txt
index 30cd692..e27dd9d 100644
--- a/wear/compose/compose-navigation/api/current.txt
+++ b/wear/compose/compose-navigation/api/current.txt
@@ -11,8 +11,10 @@
   }
 
   public final class SwipeDismissableNavHostKt {
-    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state);
-    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state);
+    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional boolean userSwipeEnabled, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state);
+    method @Deprecated @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional boolean userSwipeEnabled, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method @androidx.compose.runtime.Composable public static androidx.wear.compose.navigation.SwipeDismissableNavHostState rememberSwipeDismissableNavHostState(optional androidx.wear.compose.foundation.SwipeToDismissBoxState swipeToDismissBoxState);
     method @Deprecated @androidx.compose.runtime.Composable public static androidx.wear.compose.navigation.SwipeDismissableNavHostState rememberSwipeDismissableNavHostState(optional androidx.wear.compose.material.SwipeToDismissBoxState swipeToDismissBoxState);
   }
diff --git a/wear/compose/compose-navigation/api/restricted_current.txt b/wear/compose/compose-navigation/api/restricted_current.txt
index 30cd692..e27dd9d 100644
--- a/wear/compose/compose-navigation/api/restricted_current.txt
+++ b/wear/compose/compose-navigation/api/restricted_current.txt
@@ -11,8 +11,10 @@
   }
 
   public final class SwipeDismissableNavHostKt {
-    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state);
-    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state);
+    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional boolean userSwipeEnabled, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state);
+    method @Deprecated @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void SwipeDismissableNavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional boolean userSwipeEnabled, optional androidx.wear.compose.navigation.SwipeDismissableNavHostState state, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method @androidx.compose.runtime.Composable public static androidx.wear.compose.navigation.SwipeDismissableNavHostState rememberSwipeDismissableNavHostState(optional androidx.wear.compose.foundation.SwipeToDismissBoxState swipeToDismissBoxState);
     method @Deprecated @androidx.compose.runtime.Composable public static androidx.wear.compose.navigation.SwipeDismissableNavHostState rememberSwipeDismissableNavHostState(optional androidx.wear.compose.material.SwipeToDismissBoxState swipeToDismissBoxState);
   }
diff --git a/wear/compose/compose-navigation/src/androidTest/kotlin/androidx/wear/compose/navigation/SwipeDismissableNavHostTest.kt b/wear/compose/compose-navigation/src/androidTest/kotlin/androidx/wear/compose/navigation/SwipeDismissableNavHostTest.kt
index 655e7bc..360df14 100644
--- a/wear/compose/compose-navigation/src/androidTest/kotlin/androidx/wear/compose/navigation/SwipeDismissableNavHostTest.kt
+++ b/wear/compose/compose-navigation/src/androidTest/kotlin/androidx/wear/compose/navigation/SwipeDismissableNavHostTest.kt
@@ -101,6 +101,21 @@
     }
 
     @Test
+    fun does_not_navigate_back_to_previous_level_when_swipe_disabled() {
+        rule.setContentWithTheme {
+            SwipeDismissWithNavigation(userSwipeEnabled = false)
+        }
+
+        // Click to move to next destination then swipe to dismiss.
+        rule.onNodeWithText(START).performClick()
+        rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+
+        // Should still display "next".
+        rule.onNodeWithText(NEXT).assertExists()
+        rule.onNodeWithText(START).assertDoesNotExist()
+    }
+
+    @Test
     fun navigates_back_to_previous_level_with_back_button() {
         val onBackPressedDispatcher = OnBackPressedDispatcher()
         val dispatcherOwner =
@@ -423,12 +438,14 @@
 
     @Composable
     fun SwipeDismissWithNavigation(
-        navController: NavHostController = rememberSwipeDismissableNavController()
+        navController: NavHostController = rememberSwipeDismissableNavController(),
+        userSwipeEnabled: Boolean = true
     ) {
         SwipeDismissableNavHost(
             navController = navController,
             startDestination = START,
             modifier = Modifier.testTag(TEST_TAG),
+            userSwipeEnabled = userSwipeEnabled
         ) {
             composable(START) {
                 CompactChip(
diff --git a/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt b/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
index c373ef9..377b170 100644
--- a/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
+++ b/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
@@ -76,6 +76,7 @@
  * @param navController The navController for this host
  * @param startDestination The route for the start destination
  * @param modifier The modifier to be applied to the layout
+ * @param userSwipeEnabled [Boolean] Whether swipe-to-dismiss gesture is enabled.
  * @param state State containing information about ongoing swipe and animation.
  * @param route The route for the graph
  * @param builder The builder used to construct the graph
@@ -85,6 +86,7 @@
     navController: NavHostController,
     startDestination: String,
     modifier: Modifier = Modifier,
+    userSwipeEnabled: Boolean = true,
     state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
     route: String? = null,
     builder: NavGraphBuilder.() -> Unit
@@ -95,6 +97,7 @@
             navController.createGraph(startDestination, route, builder)
         },
         modifier,
+        userSwipeEnabled,
         state = state,
     )
 
@@ -122,6 +125,7 @@
  * @param navController [NavHostController] for this host
  * @param graph Graph for this host
  * @param modifier [Modifier] to be applied to the layout
+ * @param userSwipeEnabled [Boolean] Whether swipe-to-dismiss gesture is enabled.
  * @param state State containing information about ongoing swipe and animation.
  *
  * @throws IllegalArgumentException if no WearNavigation.Destination is on the navigation backstack.
@@ -131,6 +135,7 @@
     navController: NavHostController,
     graph: NavGraph,
     modifier: Modifier = Modifier,
+    userSwipeEnabled: Boolean = true,
     state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
 ) {
     val lifecycleOwner = LocalLifecycleOwner.current
@@ -217,7 +222,7 @@
     SwipeToDismissBox(
         state = swipeState,
         modifier = Modifier,
-        userSwipeEnabled = previous != null,
+        userSwipeEnabled = userSwipeEnabled && previous != null,
         backgroundKey = previous?.id ?: SwipeToDismissKeys.Background,
         contentKey = current?.id ?: SwipeToDismissKeys.Content,
         content = { isBackground ->
@@ -243,6 +248,104 @@
 }
 
 /**
+ * Provides a place in the Compose hierarchy for self-contained navigation to occur,
+ * with backwards navigation provided by a swipe gesture.
+ *
+ * Once this is called, any Composable within the given [NavGraphBuilder] can be navigated to from
+ * the provided [navController].
+ *
+ * The builder passed into this method is [remember]ed. This means that for this NavHost, the
+ * contents of the builder cannot be changed.
+ *
+ * Content is displayed within a [SwipeToDismissBox], showing the current navigation level.
+ * During a swipe-to-dismiss gesture, the previous navigation level (if any) is shown in
+ * the background. BackgroundScrimColor and ContentScrimColor of it are taken from
+ * [LocalSwipeToDismissBackgroundScrimColor] and [LocalSwipeToDismissContentScrimColor].
+ *
+ * Example of a [SwipeDismissableNavHost] alternating between 2 screens:
+ * @sample androidx.wear.compose.navigation.samples.SimpleNavHost
+ *
+ * Example of a [SwipeDismissableNavHost] for which a destination has a named argument:
+ * @sample androidx.wear.compose.navigation.samples.NavHostWithNamedArgument
+ *
+ * @param navController The navController for this host
+ * @param startDestination The route for the start destination
+ * @param modifier The modifier to be applied to the layout
+ * @param state State containing information about ongoing swipe and animation.
+ * @param route The route for the graph
+ * @param builder The builder used to construct the graph
+ */
+@Deprecated(
+    "This overload is provided for backwards compatibility. " +
+        "A newer overload is available with an additional userSwipeEnabled param.",
+    level = DeprecationLevel.HIDDEN
+)
+@Composable
+public fun SwipeDismissableNavHost(
+    navController: NavHostController,
+    startDestination: String,
+    modifier: Modifier = Modifier,
+    state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
+    route: String? = null,
+    builder: NavGraphBuilder.() -> Unit
+) = SwipeDismissableNavHost(
+    navController = navController,
+    startDestination = startDestination,
+    modifier = modifier,
+    userSwipeEnabled = true,
+    state = state,
+    route = route,
+    builder = builder
+)
+
+/**
+ * Provides a place in the Compose hierarchy for self-contained navigation to occur,
+ * with backwards navigation provided by a swipe gesture.
+ *
+ * Once this is called, any Composable within the given [NavGraphBuilder] can be navigated to from
+ * the provided [navController].
+ *
+ * The builder passed into this method is [remember]ed. This means that for this NavHost, the
+ * contents of the builder cannot be changed.
+ *
+ * Content is displayed within a [SwipeToDismissBox], showing the current navigation level.
+ * During a swipe-to-dismiss gesture, the previous navigation level (if any) is shown in
+ * the background. BackgroundScrimColor and ContentScrimColor of it are taken from
+ * [LocalSwipeToDismissBackgroundScrimColor] and [LocalSwipeToDismissContentScrimColor].
+ *
+ * Example of a [SwipeDismissableNavHost] alternating between 2 screens:
+ * @sample androidx.wear.compose.navigation.samples.SimpleNavHost
+ *
+ * Example of a [SwipeDismissableNavHost] for which a destination has a named argument:
+ * @sample androidx.wear.compose.navigation.samples.NavHostWithNamedArgument
+ *
+ * @param navController [NavHostController] for this host
+ * @param graph Graph for this host
+ * @param modifier [Modifier] to be applied to the layout
+ * @param state State containing information about ongoing swipe and animation.
+ *
+ * @throws IllegalArgumentException if no WearNavigation.Destination is on the navigation backstack.
+ */
+@Deprecated(
+    "This overload is provided for backwards compatibility. " +
+        "A newer overload is available with an additional userSwipeEnabled param.",
+    level = DeprecationLevel.HIDDEN
+)
+@Composable
+public fun SwipeDismissableNavHost(
+    navController: NavHostController,
+    graph: NavGraph,
+    modifier: Modifier = Modifier,
+    state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
+) = SwipeDismissableNavHost(
+    navController = navController,
+    graph = graph,
+    modifier = modifier,
+    userSwipeEnabled = true,
+    state = state
+)
+
+/**
  * State for [SwipeDismissableNavHost]
  *
  * @param swipeToDismissBoxState State for [SwipeToDismissBox], which is used to support the
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index fa0c223..36e7808d 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -26,8 +26,8 @@
         applicationId "androidx.wear.compose.integration.demos"
         minSdk 25
         targetSdk 30
-        versionCode 15
-        versionName "1.15"
+        versionCode 16
+        versionName "1.16"
         // Change the APK name to match the *testapp regex we use to pick up APKs for testing as
         // part of CI.
         archivesBaseName = "wear-compose-demos-testapp"
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
index 02fcfac..d06de43 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
@@ -43,6 +43,7 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.SwipeToDismissBox
 import androidx.wear.compose.foundation.SwipeToDismissBoxState
 import androidx.wear.compose.foundation.SwipeToDismissKeys
@@ -54,6 +55,7 @@
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
 import androidx.wear.compose.foundation.lazy.ScalingParams
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
 import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
 import androidx.wear.compose.integration.demos.common.ActivityDemo
 import androidx.wear.compose.integration.demos.common.ComposableDemo
@@ -133,7 +135,9 @@
 ) {
     ScalingLazyColumnWithRSB(
         horizontalAlignment = Alignment.CenterHorizontally,
-        modifier = Modifier.fillMaxWidth().testTag(DemoListTag),
+        modifier = Modifier
+            .fillMaxWidth()
+            .testTag(DemoListTag),
     ) {
         item {
             ListHeader {
@@ -169,7 +173,9 @@
                     ) {
                         Text(
                             text = description,
-                            modifier = Modifier.fillMaxWidth().align(Alignment.Center),
+                            modifier = Modifier
+                                .fillMaxWidth()
+                                .align(Alignment.Center),
                             textAlign = TextAlign.Center
                         )
                     }
@@ -259,13 +265,15 @@
             true
         }.let {
             if (focusRequester != null) {
-                it.focusRequester(focusRequester)
+                it
+                    .focusRequester(focusRequester)
                     .focusable()
             } else it
         }
     }
 }
 
+@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 fun ScalingLazyColumnWithRSB(
     modifier: Modifier = Modifier,
@@ -284,7 +292,7 @@
     val flingBehavior = if (snap) ScalingLazyColumnDefaults.snapFlingBehavior(
         state = state
     ) else ScrollableDefaults.flingBehavior()
-    val focusRequester = remember { FocusRequester() }
+    val focusRequester = rememberActiveFocusRequester()
     ScalingLazyColumn(
         modifier = modifier.rsbScroll(
             scrollableState = state,
@@ -300,7 +308,4 @@
         autoCentering = autoCentering,
         content = content
     )
-    LaunchedEffect(Unit) {
-        focusRequester.requestFocus()
-    }
 }
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/TimeFormatText.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/TimeFormatText.java
index e36eedb..1ecdca3 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/TimeFormatText.java
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/TimeFormatText.java
@@ -39,6 +39,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public final class TimeFormatText implements TimeDependentText {
+    private static final Date sDate = new Date();
 
     @Override
     public boolean equals(Object o) {
@@ -48,13 +49,12 @@
         return mStyle == that.mStyle
                 && mTimePrecision == that.mTimePrecision
                 && Objects.equals(mDateFormat, that.mDateFormat)
-                && Objects.equals(mTimeZone, that.mTimeZone)
-                && Objects.equals(mDate.toString(), that.mDate.toString());
+                && Objects.equals(mTimeZone, that.mTimeZone);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mDateFormat, mStyle, mTimeZone, mDate, mTimePrecision);
+        return Objects.hash(mDateFormat, mStyle, mTimeZone, mTimePrecision);
     }
 
     @NonNull
@@ -69,8 +69,6 @@
                 + mStyle
                 + ", mTimeZone="
                 + mTimeZone
-                + ", mDate="
-                + mDate
                 + ", mTimePrecision="
                 + mTimePrecision
                 + '}';
@@ -98,7 +96,6 @@
 
     @ComplicationText.TimeFormatStyle private final int mStyle;
     private final TimeZone mTimeZone;
-    private final Date mDate;
     private long mTimePrecision;
 
     public TimeFormatText(
@@ -117,7 +114,6 @@
         } else {
             mTimeZone = mDateFormat.getTimeZone();
         }
-        mDate = new Date();
     }
 
     TimeFormatText(
@@ -128,7 +124,6 @@
         mDateFormat = dateFormat;
         mStyle = style;
         mTimeZone = timeZone;
-        mDate = new Date();
         mTimePrecision = timePrecision;
     }
 
@@ -229,8 +224,8 @@
     }
 
     private long getOffset(long date) {
-        mDate.setTime(date);
-        if (mTimeZone.inDaylightTime(mDate)) {
+        sDate.setTime(date);
+        if (mTimeZone.inDaylightTime(sDate)) {
             return (long) mTimeZone.getRawOffset() + mTimeZone.getDSTSavings();
         }
         return mTimeZone.getRawOffset();
@@ -277,7 +272,6 @@
         this.mStyle = in.readInt();
         this.mTimeZone = (TimeZone) in.readSerializable();
         this.mTimePrecision = -1;
-        this.mDate = new Date();
     }
 
     public static final Creator<TimeFormatText> CREATOR =
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index 9255883..e7e8235 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -632,6 +632,8 @@
 
                 private val backgroundHandler = Handler(backgroundHandlerThread.looper)
 
+                override var editorObscuresWatchFace: Boolean = false
+
                 override val userStyleSchema = userStyleRepository.schema
                 override var userStyle: UserStyle
                     get() = userStyleRepository.userStyle.value
diff --git a/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt b/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
index 70b05b1..55da8af 100644
--- a/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
+++ b/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
@@ -427,6 +427,7 @@
 ) : EditorSession {
     protected var closed: Boolean = false
     protected var forceClosed: Boolean = false
+    protected open var editorObscuresWatchFace = false
 
     private val editorSessionTraceEvent = AsyncTraceEvent("EditorSession")
     private val closeCallback =
@@ -522,6 +523,10 @@
                 "Can't configure fixed complication ID $complicationSlotId"
             }
 
+            // Don't animate the watch face while the provider is running, because that makes
+            // hardware rendering of the complication preview images very much slower.
+            editorObscuresWatchFace = true
+
             val deferredResult = CompletableDeferred<ComplicationDataSourceChooserResult?>()
 
             synchronized(this) {
@@ -552,6 +557,8 @@
                     synchronized(this) { pendingComplicationDataSourceChooserResult = null }
                 }
 
+            editorObscuresWatchFace = false
+
             // If deferredResult was null then the user canceled so return null.
             if (complicationDataSourceChooserResult == null) {
                 return null
@@ -844,6 +851,12 @@
 
     internal val wrappedUserStyle by lazy { MutableStateFlow(editorDelegate.userStyle) }
 
+    override var editorObscuresWatchFace: Boolean
+        get() = editorDelegate.editorObscuresWatchFace
+        set(value) {
+            editorDelegate.editorObscuresWatchFace = value
+        }
+
     // Unfortunately a dynamic proxy is the only way we can reasonably validate the UserStyle,
     // exceptions thrown within a coroutine are lost and the MutableStateFlow interface includes
     // internal unstable methods so we can't use a static proxy...
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index fba8756..85b31e1 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -22,6 +22,7 @@
 import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Paint
+import android.graphics.Picture
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffXfermode
 import android.graphics.Rect
@@ -39,6 +40,7 @@
 import androidx.annotation.IntDef
 import androidx.annotation.IntRange
 import androidx.annotation.Px
+import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.annotation.UiThread
 import androidx.annotation.WorkerThread
@@ -78,8 +80,8 @@
      * to a software canvas.
      *
      * NOTE the system takes screenshots for use in the watch face picker UI and these will be
-     * taken using software rendering. This means [Bitmap]s with [Bitmap.Config.HARDWARE] must
-     * be avoided.
+     * taken using software rendering for API level 27 and below. This means on API level 27 and
+     * below [Bitmap]s with [Bitmap.Config.HARDWARE] must be avoided.
      */
     public const val HARDWARE: Int = 1
 }
@@ -605,22 +607,39 @@
             renderParameters: RenderParameters
         ): Bitmap =
             TraceEvent("CanvasRenderer.takeScreenshot").use {
-                val bitmap =
-                    Bitmap.createBitmap(
-                        screenBounds.width(),
-                        screenBounds.height(),
-                        Bitmap.Config.ARGB_8888
-                    )
                 val prevRenderParameters = this.renderParameters
                 val originalIsForScreenshot = renderParameters.isForScreenshot
 
                 renderParameters.isForScreenshot = true
                 this.renderParameters = renderParameters
-                renderAndComposite(Canvas(bitmap), zonedDateTime)
-                this.renderParameters = prevRenderParameters
-                renderParameters.isForScreenshot = originalIsForScreenshot
 
-                return bitmap
+                if (Build.VERSION.SDK_INT >= 28) {
+                    val picture = Picture()
+                    renderAndComposite(
+                        picture.beginRecording(screenBounds.width(), screenBounds.height()),
+                        zonedDateTime
+                    )
+                    picture.endRecording()
+                    this.renderParameters = prevRenderParameters
+                    renderParameters.isForScreenshot = originalIsForScreenshot
+                    return Api28CreateBitmapHelper.createBitmap(
+                        picture,
+                        screenBounds.width(),
+                        screenBounds.height(),
+                        Bitmap.Config.ARGB_8888
+                    )
+                } else {
+                    val bitmap =
+                        Bitmap.createBitmap(
+                            screenBounds.width(),
+                            screenBounds.height(),
+                            Bitmap.Config.ARGB_8888
+                        )
+                    renderAndComposite(Canvas(bitmap), zonedDateTime)
+                    this.renderParameters = prevRenderParameters
+                    renderParameters.isForScreenshot = originalIsForScreenshot
+                    return bitmap
+                }
             }
 
         internal override fun renderScreenshotToSurface(
@@ -653,17 +672,34 @@
                 // Render and composite the HighlightLayer
                 val highlightLayer = renderParameters.highlightLayer
                 if (highlightLayer != null) {
-                    val highlightLayerBitmap =
-                        Bitmap.createBitmap(
+                    val highlightLayerBitmap: Bitmap
+                    if (Build.VERSION.SDK_INT >= 28) {
+                        val picture = Picture()
+                        val highlightCanvas =
+                            picture.beginRecording(screenBounds.width(), screenBounds.height())
+                        if (clearWithBackgroundTintBeforeRenderingHighlightLayer) {
+                            highlightCanvas.drawColor(highlightLayer.backgroundTint)
+                        }
+                        renderHighlightLayer(highlightCanvas, screenBounds, zonedDateTime)
+                        picture.endRecording()
+                        highlightLayerBitmap = Api28CreateBitmapHelper.createBitmap(
+                            picture,
                             screenBounds.width(),
                             screenBounds.height(),
                             Bitmap.Config.ARGB_8888
                         )
-                    val highlightCanvas = Canvas(highlightLayerBitmap)
-                    if (clearWithBackgroundTintBeforeRenderingHighlightLayer) {
-                        highlightCanvas.drawColor(highlightLayer.backgroundTint)
+                    } else {
+                        highlightLayerBitmap = Bitmap.createBitmap(
+                            screenBounds.width(),
+                            screenBounds.height(),
+                            Bitmap.Config.ARGB_8888
+                        )
+                        val highlightCanvas = Canvas(highlightLayerBitmap)
+                        if (clearWithBackgroundTintBeforeRenderingHighlightLayer) {
+                            highlightCanvas.drawColor(highlightLayer.backgroundTint)
+                        }
+                        renderHighlightLayer(highlightCanvas, screenBounds, zonedDateTime)
                     }
-                    renderHighlightLayer(highlightCanvas, screenBounds, zonedDateTime)
                     canvas.drawBitmap(highlightLayerBitmap, 0f, 0f, HIGHLIGHT_LAYER_COMPOSITE_PAINT)
                     highlightLayerBitmap.recycle()
                 }
@@ -1761,3 +1797,14 @@
         }
     }
 }
+
+/** Helper to allow class verification. */
+@RequiresApi(28)
+internal object Api28CreateBitmapHelper {
+    fun createBitmap(
+        picture: Picture,
+        width: Int,
+        height: Int,
+        config: Bitmap.Config
+    ) = Bitmap.createBitmap(picture, width, height, config)
+}
\ No newline at end of file
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 33bfea4..20f9460 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -25,6 +25,7 @@
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.Color
+import android.graphics.Picture
 import android.graphics.PixelFormat
 import android.graphics.Rect
 import android.os.Build
@@ -50,6 +51,7 @@
 import androidx.wear.watchface.complications.data.ComplicationData
 import androidx.wear.watchface.complications.data.toApiComplicationData
 import androidx.wear.watchface.control.HeadlessWatchFaceImpl
+import androidx.wear.watchface.control.InteractiveInstanceManager
 import androidx.wear.watchface.control.RemoteWatchFaceView
 import androidx.wear.watchface.control.WatchFaceControlService
 import androidx.wear.watchface.control.data.ComplicationRenderParams
@@ -281,6 +283,12 @@
         public val complicationRationaleDialogIntent: Intent?
 
         /**
+         * Allows the delegate to inform the watch face if it's obscured by the editor UI. If the
+         * watch face is not visible, it will stop animating.
+         */
+        public var editorObscuresWatchFace: Boolean
+
+        /**
          * Renders the watchface to a [Bitmap] with the [CurrentUserStyleRepository]'s [UserStyle].
          */
         public fun renderWatchFaceToBitmap(
@@ -564,6 +572,16 @@
     internal val broadcastsObserver: BroadcastsObserver,
     internal var broadcastsReceiver: BroadcastsReceiver?
 ) {
+    internal var editorObscuresWatchFace = false
+        set(value) {
+            field = value
+
+            // Start animating again if needed.
+            if (!value) {
+                scheduleDraw()
+            }
+        }
+
     internal companion object {
         internal const val NO_DEFAULT_DATA_SOURCE = SystemDataSources.NO_DATA_SOURCE
 
@@ -854,6 +872,15 @@
         override val complicationRationaleDialogIntent
             get() = watchFaceHostApi.getComplicationRationaleIntent()
 
+        override var editorObscuresWatchFace: Boolean
+            get() = InteractiveInstanceManager
+                .getCurrentInteractiveInstance()?.engine?.editorObscuresWatchFace ?: false
+            set(value) {
+                InteractiveInstanceManager.getCurrentInteractiveInstance()?.engine?.let {
+                    it.editorObscuresWatchFace = value
+                }
+            }
+
         override fun renderWatchFaceToBitmap(
             renderParameters: RenderParameters,
             instant: Instant,
@@ -902,6 +929,9 @@
         @SuppressLint("NewApi") // release
         override fun onDestroy(): Unit =
             TraceEvent("WFEditorDelegate.onDestroy").use {
+                InteractiveInstanceManager.getCurrentInteractiveInstance()?.engine?.let {
+                    it.editorObscuresWatchFace = false
+                }
                 if (watchState.isHeadless) {
                     headlessWatchFaceImpl!!.release()
                     [email protected]()
@@ -943,7 +973,9 @@
     private fun scheduleDraw() {
         // Separate calls are issued to deliver the state of isAmbient and isVisible, so during init
         // we might not yet know the state of both (which is required by the shouldAnimate logic).
-        if (!watchState.isAmbient.hasValue() || !watchState.isVisible.hasValue()) {
+        // If the editor is obscuring the watch face, there's no need to schedule a frame.
+        if (!watchState.isAmbient.hasValue() || !watchState.isVisible.hasValue() ||
+            editorObscuresWatchFace) {
             return
         }
 
@@ -992,7 +1024,8 @@
         renderer.renderInternal(startTime)
         lastDrawTimeMillis = startTimeMillis
 
-        if (renderer.shouldAnimate()) {
+        // If the editor is obscuring the watch face, there's no need to draw.
+        if (renderer.shouldAnimate() && !editorObscuresWatchFace) {
             val currentTimeMillis = systemTimeProvider.getSystemTimeMillis()
             var delayMillis =
                 computeDelayTillNextFrame(startTimeMillis, currentTimeMillis, Instant.now())
@@ -1218,8 +1251,6 @@
                 }
 
                 val bounds = it.computeBounds(renderer.screenBounds)
-                val complicationBitmap =
-                    Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888)
 
                 var prevData: ComplicationData? = null
                 val screenshotComplicationData = params.complicationData
@@ -1232,13 +1263,38 @@
                     )
                 }
 
-                it.renderer.render(
-                    Canvas(complicationBitmap),
-                    Rect(0, 0, bounds.width(), bounds.height()),
-                    zonedDateTime,
-                    RenderParameters(params.renderParametersWireFormat),
-                    params.complicationSlotId
-                )
+                val complicationBitmap: Bitmap
+                val picture = Picture()
+                if (Build.VERSION.SDK_INT >= 28) {
+                    it.renderer.render(
+                        picture.beginRecording(bounds.width(), bounds.height()),
+                        Rect(0, 0, bounds.width(), bounds.height()),
+                        zonedDateTime,
+                        RenderParameters(params.renderParametersWireFormat),
+                        params.complicationSlotId
+                    )
+                    picture.endRecording()
+                    complicationBitmap = Api28CreateBitmapHelper.createBitmap(
+                        picture,
+                        bounds.width(),
+                        bounds.height(),
+                        Bitmap.Config.ARGB_8888
+                    )
+                } else {
+                    complicationBitmap =
+                        Bitmap.createBitmap(
+                            bounds.width(),
+                            bounds.height(),
+                            Bitmap.Config.ARGB_8888
+                        )
+                    it.renderer.render(
+                        Canvas(complicationBitmap),
+                        Rect(0, 0, bounds.width(), bounds.height()),
+                        zonedDateTime,
+                        RenderParameters(params.renderParametersWireFormat),
+                        params.complicationSlotId
+                    )
+                }
 
                 // No point in restoring the old style and complication if this is headless.
                 if (!watchState.isHeadless) {
@@ -1257,7 +1313,9 @@
                     }
                 }
 
-                SharedMemoryImage.ashmemWriteImageBundle(complicationBitmap)
+                val bundle = SharedMemoryImage.ashmemWriteImageBundle(complicationBitmap)
+                complicationBitmap.recycle()
+                bundle
             }
         }
 
@@ -1276,6 +1334,7 @@
             "currentUserStyleRepository.userStyle=${currentUserStyleRepository.userStyle.value}"
         )
         writer.println("currentUserStyleRepository.schema=${currentUserStyleRepository.schema}")
+        writer.println("editorObscuresWatchFace=$editorObscuresWatchFace")
         overlayStyle.dump(writer)
         watchState.dump(writer)
         complicationSlotsManager.dump(writer)
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 537923c..d9c04a97 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -1224,6 +1224,12 @@
          */
         private var frameCallbackPending = false
 
+        internal var editorObscuresWatchFace = false
+            set(value) {
+                getWatchFaceImplOrNull()?.editorObscuresWatchFace = value
+                field = value
+            }
+
         private val frameCallback =
             object : Choreographer.FrameCallback {
                 @SuppressWarnings("SyntheticAccessor")
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index d6e5cdc..07232b0 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -163,6 +163,15 @@
             }
         }
 
+        fun getCurrentInteractiveInstance(): InteractiveWatchFaceImpl? {
+            synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
+                if (instances.size == 1) {
+                    return instances.entries.first().value.impl
+                }
+            }
+            return null
+        }
+
         /** Can be called on any thread. */
         @SuppressLint("SyntheticAccessor")
         fun getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
diff --git a/work/work-lint/src/main/java/androidx/work/lint/IdleBatteryChargingConstraintsDetector.kt b/work/work-lint/src/main/java/androidx/work/lint/IdleBatteryChargingConstraintsDetector.kt
index 975a5a8..d9d3f47 100644
--- a/work/work-lint/src/main/java/androidx/work/lint/IdleBatteryChargingConstraintsDetector.kt
+++ b/work/work-lint/src/main/java/androidx/work/lint/IdleBatteryChargingConstraintsDetector.kt
@@ -34,6 +34,7 @@
 import org.jetbrains.uast.UQualifiedReferenceExpression
 import org.jetbrains.uast.USimpleNameReferenceExpression
 import org.jetbrains.uast.getParentOfType
+import org.jetbrains.uast.skipParenthesizedExprDown
 import org.jetbrains.uast.toUElement
 import org.jetbrains.uast.visitor.AbstractUastVisitor
 
@@ -110,9 +111,10 @@
     }
 
     fun UCallExpression.identifierName(): String? {
-        var current = receiver
+        var current = receiver?.skipParenthesizedExprDown()
         while (current != null && current !is USimpleNameReferenceExpression) {
-            current = (current as? UQualifiedReferenceExpression)?.receiver
+            current =
+                (current as? UQualifiedReferenceExpression)?.receiver?.skipParenthesizedExprDown()
         }
         if (current != null && current is USimpleNameReferenceExpression) {
             return current.identifier
diff --git a/work/work-lint/src/main/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetector.kt b/work/work-lint/src/main/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetector.kt
index 6404e126cc..eec241d 100644
--- a/work/work-lint/src/main/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetector.kt
+++ b/work/work-lint/src/main/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetector.kt
@@ -32,6 +32,9 @@
 import org.jetbrains.kotlin.name.ClassId
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.getParameterForArgument
+import org.jetbrains.uast.skipParenthesizedExprDown
 
 /**
  * Ensures a valid interval duration for a `PeriodicWorkRequest`.
@@ -65,11 +68,20 @@
         constructor: PsiMethod
     ) {
         if (node.valueArgumentCount >= 2) {
-            val type = node.valueArguments[1].getExpressionType()?.canonicalText
+            // TestMode.PARENTHESIZED wraps Duration call in parenthesizes
+            val repeatInterval = node.valueArguments.find {
+                node.getParameterForArgument(it)?.name == "repeatInterval"
+            }?.skipParenthesizedExprDown()
+
+            val timeUnit = node.valueArguments.find {
+                node.getParameterForArgument(it)?.name == "repeatIntervalTimeUnit"
+            }?.skipParenthesizedExprDown()
+
+            val type = repeatInterval?.getExpressionType()?.canonicalText
             if ("long" == type) {
-                val value = node.valueArguments[1].evaluate() as? Long
+                val value = repeatInterval.evaluate() as? Long
                 // TimeUnit
-                val units = node.valueArguments[2].evaluate() as? Pair<ClassId, Name>
+                val units = timeUnit?.evaluate() as? Pair<ClassId, Name>
                 if (value != null && units != null) {
                     val (_, timeUnitType) = units
                     val interval: Long? = when (timeUnitType.identifier) {
@@ -94,21 +106,26 @@
                     }
                 }
             } else if ("java.time.Duration" == type) {
-                val source = node.valueArguments[1].asSourceString()
                 // Look for the most common Duration specification
                 // Example: Duration.ofMinutes(15)
-                val regexp = Regex("Duration.of(\\w+)\\((\\d+)\\)")
-                val matchResult = regexp.matchEntire(source)
-                if (matchResult != null) {
-                    val unit = matchResult.groupValues[1]
-                    val value = matchResult.groupValues[2].toLong()
+
+                val callExpression: UCallExpression? = when (repeatInterval) {
+                    // ofMinutes(...)
+                    is UCallExpression -> repeatInterval
+                    // Duration.ofMinutes(...)
+                    is UQualifiedReferenceExpression -> repeatInterval.selector as? UCallExpression
+                    else -> null
+                }
+                val unit = callExpression?.methodName
+                val value = callExpression?.valueArguments?.firstOrNull()?.evaluate() as? Long
+                if (value != null) {
                     val interval: Long? = when (unit) {
-                        "Nanos" -> TimeUnit.MINUTES.convert(value, TimeUnit.NANOSECONDS)
-                        "Millis" -> TimeUnit.MINUTES.convert(value, TimeUnit.MILLISECONDS)
-                        "Seconds" -> TimeUnit.MINUTES.convert(value, TimeUnit.SECONDS)
-                        "Minutes" -> value
-                        "Hours" -> TimeUnit.MINUTES.convert(value, TimeUnit.HOURS)
-                        "Days" -> TimeUnit.MINUTES.convert(value, TimeUnit.DAYS)
+                        "ofNanos" -> TimeUnit.MINUTES.convert(value, TimeUnit.NANOSECONDS)
+                        "ofMillis" -> TimeUnit.MINUTES.convert(value, TimeUnit.MILLISECONDS)
+                        "ofSeconds" -> TimeUnit.MINUTES.convert(value, TimeUnit.SECONDS)
+                        "ofMinutes" -> value
+                        "ofHours" -> TimeUnit.MINUTES.convert(value, TimeUnit.HOURS)
+                        "ofDays" -> TimeUnit.MINUTES.convert(value, TimeUnit.DAYS)
                         else -> null
                     }
                     if (interval != null && interval < 15) {
diff --git a/work/work-lint/src/test/java/androidx/work/lint/IdleBatteryChargingConstraintsDetectorTest.kt b/work/work-lint/src/test/java/androidx/work/lint/IdleBatteryChargingConstraintsDetectorTest.kt
index 02ae6d1..16babb0 100644
--- a/work/work-lint/src/test/java/androidx/work/lint/IdleBatteryChargingConstraintsDetectorTest.kt
+++ b/work/work-lint/src/test/java/androidx/work/lint/IdleBatteryChargingConstraintsDetectorTest.kt
@@ -19,7 +19,6 @@
 import androidx.work.lint.Stubs.CONSTRAINTS
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
 import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
-import org.junit.Ignore
 import org.junit.Test
 
 class IdleBatteryChargingConstraintsDetectorTest {
@@ -147,7 +146,6 @@
             .expectClean()
     }
 
-    @Ignore("b/196831196")
     @Test
     fun noWarningsWhenSeparateConstraints() {
         val customApplication = kotlin(
diff --git a/work/work-lint/src/test/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetectorTest.kt b/work/work-lint/src/test/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetectorTest.kt
index b109b25..bfa5e82 100644
--- a/work/work-lint/src/test/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetectorTest.kt
+++ b/work/work-lint/src/test/java/androidx/work/lint/InvalidPeriodicWorkRequestIntervalDetectorTest.kt
@@ -20,7 +20,6 @@
 import androidx.work.lint.Stubs.PERIODIC_WORK_REQUEST
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
 import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
-import org.junit.Ignore
 import org.junit.Test
 
 class InvalidPeriodicWorkRequestIntervalDetectorTest {
@@ -33,12 +32,10 @@
 
             import androidx.work.ListenableWorker
 
-            class TestWorker: ListenableWorker() {
-
-            }
+            class TestWorker: ListenableWorker()
             """
         ).indented().within("src")
-
+        /* ktlint-disable max-line-length */
         val snippet = kotlin(
             "com/example/Test.kt",
             """
@@ -50,14 +47,11 @@
 
             class Test {
                 fun enqueue() {
-                    val worker = TestWorker()
-                    val builder = PeriodicWorkRequest.Builder(worker, 15L, TimeUnit.MILLISECONDS)
+                    val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, 15L, TimeUnit.MILLISECONDS)
                 }
             }
             """
         ).indented().within("src")
-
-        /* ktlint-disable max-line-length */
         lint().files(
             LISTENABLE_WORKER,
             PERIODIC_WORK_REQUEST,
@@ -67,9 +61,9 @@
             .run()
             .expect(
                 """
-                src/com/example/Test.kt:10: Error: Interval duration for `PeriodicWorkRequest`s must be at least 15 minutes. [InvalidPeriodicWorkRequestInterval]
-                        val builder = PeriodicWorkRequest.Builder(worker, 15L, TimeUnit.MILLISECONDS)
-                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                src/com/example/Test.kt:9: Error: Interval duration for `PeriodicWorkRequest`s must be at least 15 minutes. [InvalidPeriodicWorkRequestInterval]
+                        val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, 15L, TimeUnit.MILLISECONDS)
+                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                 1 errors, 0 warnings
                 """.trimIndent()
             )
@@ -85,12 +79,11 @@
 
             import androidx.work.ListenableWorker
 
-            class TestWorker: ListenableWorker() {
-
-            }
+            class TestWorker: ListenableWorker()
             """
         ).indented().within("src")
 
+        /* ktlint-disable max-line-length */
         val snippet = kotlin(
             "com/example/Test.kt",
             """
@@ -103,11 +96,12 @@
             class Test {
                 fun enqueue() {
                     val worker = TestWorker()
-                    val builder = PeriodicWorkRequest.Builder(worker, 15L, TimeUnit.MINUTES)
+                    val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, 15L, TimeUnit.MINUTES)
                 }
             }
             """
         ).indented().within("src")
+        /* ktlint-enable max-line-length */
 
         lint().files(
             LISTENABLE_WORKER,
@@ -119,7 +113,54 @@
             .expectClean()
     }
 
-    @Ignore("b/196831196")
+    @Test
+    fun testWithInvalidDurationTypeStaticImport() {
+        val worker = kotlin(
+            "com/example/TestWorker.kt",
+            """
+            package com.example
+
+            import androidx.work.ListenableWorker
+
+            class TestWorker: ListenableWorker()
+            """
+        ).indented().within("src")
+        /* ktlint-disable max-line-length */
+        val snippet = kotlin(
+            "com/example/Test.kt",
+            """
+            package com.example
+
+            import androidx.work.PeriodicWorkRequest
+            import com.example.TestWorker
+            import java.time.Duration.ofNanos
+
+            class Test {
+                fun enqueue() {
+                    val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, ofNanos(10L))
+                }
+            }
+            """
+        ).indented().within("src")
+
+        lint().files(
+            LISTENABLE_WORKER,
+            PERIODIC_WORK_REQUEST,
+            worker,
+            snippet
+        ).issues(InvalidPeriodicWorkRequestIntervalDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                src/com/example/Test.kt:9: Error: Interval duration for `PeriodicWorkRequest`s must be at least 15 minutes. [InvalidPeriodicWorkRequestInterval]
+                        val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, ofNanos(10L))
+                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                1 errors, 0 warnings
+                """.trimIndent()
+            )
+        /* ktlint-enable max-line-length */
+    }
+
     @Test
     fun testWithInvalidDurationType() {
         val worker = kotlin(
@@ -129,12 +170,10 @@
 
             import androidx.work.ListenableWorker
 
-            class TestWorker: ListenableWorker() {
-
-            }
+            class TestWorker: ListenableWorker()
             """
         ).indented().within("src")
-
+        /* ktlint-disable max-line-length */
         val snippet = kotlin(
             "com/example/Test.kt",
             """
@@ -146,14 +185,12 @@
 
             class Test {
                 fun enqueue() {
-                    val worker = TestWorker()
-                    val builder = PeriodicWorkRequest.Builder(worker, Duration.ofNanos(15))
+                    val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, Duration.ofSeconds(10L))
                 }
             }
             """
         ).indented().within("src")
 
-        /* ktlint-disable max-line-length */
         lint().files(
             LISTENABLE_WORKER,
             PERIODIC_WORK_REQUEST,
@@ -163,9 +200,9 @@
             .run()
             .expect(
                 """
-                src/com/example/Test.kt:10: Error: Interval duration for `PeriodicWorkRequest`s must be at least 15 minutes. [InvalidPeriodicWorkRequestInterval]
-                        val builder = PeriodicWorkRequest.Builder(worker, Duration.ofNanos(15))
-                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                src/com/example/Test.kt:9: Error: Interval duration for `PeriodicWorkRequest`s must be at least 15 minutes. [InvalidPeriodicWorkRequestInterval]
+                        val builder = PeriodicWorkRequest.Builder(TestWorker::class.java, Duration.ofSeconds(10L))
+                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                 1 errors, 0 warnings
                 """.trimIndent()
             )
diff --git a/work/work-lint/src/test/java/androidx/work/lint/SpecifyForegroundServiceTypeIssueDetectorTest.kt b/work/work-lint/src/test/java/androidx/work/lint/SpecifyForegroundServiceTypeIssueDetectorTest.kt
index b2a8fd2..fcc25223 100644
--- a/work/work-lint/src/test/java/androidx/work/lint/SpecifyForegroundServiceTypeIssueDetectorTest.kt
+++ b/work/work-lint/src/test/java/androidx/work/lint/SpecifyForegroundServiceTypeIssueDetectorTest.kt
@@ -21,11 +21,9 @@
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest.manifest
 import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
-import org.junit.Ignore
 import org.junit.Test
 
 class SpecifyForegroundServiceTypeIssueDetectorTest {
-    @Ignore("b/196831196")
     @Test
     fun failWhenServiceTypeIsNotSpecified() {
         val application = kotlin(
@@ -64,7 +62,6 @@
         /* ktlint-enable max-line-length */
     }
 
-    @Ignore("b/196831196")
     @Test
     fun failWhenSpecifiedServiceTypeIsInSufficient() {
         val manifest = manifest(
diff --git a/work/work-lint/src/test/java/androidx/work/lint/Stubs.kt b/work/work-lint/src/test/java/androidx/work/lint/Stubs.kt
index 45855a3..904f95b 100644
--- a/work/work-lint/src/test/java/androidx/work/lint/Stubs.kt
+++ b/work/work-lint/src/test/java/androidx/work/lint/Stubs.kt
@@ -110,36 +110,40 @@
         """
     ).indented().within("src")
 
-    val PERIODIC_WORK_REQUEST: TestFile = java(
-        "androidx/work/PeriodicWorkRequest.java",
+    val PERIODIC_WORK_REQUEST: TestFile = kotlin(
+        "androidx/work/PeriodicWorkRequest.kt",
         """
-            package androidx.work;
+            package androidx.work
 
-            import androidx.work.ListenableWorker;
-            import java.time.Duration;
-            import java.util.concurrent.TimeUnit;
+            import androidx.work.ListenableWorker
+            import java.time.Duration
+            import java.util.concurrent.TimeUnit
 
-            class PeriodicWorkRequest extends WorkRequest {
-                static class Builder {
-                    public Builder(ListenableWorker worker, long interval, TimeUnit unit) {
-                        
-                    }
-                    public Builder(ListenableWorker worker, Duration duration) {
-                        
-                    }
-                    public Builder(
-                        ListenableWorker worker,
-                        long interval, TimeUnit intervalUnit, 
-                        long flex,
-                        TimeUnit flexUnits) {
-                        
-                    }
-                    public Builder(
-                        ListenableWorker worker,
-                        Duration intervalDuration,
-                        Duration flexDuration) {
+            class PeriodicWorkRequest: WorkRequest {
+                class Builder {
+                    constructor(
+                        workerClass: Class<out ListenableWorker?>,
+                        repeatInterval: Duration
+                    )
+                    constructor(
+                        workerClass: Class<out ListenableWorker?>,
+                        repeatInterval: Long,
+                        repeatIntervalTimeUnit: TimeUnit
+                    ){}
 
-                    }
+                    constructor(
+                        workerClass: Class<out ListenableWorker?>,
+                        repeatInterval: Long,
+                        repeatIntervalTimeUnit: TimeUnit,
+                        flexInterval: Long,
+                        flexIntervalTimeUnit: TimeUnit
+                    )
+
+                    constructor(
+                        workerClass: Class<out ListenableWorker?>,
+                        repeatInterval: Duration,
+                        flexInterval: Duration
+                    )
                 }
             }
         """
@@ -184,17 +188,18 @@
         """
     ).indented().within("src")
 
-    val FOREGROUND_INFO: TestFile = kotlin(
-        "androidx/work/ForegroundInfo.kt",
+    val FOREGROUND_INFO: TestFile = java(
+        "androidx/work/ForegroundInfo.java",
         """
-            package androidx.work
+            package androidx.work;
 
-            import android.app.Notification
+            import android.app.Notification;
 
-            class ForegroundInfo(id: Int, notification: Notification, serviceType: Int) {
-                constructor(id: Int, notification: Notification) {
-                   this(id, notification, 0)
-                }
+            public class ForegroundInfo {
+                 public ForegroundInfo(
+                    int notificationId,
+                    Notification notification,
+                    int foregroundServiceType) { }
             }
         """
     ).indented().within("src")
diff --git a/work/work-runtime/src/main/java/androidx/work/ForegroundInfo.java b/work/work-runtime/src/main/java/androidx/work/ForegroundInfo.java
index 2e2b727..b77c79e 100644
--- a/work/work-runtime/src/main/java/androidx/work/ForegroundInfo.java
+++ b/work/work-runtime/src/main/java/androidx/work/ForegroundInfo.java
@@ -24,6 +24,8 @@
  * The information required when a {@link ListenableWorker} runs in the context of a foreground
  * service.
  */
+// NOTE: once this file is migrated to Kotlin, corresponding stub in lint rules should be migrated.
+// As a result lint checks should start relying on parameter names instead.
 public final class ForegroundInfo {
 
     private final int mNotificationId;