Probar vínculos de aplicaciones

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

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

En las siguientes secciones, se describe cómo probar la verificación de tus vínculos de aplicación de forma manual. Si lo prefieres, puedes probar la verificación con la herramienta de vínculos directos de Play o el asistente de Android App Links de Android Studio.

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

Cuando realices pruebas, debes confirmar la lista de hosts asociados que el sistema debe verificar para tu app. Crea una lista de todas las URLs cuyos filtros de intents correspondientes incluyan 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 verificar que se proporcione un archivo JSON de Vínculos de recursos digitales en cada host y subdominio con nombre.

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

Para cada sitio web, usa la API de Vínculos de recursos digitales para confirmar que el archivo JSON de Vínculos de recursos digitales esté 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 Dynamic App Links, también puedes verificar 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 verificar la configuración actual del sistema para el manejo de vínculos. Usa el siguiente comando para obtener una lista de las políticas de manejo 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 devuelve una lista de cada usuario o perfil definido en el dispositivo, precedido por un encabezado con el siguiente formato:

App linkages for user 0:

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

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

En esta lista, se indica qué apps están asociadas con qué dominios para ese usuario:

  • Package: Identifica una app por su nombre de paquete, como se declara en su manifiesto.
  • Domains: Muestra la lista completa de hosts cuyos vínculos web controla esta app, con espacios en blanco como delimitadores.
  • Status: Muestra el parámetro de 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 el estado always. El número hexadecimal que aparece después de este estado se relaciona con el registro del sistema Android de las preferencias de vinculación de apps del usuario. Este valor no indica si la verificación se realizó correctamente.

Ejemplo de comprobación

Para que la verificación de vínculos de apps se realice correctamente, 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 de los vínculos de apps. En el siguiente ejemplo, se muestra una configuración de manifiesto con varios vínculos de aplicación 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 lista de hosts que la plataforma intentaría verificar a partir del manifiesto anterior es la siguiente:

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

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

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 el intent, junto con las reglas específicas del manifiesto de la app y el archivo assetlinks.json (para los vínculos dinámicos a 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 objetivo: Identifica cada app candidata que coincide por su nombre de paquete y actividad objetivo.
  • Coincidencia de filtro de intents (AndroidManifest.xml): Muestra qué atributos estáticos del filtro de intents del manifiesto (como scheme, host, path, pathPrefix o pathPattern) coincidieron con el URI.
  • Verificación de vínculos de aplicación: Muestra el estado actual de la verificación del dominio (como STATE_SUCCESS).
  • Dynamic App Links: Si la app usa reglas de coincidencia de Dynamic App Links en su archivo assetlinks.json, en esta sección, se enumeran todas las reglas que se evaluaron con respecto al URI. Cada regla indica los filtros de URI que coinciden (como prefijos o patrones de ruta de acceso) y un campo allow:
    • allow = 0: Es una regla de permiso o 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 o 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 de acceso vacío que coincide con todas las rutas de acceso del dominio (actúa como comodín o comodín).

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, al mismo tiempo, 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): Se genera 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 =): Se genera 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 (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 Dynamic App Link se evalúan en orden secuencial de arriba abajo (se aplica la primera regla que coincide).
  3. Como la Regla 0 aparece primero en la lista de instrucciones y es una regla de exclusión (allow = 1), tiene prioridad sobre la regla de permiso general (Regla 1).
  4. Por lo tanto, la app se excluye de controlar https://xyz.com/foo, lo que hace que el sistema recurra al navegador o muestre un diálogo de desambiguación.