Probar vínculos de aplicaciones

Cuando implementes la función de vínculos de apps, debes probar la funcionalidad de vinculación para asegurarte de que el sistema pueda asociar tu app con tus sitios web y manejar las solicitudes de URL, tal como lo esperas.

Para probar un archivo de instrucciones existente, puedes usar la herramienta Generador y comprobador de listas de instrucciones.

En las siguientes secciones, se describe cómo probar manualmente la verificación de los vínculos de la app. Si lo prefieres, puedes probar la verificación desde la herramienta Play Deep Links o el Asistente de Android Studio App Links.

Cómo confirmar la lista de hosts que se deben verificar

Cuando realices la prueba, debes confirmar la lista de hosts asociados que el sistema debe verificar para tu app. Haz una lista de todas las URLs cuyos filtros de intents correspondientes incluyen los siguientes atributos y elementos:

  • Atributo android:scheme con un valor de http o https
  • Atributo android:host con un patrón de URL de dominio
  • Elemento de acción android.intent.action.VIEW
  • Elemento de categoría android.intent.category.BROWSABLE

Usa esta lista para comprobar que se proporcione un archivo JSON de Vínculos de recursos digitales en cada host y subdominio nombrado.

Cómo confirmar los archivos de Vínculos de recursos digitales

En cada sitio web, usa la API de Vínculos de recursos digitales para confirmar que el archivo JSON de Vínculos de recursos digitales se encuentre alojado y definido correctamente:

https://digitalassetlinks.googleapis.com/v1/statements:list?
   source.web.site=https://<var>domain.name</var>:<var>optional_port</var>&amp;
   relation=delegate_permission/common.handle_all_urls

En el caso de los vínculos de aplicaciones dinámicos, también puedes consultar las extensiones de relación.

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://www.example.com&relation=delegate_permission/common.handle_all_urls&return_relation_extensions=true

Como parte de tu proceso de prueba, puedes comprobar la configuración actual del sistema para el control de vínculos. Usa el siguiente comando para obtener una lista de las políticas de control de vínculos existentes para todas las apps de tu dispositivo conectado:

adb shell dumpsys package domain-preferred-apps

El siguiente comando hace lo mismo:

adb shell dumpsys package d

El comando muestra un listado de cada usuario o perfil definidos en el dispositivo, precedido por un encabezado en el siguiente formato:

App linkages for user 0:

Luego de este encabezado, el resultado usa el siguiente formato para enumerar la configuración de control de vínculos para ese usuario:

Package: com.android.vending
Domains: play.google.com market.android.com
Status: always : 200000002

Este listado indica qué apps están asociadas con qué dominios para ese usuario:

  • Package: Identifica una app por el nombre de su paquete, como se encuentra declarado en su manifiesto.
  • Domains: Muestra la lista completa de hosts cuyos vínculos web maneja esta app; utiliza espacios en blanco como delimitadores.
  • Status: Muestra la configuración actual de control de vínculos para esta app. Una app que aprobó la verificación y cuyo manifiesto contiene android:autoVerify="true" muestra un estado de always. El número hexadecimal que le sigue a ese estado está relacionado con el registro de las preferencias de usuario para la vinculación de apps del sistema Android. Este valor no indica si la verificación tuvo éxito.

Ejemplo de comprobación

Para que la verificación de vínculos de apps tenga éxito, el sistema debe poder verificar tu app con cada uno de los sitios web que especifiques en un filtro de intents determinado que cumpla con los criterios para los vínculos de apps. En el siguiente ejemplo, se muestra la configuración de un manifiesto con varios vínculos de apps definidos:

<activity android:name="MainActivity">
        <intent-filter android:autoVerify="true">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
            <data android:scheme="https" />
            <data android:host="www.example.com" />
            <data android:host="mobile.example.com" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
            <data android:host="www.example2.com" />
        </intent-filter>
    </activity>

    <activity android:name="SecondActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
            <data android:host="account.example.com" />
        </intent-filter>
    </activity>

      <activity android:name="ThirdActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="https" />
            <data android:host="map.example.com" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="market" />
            <data android:host="example.com" />
        </intent-filter>
      </activity>

</application>

La siguiente es la lista de hosts que la plataforma intentaría verificar a partir del manifiesto anterior:

www.example.com
mobile.example.com
www.example2.com
account.example.com

La siguiente es la lista de hosts que la plataforma no intentaría verificar a partir del manifiesto anterior:

map.example.com (it does not have android.intent.category.BROWSABLE)
market://example.com (it does not have either an "http" or "https" scheme)

Para obtener más información sobre las listas de instrucciones, consulta Cómo crear una lista de instrucciones.

A partir de Android 17, puedes usar la marca --debug-link con el comando del administrador de actividades (am start) para diagnosticar cómo el sistema resuelve una URL específica. Esta herramienta proporciona un desglose detallado de las apps candidatas que coincidieron con la intención, junto con las reglas específicas del manifiesto de la app y el archivo assetlinks.json (para los vínculos dinámicos de la app) que se evaluaron durante la resolución.

Para probar la resolución de vínculos de una URL específica, ejecuta el siguiente comando en una ventana de terminal:

adb shell am start --debug-link -a android.intent.action.VIEW -d "https://xyz.com/foo"

El resultado del diagnóstico se imprime debajo del encabezado App Link Resolution Debug y contiene las siguientes secciones para ayudarte a comprender el proceso de resolución:

  • Detalles del destino: Identifica cada app candidata coincidente por su nombre de paquete y actividad de destino.
  • Intent Filter Match (AndroidManifest.xml): Muestra qué atributos estáticos en el filtro de intents del manifiesto (como scheme, host, path, pathPrefix o pathPattern) coincidieron con el URI.
  • Verificación de App Links: Muestra el estado actual de la verificación del dominio (por ejemplo, STATE_SUCCESS).
  • Vínculos dinámicos a la aplicación: Si la app usa reglas de coincidencia de vínculos dinámicos a la aplicación en su archivo assetlinks.json, en esta sección, se enumeran todas las reglas que se evaluaron en relación con el URI. Cada regla indica los filtros de URI coincidentes (como prefijos o patrones de ruta) y un campo allow:
    • allow = 0: Es una regla de permiso/inclusión (allow: true). Si esta regla coincide, la app puede abrir el URI.
    • allow = 1: Es una regla de bloqueo o exclusión (allow: false / exclude: true). Si esta regla coincide, se impide que la app abra el URI.
    • Nota: Una cadena de filtro vacía (filter =) indica un prefijo de ruta vacío que coincide con todas las rutas del dominio (actúa como comodín o catch-all).

Ejemplo de resultado de depuración

Considera una app (com.example.xyzapp) asociada al dominio https://xyz.com que define reglas dinámicas en su archivo assetlinks.json para excluir /foo* y permitir todas las demás rutas:

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.xyzapp",
      "sha256_cert_fingerprints": ["..."]
    },
    "relation_extensions": {
      "delegate_permission/common.handle_all_urls": {
        "dynamic_app_link_components": [
          {"/": "/foo*", "exclude": true},
          {"/": "*"}
        ]
      }
    }
  }
]

Cuando diagnostiques la URL https://xyz.com/foo con --debug-link, ten en cuenta lo siguiente:

adb shell am start --debug-link -a android.intent.action.VIEW -d "https://xyz.com/foo"

El comando genera el siguiente desglose de diagnóstico:

--- App Link Resolution Debug ---

URI: https://xyz.com/foo
Resolution: Ambiguous (Multiple apps or Browser fallback)
This usually happens when multiple apps can handle the link and no default is set.

All Matching Candidates:

Target:
  Package: com.example.xyzapp
  Activity: com.example.xyzapp.MainActivity

  Intent Filter Match (AndroidManifest.xml)
    Scheme: 'https' matched android:scheme="https"
    Host: 'xyz.com' matched android:host="xyz.com"

App Link Verification:
  Verification status: STATE_SUCCESS
  Dynamic App Links:
    -> Matched Rule 0: UriRelativeFilterGroup { allow = 1, uri_filters = {UriRelativeFilter { uriPart = PATH, patternType = PREFIX, filter = /foo }},  }
    -> Matched Rule 1: UriRelativeFilterGroup { allow = 0, uri_filters = {UriRelativeFilter { uriPart = PATH, patternType = PREFIX, filter =  }},  }

Target:
  Package: org.chromium.webview_shell
  Activity: org.chromium.webview_shell.WebViewBrowserActivity

  Intent Filter Match (AndroidManifest.xml)
    Scheme: 'https' matched android:scheme="https"

---------------------------------

Starting: Intent { act=android.intent.action.VIEW dat=https://xyz.com/foo }

En este ejemplo, el sistema evaluó las dos reglas de App Link dinámicos de assetlinks.json:

  • Regla 0 (allow = 1, filter = /foo): Se genera a partir de {"/": "/foo*", "exclude": true}. Es una regla de exclusión (allow: false) que bloquea las URLs que comienzan con el prefijo de ruta de acceso /foo.
  • Regla 1 (allow = 0, filter =): Se genera a partir de {"/": "*"}. Es una regla de inclusión (allow: true) con un prefijo de ruta de acceso vacío (filter =), que coincide con todas las rutas de acceso en xyz.com (comodín).

Cómo funciona la resolución en este caso:

  1. Tanto la regla 0 como la regla 1 coinciden con la URL https://xyz.com/foo.
  2. Las reglas de App Link dinámicos se evalúan en orden secuencial de arriba a abajo (gana la primera regla que coincida).
  3. Como la regla 0 aparece primero en la lista de declaraciones y es una regla de exclusión (allow = 1), tiene prioridad sobre la regla general de permiso (regla 1).
  4. Por lo tanto, la app se excluye del control de https://xyz.com/foo, lo que hace que el sistema recurra al navegador o muestre un diálogo de desambiguación.