blob: d1c74a68b797a2070c6db4b035bbc9b97c8e5f8c [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.connecteddevice.api
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.os.ParcelUuid
import androidx.annotation.IntDef
import com.google.android.connecteddevice.model.AssociatedDevice
import com.google.android.connecteddevice.model.ConnectedDevice
import com.google.common.util.concurrent.ListenableFuture
import java.util.UUID
/** Class for establishing and maintaining a connection to the companion device platform. */
interface Connector {
/**
* Optional feature id for sending and receiving messages. Connectors initialized without a
* [featureId] will not be registered for device callbacks and will not be able to send or receive
* messages.
*/
var featureId: ParcelUuid?
/** [Callback] for connection events. */
var callback: Callback?
/** List of the currently connected devices. */
val connectedDevices: List<ConnectedDevice>
/** Whether this [Connector] is currently connected and ready for interaction. */
val isConnected: Boolean
/** Establishes a connection to the companion platform. */
fun connect()
/** Disconnects from the companion platform. */
fun disconnect()
/** Returns the backing [IBinder] of the bound service for the given [action]. */
fun binderForAction(action: String): IBinder?
/** Securely sends message to a device. */
fun sendMessageSecurely(deviceId: String, message: ByteArray)
/** Securely sends message to a device. */
fun sendMessageSecurely(device: ConnectedDevice, message: ByteArray)
/** Returns whether this feature is supported by the [device]. */
suspend fun isFeatureSupported(device: ConnectedDevice): Boolean
/**
* Checks whether this feature is supported by the [device].
*
* This method is added for java-compatibility; prefer the suspend version in Kotlin.
*/
fun isFeatureSupportedFuture(device: ConnectedDevice): ListenableFuture<Boolean>
/**
* Batch queries whether the listed features are supported by the [device].
*
* Returns an empty list if there was any error during the query.
*
* Use [isFeatureSupported] instead. This API is intended for the companion platform
* functionality.
*/
suspend fun queryFeatureSupportStatuses(
device: ConnectedDevice,
queriedFeatures: List<UUID>
): List<Pair<UUID, Boolean>>
/** Securely send a query to a device and registers a [QueryCallback] for a response. */
fun sendQuerySecurely(
deviceId: String,
request: ByteArray,
parameters: ByteArray?,
callback: QueryCallback
)
/** Securely sends a query to a device and registers a [QueryCallback] for a response. */
fun sendQuerySecurely(
device: ConnectedDevice,
request: ByteArray,
parameters: ByteArray?,
callback: QueryCallback
)
/** Sends a secure response to a query with an indication of whether it was successful. */
fun respondToQuerySecurely(
device: ConnectedDevice,
queryId: Int,
success: Boolean,
response: ByteArray?
)
/**
* Returns the [ConnectedDevice] with a matching device id for the currently active user. Returns
* `null` if no match found.
*/
fun getConnectedDeviceById(deviceId: String): ConnectedDevice?
/** Queries the [ConnectedDevice] for its companion application name. */
fun retrieveCompanionApplicationName(device: ConnectedDevice, callback: AppNameCallback)
/**
* Starts the association with a new device with a [callback] to be notified for association
* events.
*/
fun startAssociation(callback: IAssociationCallback)
/**
* Starts the association with a new device using the specified [identifier] with a [callback] to
* be notified for association events.
*/
fun startAssociation(identifier: ParcelUuid, callback: IAssociationCallback)
/** Stops the association process. */
fun stopAssociation()
/** Confirms the pairing code. */
fun acceptVerification()
/** Remove the associated device of the given [deviceId]. */
fun removeAssociatedDevice(deviceId: String)
/** Enable connection on the associated device with the given [deviceId]. */
fun enableAssociatedDeviceConnection(deviceId: String)
/** Disable connection on the associated device with the given [deviceId]. */
fun disableAssociatedDeviceConnection(deviceId: String)
/**
* Retrieves all associated devices with a [listener] that will be notified when the associated
* devices are retrieved.
*/
fun retrieveAssociatedDevices(listener: IOnAssociatedDevicesRetrievedListener)
/**
* Retrieves the associated devices belonging to the current driver with a [listener] that will be
* notified when the associated devices are retrieved.
*/
fun retrieveAssociatedDevicesForDriver(listener: IOnAssociatedDevicesRetrievedListener)
/**
* Retrieves all associated devices belonging to all passengers with a [listener] that will be
* notified when the associated devices are retrieved.
*/
fun retrieveAssociatedDevicesForPassengers(listener: IOnAssociatedDevicesRetrievedListener)
/** Claim an associated device as belonging to the current user. */
fun claimAssociatedDevice(deviceId: String)
/** Remove the claim on the identified associated device. */
fun removeAssociatedDeviceClaim(deviceId: String)
/** Callbacks invoked on connection events. */
interface Callback {
/** Invoked when a connection has been successfully established. */
fun onConnected() {}
/** Invoked when the connection to the platform has been lost. */
fun onDisconnected() {}
/** Invoked when no connection to the platform could be established. */
fun onFailedToConnect() {}
/**
* Called when a new [ConnectedDevice] is connected.
*
* This callback only indicates a plain device connection. Most features should wait for
* callback [onSecureChannelEstablished] to start a secure communication.
*/
fun onDeviceConnected(device: ConnectedDevice) {}
/** Called when a [ConnectedDevice] disconnects. */
fun onDeviceDisconnected(device: ConnectedDevice) {}
/** Called when a secure channel has been established with a [ConnectedDevice]. */
fun onSecureChannelEstablished(device: ConnectedDevice) {}
/**
* Called when a message fails to send to a device.
*
* @param deviceId Id of the device the message failed to send to.
* @param message Message to send.
* @param isTransient `true` if cause of failure is transient and can be retried. `false` if
* failure is permanent.
*/
fun onMessageFailedToSend(deviceId: String, message: ByteArray, isTransient: Boolean) {}
/** Called when a new [byte[]] message is received for this feature. */
fun onMessageReceived(device: ConnectedDevice, message: ByteArray) {}
/** Called when a new query is received for this feature. */
fun onQueryReceived(
device: ConnectedDevice,
queryId: Int,
request: ByteArray,
parameters: ByteArray?
) {}
/** Called when an error has occurred with the connection. */
fun onDeviceError(device: ConnectedDevice, error: Int) {}
/** Called when a new [AssociatedDevice] is added for the given user. */
fun onAssociatedDeviceAdded(device: AssociatedDevice) {}
/** Called when an [AssociatedDevice] is removed for the given user. */
fun onAssociatedDeviceRemoved(device: AssociatedDevice) {}
/** Called when an [AssociatedDevice] is updated for the given user. */
fun onAssociatedDeviceUpdated(device: AssociatedDevice) {}
}
/** Callback for a query response. */
interface QueryCallback {
/** Invoked with a successful response to a query. */
fun onSuccess(response: ByteArray) {}
/** Invoked with an unsuccessful response to a query. */
fun onError(response: ByteArray) {}
/**
* Invoked when a query failed to send to the device. `isTransient` is set to `true` if cause of
* failure is transient and can be retried, or `false` if failure is permanent.
*/
fun onQueryFailedToSend(isTransient: Boolean) {}
}
/** Callback for a query for the name of the companion application on the connected device. */
interface AppNameCallback {
/** Invoked with the name of the companion application on the connected device. */
fun onNameReceived(appName: String) {}
/** Invoked when the name failed to be retrieved from the connected device. */
fun onError() {}
}
companion object {
/**
* When a client calls [Context.bindService] to get the [IFeatureCoordinator], this action is
* required in the param [Intent].
*/
const val ACTION_BIND_FEATURE_COORDINATOR =
"com.google.android.connecteddevice.api.BIND_FEATURE_COORDINATOR"
/**
* When a client calls [Context.bindService] to get the [IFeatureCoordinator] from a service
* running in the foreground user. Any process that resides outside of the service host
* application must use this action in its [Intent].
*/
const val ACTION_BIND_FEATURE_COORDINATOR_FG =
"com.google.android.connecteddevice.api.BIND_FEATURE_COORDINATOR_FG"
/** Type associated with a driver's device. */
const val USER_TYPE_DRIVER = 1 shl 0
/** Type associated with a passenger's device. */
const val USER_TYPE_PASSENGER = 1 shl 1
/** Type associated with all user types. */
const val USER_TYPE_ALL = USER_TYPE_DRIVER or USER_TYPE_PASSENGER
/** Id for the system query feature. */
val SYSTEM_FEATURE_ID: ParcelUuid =
ParcelUuid.fromString("892ac5d9-e9a5-48dc-874a-c01e3cb00d5d")
/** User types that can be associated with a connected device. */
@IntDef(USER_TYPE_DRIVER, USER_TYPE_PASSENGER, USER_TYPE_ALL)
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.TYPE)
annotation class UserType
}
}