API de Permissions en Android

Antes de usar cualquiera de las APIs de Home para Android, la app debe tener permiso para acceder a los dispositivos de la casa del usuario, que en la API se denomina la estructura. Con la API de Permissions, el usuario puede, con su Cuenta de Google, otorgar acceso a las apps de las APIs de Home a los dispositivos de su casa.

El flujo de permisos permite que el usuario cree una estructura y asigne su dirección, si aún no se configuró una, sin tener que usar la Google Home app (GHA).

Integra la API de Permissions

Antes de continuar, asegúrate de haber seguido los pasos que se indican en Inicializa la casa en Android. La instancia homeManager de ese paso se usa en todos los ejemplos de permisos que se muestran aquí.

Primero, registra un ActivityResultCaller con el SDK. Por ejemplo, así es como la app de ejemplo lo controla:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    homeManager.registerActivityResultCallerForPermissions(this)
  }

Verifica si tu app tiene permisos

Antes de solicitar permisos, te recomendamos que verifiques si el usuario de la app ya otorgó el consentimiento para acceder a la estructura. Para ello, llama al método hasPermissions() de la instancia de Home para obtener un Flow de valores PermissionsState:

val permissionsReadyState =
homeManager.hasPermissions().collect { state ->
    when (state) {
      PermissionsState.GRANTED -> println("Permissions granted, no need to request permissions")
      PermissionsState.PERMISSIONS_STATE_UNAVAILABLE ->
          println("Permissions state unavailable, request permissions")

      PermissionsState.NOT_GRANTED ->
          println("OAuth permission is enabled but not granted yet, request permissions")

      PermissionsState.PERMISSIONS_STATE_UNINITIALIZED -> println(
          "Permissions state is not initialized yet. Clients should wait for another status update"
      )
      else ->
          throw IllegalStateException("""
            HomeClient.hasPermissions state should be PermissionsState.GRANTED,
            PermissionState.PERMISSIONS_STATE_UNINITIALIZED, or
            PermissionsState.PERMISSIONS_STATE_UNAVAILABLE. Actual state: $state
          """.trimIndent())
    }
}

Si la verificación devuelve un PermissionsState de NOT_GRANTED o PERMISSIONS_STATE_UNAVAILABLE, significa que el usuario o la aplicación no tienen acceso a la estructura. Si la verificación devuelve un PermissionsState de GRANTED, pero una llamada posterior a structures() no devuelve estructuras, significa que el usuario revocó el acceso a la app a través de la página de configuración de GHA o que el usuario no tiene el acceso requerido.

Solicitar permisos

Se debe otorgar permiso a tu app para acceder a las estructuras y los dispositivos dentro de una estructura determinada.

Si el usuario aún no otorgó permisos, usa el método requestPermissions() de la instancia de Home para iniciar la IU de permisos y procesar el resultado:

fun requestPermissions(scope: CoroutineScope, onShowSnackbar: (String) -> Unit) {
  scope.launch {
    val result =
      try {
        homeManager.requestPermissions()
      } catch (e: HomeException) {
        PermissionsResult(
          PermissionsResultStatus.ERROR,
          "Got HomeException with error: ${e.message}",
        )
      }
    when (result.status) {
      PermissionsResultStatus.SUCCESS -> {
        Log.i(TAG, "Permissions successfully granted.")
      }
      PermissionsResultStatus.CANCELLED -> {
        Log.i(TAG, "User cancelled Permissions flow.")
        onShowSnackbar("User cancelled Permissions flow")
      }
      else -> {
        Log.e(
          TAG,
          "Failed to grant permissions with error: ${result.status}, ${result.errorMessage}",
        )
        onShowSnackbar("Failed to grant permissions with error: ${result.errorMessage}")
      }
    }
  }
}

Para que la IU de permisos se inicie correctamente, ya debes haber configurado OAuth para tu app.

La IU de consentimiento de permisos se renderiza en la configuración regional establecida con HomeClient.setLocale() o, de forma predeterminada, en la configuración regional de la app o del dispositivo si no se establece. Para mostrar la pantalla de consentimiento en un idioma específico, llama a setLocale() en tu instancia de HomeClient antes de llamar a requestPermissions(). Para obtener más información, consulta Cómo configurar la configuración regional de la app.

Otorga permisos

Ahora deberías poder ejecutar tu app y hacer que un usuario otorgue permisos. El tipo de usuarios que pueden otorgar permisos y los tipos de dispositivos que están disponibles para otorgar permisos variarán según si registraste tu app en la Google Home Developer Console.

Se requiere el registro de Developer Console para publicar una app que use las APIs de Home. No es necesario para probar y usar las APIs de Home.

Si una app no está registrada en Developer Console, estará en un estado no verificado. Se recomienda para probar el uso de las APIs de Home:

  • Solo los usuarios registrados como usuarios de prueba en la consola de OAuth pueden otorgar permisos para la app. Hay un límite de 100 usuarios de prueba para una app no verificada.

  • Una app no verificada tendrá acceso a dispositivos de cualquier tipo que sea compatible con OAuth para las APIs de Home (la lista de tipos de dispositivos en el Developer Console). Se otorgarán todos los dispositivos de una estructura.

Si una app está registrada en la Developer Console y se aprobó para acceder a uno o más tipos de dispositivos, y se completó la verificación de la marca para OAuth, estará en un estado verificado. Este estado es obligatorio para lanzar una app a producción:

  • Ya no se aplican los límites de usuarios de prueba. Cualquier usuario puede otorgar permiso a la app.
  • El usuario solo puede otorgar permiso a los tipos de dispositivos que se aprobaron en la Developer Console.

Ahora que OAuth está configurado, la llamada de la app a requestPermissions() activa los siguientes diálogos:

  1. Se le solicita al usuario que seleccione la Cuenta de Google que desea usar.
  2. Se le solicita al usuario que seleccione la estructura a la que quiere otorgarle acceso a la app.
    1. En el caso de una app no verificada, todos los tipos de dispositivos compatibles con las APIs de Home están disponibles para la app.
    2. En el caso de una app verificada, el usuario puede otorgar permiso solo a los tipos de dispositivos que se aprobaron en Developer Console.
    3. En el caso de los tipos de dispositivos sensibles a los que la app tiene acceso para administrar, el usuario puede restringir el acceso por dispositivo. Por ejemplo, si un usuario tiene tres cerraduras, puede otorgar acceso solo a una de ellas.
  • Consentimiento de OAuth: Selecciona una cuenta
  • Consentimiento de OAuth: Vincula dispositivos 01
  • Consentimiento de OAuth: Vincula el dispositivo 02
Figura 1: Ejemplo de flujo de consentimiento de OAuth

Una vez que se otorga el permiso, la app puede usar las APIs de Home para leer el estado de los dispositivos de la estructura y controlarlos. Si el usuario no otorga permiso a la app para un tipo de dispositivo o dispositivo sensible en particular, la app no podrá usar las APIs de Home para acceder a él, controlarlo ni automatizarlo.

Cambiar permisos

Para otorgar permiso de acceso a los dispositivos de una estructura diferente, se puede iniciar el selector de cuentas para que el usuario elija la Cuenta de Google y la estructura a la que desea cambiar. Durante este proceso, se volverá a mostrar la pantalla de consentimiento al usuario, incluso si ya lo otorgó.

Para ello, vuelve a llamar a requestPermissions() con forcePermissionFlow configurado como ForcePermissionFlow.FORCE_LAUNCH:

homeManager.requestPermissions(forcePermissionFlow = ForcePermissionFlow.FORCE_LAUNCH)

Cambia los permisos con la preselección de estructura

La preselección de estructura permite que una app preseleccione una estructura específica o restrinja la lista de estructuras disponibles cuando se solicita un cambio incremental en los permisos de las APIs de Home del usuario. Si pasas parámetros de estructura a la solicitud de autorización, el diálogo de permisos se enfocará automáticamente en la estructura elegida, lo que reducirá la fricción del usuario y evitará errores de configuración.

La preselección de estructura se administra con la clase de datos ConsentScreenOptions. La clase ConsentScreenOptions acepta los siguientes parámetros de configuración:

  • structureId: Es el ID de la estructura específica que se preseleccionará en el diálogo de permisos. Para obtenerlo, consulta las propiedades de la estructura que se está actualizando.
  • allowedStructureIds: Es una lista de IDs de estructura. Si se proporciona, el diálogo de permiso filtrará las estructuras disponibles para mostrar solo las que se encuentran en esta lista. En la mayoría de los casos, esto se puede dejar sin especificar, a menos que quieras asegurarte de que el usuario permanezca dentro de una lista de estructuras ya otorgada.
  • allowStructureChange: Determina si el usuario tiene permiso para cambiar la estructura preseleccionada. Establece este valor en true si se especifica al menos uno de los valores allowedStructureIds y structureId en la mayoría de los casos para admitir el comportamiento natural del usuario.

Pasa este objeto como un parámetro opcional en la llamada a requestPermissions(), con forcePermissionFlow establecido en ForcePermissionFlow.FORCE_LAUNCH:

import com.google.home.ConsentScreenOptions

// Create the ConsentScreenOptions class, allowing structure changes while
// ensuring the permissions dialog pre-selects the target structure on launch
val consentOptions = ConsentScreenOptions(
    structureId = target-structure-id,
    allowStructureChange = true
)

homeManager.requestPermissions(forcePermissionFlow = ForcePermissionFlow.FORCE_LAUNCH, consentScreenOptions = consentOptions)

Al usuario se le presentará la pantalla de consentimiento ya filtrada para la estructura indicada en el objeto ConsentScreenOptions.

Permite que el usuario cambie de estructura con la preselección de estructura

Si un usuario tiene varias estructuras en una app y quieres preseleccionar una, pero permitirle cambiar entre las estructuras disponibles, habilita los cambios de estructura con la marca allowStructureChange y proporciona una lista de estructuras en allowedStructureIds:

val consentOptions = ConsentScreenOptions(
    structureId = target-structure-id,
    allowedStructureIds = listOf(target-structure-id, another-structure-id),
    allowStructureChange = true
)

Revoca permisos

Los usuarios pueden revocar el acceso otorgado anteriormente:

  1. A través de la configuración de la Cuenta de Google > Datos y privacidad > Apps y servicios de terceros. Esto revocará el token de OAuth que se emitió cuando se otorgó el consentimiento inicial y revocará el acceso a cualquier instancia de la app que el usuario haya estado usando en todas las plataformas (teléfonos) y estructuras.

    Se puede dirigir al usuario con un vínculo directo a la subpágina Apps y servicios de terceros con el siguiente esquema de URL:

    https://myaccount.google.com/connections/link?project_number=Cloud project_number
    
  2. A través de la página GHA > Configuración > Apps vinculadas. Si haces clic en en el GHA, se te dirigirá a la página Configuración. Desde allí, haz clic en la tarjeta Apps vinculadas, que te llevará a una página similar a la pantalla de consentimiento. En esta página, el usuario puede quitar el acceso a la app. También puede usar esta misma página para cambiar los tipos de dispositivos o los dispositivos sensibles específicos a los que puede acceder la app.

Eliminación y exportación de datos del usuario

La plataforma administra automáticamente la eliminación de datos del backend cuando un usuario borra su Cuenta de Google o abandona una estructura:

  • Si el usuario borrado es el único propietario de una estructura, la plataforma borra la estructura y sus asociaciones de dispositivos.
  • Si quedan otros propietarios en una estructura compartida, la estructura y sus dispositivos compartidos persistirán para los miembros restantes. La plataforma quita la membresía del usuario que se fue y desvincula los dispositivos de terceros de Cloud-to-cloud que se conectaron solo a través de las credenciales de ese usuario.
  • La eliminación de la cuenta quita de inmediato y de forma permanente los datos de la estructura y los permisos de la aplicación del usuario. Incluso si el usuario recupera su Cuenta de Google más adelante, no se restablecerán sus estructuras ni sus permisos.

Las APIs de Home no proporcionan extremos para exportar o borrar los datos de la Cuenta de Google de un usuario. Dirige a los usuarios que tengan una Cuenta de Google a Google Takeout para exportar sus datos de Google Home y a la configuración de su Cuenta de Google para administrar la eliminación de la cuenta.

En el ecosistema de Google Home, para la mayoría de los tipos de dispositivos, los usuarios pueden otorgar permisos para todos los dispositivos de ese tipo a la vez. En el caso de los tipos de dispositivos sensibles o restringidos, como cerraduras, cámaras o timbres, los usuarios deben otorgarles permiso de forma individual.

Para determinar si un usuario otorgó permiso para acceder a un tipo de dispositivo sensible o restringido, usa la función consentedDeviceTypes() a nivel de la estructura:

import com.google.home.Structure
import com.google.home.DeviceType
import com.google.home.DeviceTypeFactory
import com.google.home.consentedDeviceTypes // Extension function from the SDK
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/**
 * Example of how an app may monitor which device types have been granted access by a user.
 */
fun monitorDeviceConsent(structure: Structure, myScope: CoroutineScope) {
    // Obtain the flow of consented device type factories
    val consentedTypesFlow: Flow<Set<DeviceTypeFactory<out DeviceType>>> =
        structure.consentedDeviceTypes()

    myScope.launch {
        consentedTypesFlow.collect { consentedSet ->
            // Check if the user has consented to share a specific restricted
            // type, such as a Doorbell or Camera.
            val hasCameraAccess = consentedSet.any {
                it.toString() == "matter.google.type.GoogleDoorbellDevice"
            }

            if (hasCameraAccess) {
                // Enable features that require camera access
            } else {
                // Inform the user or disable camera-specific features
            }
        }
    }
}

Permisos de OkGoogle

El comando okGoogle es un comando a nivel del dispositivo y se puede usar para automatizar cualquier dispositivo de la estructura. Sin embargo, es posible que una app de las APIs de Home no tenga acceso a todos los dispositivos. En la siguiente tabla, se describe cómo se aplican los permisos en esos casos.

Automatización Rasgo Aplicación de permisos
A las 10:00 p.m., anuncia "Hora de dormir" en la bocina de la habitación. AssistantBroadcastTrait en el dispositivo. Creación de automatizaciones:
  • El dispositivo de transmisión debe ser un dispositivo con el Asistente integrado.
  • La app y el usuario deben tener acceso al dispositivo en el que se produce la transmisión.
Ejecución de la automatización:
  • La app y el usuario deben tener acceso al dispositivo en el que se produce la transmisión.
A las 10:00 p.m., anuncia "Hora de dormir" en todos los dispositivos AssistantBroadcastTrait sobre la estructura Creación de automatizaciones:
  • Debe haber al menos un dispositivo con el Asistente integrado en la estructura a la que la app y el usuario tengan acceso.
  • La app y el usuario deben tener acceso a la estructura.
Ejecución de la automatización:
  • La app y el usuario deben tener acceso a la estructura.
A las 10:00 p.m., "reproduce música" AssistantFulfillmentTrait.OkGoogleCommand Creación de automatizaciones:
  • La app y el usuario deben tener acceso a los dispositivos a los que la automatización emite comandos.
Ejecución de la automatización:
  • La app y el usuario deben tener acceso a los dispositivos a los que la automatización emite comandos.
Cada vez que alguien diga "reproduce música" VoiceStarterTrait.OkGoogleEvent Creación de automatizaciones:
  • La app y el usuario deben tener acceso a la estructura. Una automatización no requiere un dispositivo con el Asistente integrado para pasar la validación o ejecutarse, ya que cualquier usuario con acceso a la estructura puede usar su teléfono (con la misma Cuenta de Google) para interactuar con el Asistente y activar el VoiceStarter.
Ejecución de la automatización:
  • La app no requiere permiso para acceder al dispositivo que inicia la automatización.
  • La app y el usuario deben tener permiso para acceder al dispositivo en el que se produce la acción.

Orientación si el usuario revoca los permisos completos

Si el usuario revoca los permisos completos, todas las automatizaciones existentes dejarán de funcionar. Además, si el usuario revoca el acceso a dispositivos específicos, los activadores, las condiciones y las acciones asociados a esos dispositivos dejarán de funcionar.

Cada vez que se inicie la app, asegúrate de verificar que los permisos sigan vigentes. Si se revocaron, asegúrate de que se quiten todos los datos anteriores, incluidos los datos almacenados en caché en la aplicación.

Cuando se revoca el acceso a la estructura, se entrega un StructureAccessRevokedEvent a tu backend de la nube. Consulta Permisos de estructura para conocer el flujo de trabajo de revocación de extremo a extremo de la app para dispositivos móviles y la nube del socio.