Probar vínculos de aplicaciones

Cuando implementas 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 URI, tal como lo quieres.

Para probar un archivo de declaración existente, puedes usar la herramienta Statement List Generator and Tester.

En las siguientes secciones, se describe cómo probar manualmente la verificación de tus App Links. Si lo prefieres, puedes probar la verificación desde la herramienta Play Deep Links o el App Links Assistant de Android Studio.

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

Cuando se realiza la prueba, debes confirmar la lista de hosts asociados que el sistema debe verificar para tu app. Haz una lista de todas las URL 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 App Links 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 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 a fin de enumerar las configuraciones 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é aplicaciones 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 se realizó correctamente.

Ejemplo de comprobación

Para que tenga éxito la verificación de vínculos de apps, 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 una configuración de 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 declaraciones, consulta Cómo crear una lista de declaraciones.

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 el intent, junto con las reglas específicas del manifiesto de la app y el archivo assetlinks.json (para los App Links dinámicos) 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 la terminal:

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

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

  • Target details: Identifica cada app candidata coincidente por su nombre de paquete y actividad de destino.
  • Coincidencia de filtro de intents (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.
  • App Link Verification: Muestra el estado actual de verificación del dominio (como STATE_SUCCESS).
  • Dynamic App Links: Si la app usa reglas de coincidencia de App Links dinámicos en su archivo assetlinks.json, esta sección muestra cada regla que se evaluó en comparación con el URI. Cada regla indica los filtros de URI coincidentes (como prefijos o patrones de ruta de acceso) y un campo allow:
    • allow = 0: Es una regla de permitir/inclusión (allow: true). Si esta regla coincide, la app puede abrir el URI.
    • allow = 1: Es una regla de bloqueo/exclusión (allow: false / exclude: true). Si esta regla coincide, la app no puede abrir el URI.
    • Nota: Una cadena de filtro vacía (filter =) indica un prefijo de ruta de acceso vacío que coincide con todas las rutas de acceso del dominio (que actúa como comodín o catch-all).

Ejemplo de resultado de depuración

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

[
  {
    "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 se diagnostica la URL https://xyz.com/foo con --debug-link, ocurre 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 Links dinámicos de assetlinks.json:

  • Regla 0 (allow = 1, filter = /foo): Generada a partir de {"/": "/foo*", "exclude": true}, esta 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 =): Generada a partir de {"/": "*"}, esta 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 (catch-all).

Cómo funciona la resolución en esta situación:

  1. La regla 0 y la regla 1 coinciden con la URL https://xyz.com/foo.
  2. Las reglas de App Links dinámicos se evalúan en orden secuencial de forma descendente (gana la primera regla coincidente).
  3. Debido a que 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 permitir (regla 1).
  4. Por lo tanto, la app se excluye del control de https://xyz.com/foo, lo que hace que el sistema vuelva al navegador o muestre un diálogo de desambiguación.