Testar links de aplicativos

Ao implementar o recurso de vinculação de apps, teste essa funcionalidade para verificar se o sistema consegue associar seu app aos sites e processar solicitações de URL conforme esperado.

Para testar um arquivo de instrução já existente, use a ferramenta Statement List Generator and Tester.

As seções a seguir descrevem como testar a verificação de links do app manualmente. Se preferir, você pode testar a verificação na ferramenta Play Deep Links ou no assistente de Links do app do Android Studio.

Confirmar a lista de hosts a serem verificados

Durante os testes, confirme a lista de hosts associados que o sistema precisa verificar para o app. Faça uma lista de todas os URLs da Web com filtros de intent que incluem os seguintes atributos e elementos:

  • Atributo android:scheme com um valor de http ou https
  • Atributo android:host com um padrão do URL de domínio
  • Elemento de ação android.intent.action.VIEW
  • Elemento de categoria android.intent.category.BROWSABLE

Use essa lista para verificar se um arquivo JSON Digital Asset Links foi disponibilizado para cada host e subdomínio nomeado.

Confirmar os arquivos Digital Asset Links

Para cada site, use a API Digital Asset Links para confirmar se o arquivo JSON Digital Asset Links está hospedado e definido corretamente:

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

Para links dinâmicos do app, também é possível verificar as extensões de relação.

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 do processo de testes, é possível verificar as definições atuais do sistema para o processamento de links. Use o seguinte comando para ver uma lista das políticas de processamento de links já existentes para todos os apps no dispositivo conectado:

adb shell dumpsys package domain-preferred-apps

O comando a seguir faz a mesma coisa:

adb shell dumpsys package d

O comando retorna uma lista de cada usuário ou perfil definido no dispositivo, precedida por um cabeçalho no seguinte formato:

App linkages for user 0:

Depois desse cabeçalho, o resultado usa o seguinte formato para listar as configurações de processamento de links para esse usuário:

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

A lista indica quais apps foram associados a cada domínio para o usuário:

  • Package - identifica um app pelo nome do pacote, como declarado no manifesto.
  • Domains - mostra a lista completa de hosts cujos links da Web são processados pelo app, usando espaços em branco como delimitadores.
  • Status - Mostra a configuração atual de processamento de links para esse app. Um app que foi aprovado na verificação e cujo manifesto contém android:autoVerify="true", mostra o status always. O número hexadecimal depois desse status refere-se ao registro do sistema Android das preferências de vinculação de app do usuário. Esse valor não indica se a verificação foi bem-sucedida.

Exemplo de teste

Para que a verificação de vinculação de app seja realizada corretamente, o sistema precisa verificar seu app com todos os sites especificados por você em um determinado filtro de intent que atenda aos critérios de vinculação de app. O exemplo a seguir mostra uma configuração de manifesto com vários links de app 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>

A lista de hosts que a plataforma tentaria verificar do manifesto anterior é:

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

A lista de hosts que a plataforma não tentaria verificar do manifesto 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 saber mais sobre listas de instruções, consulte Criar uma lista de instruções.

No Android 17 e versões mais recentes, é possível usar a flag --debug-link com o comando do gerenciador de atividades (am start) para diagnosticar como o sistema resolve um URL específico. Essa ferramenta fornece um detalhamento detalhado dos apps candidatos que corresponderam à intent, juntamente com as regras específicas do manifesto do app e do arquivo assetlinks.json (para links dinâmicos do app) que foram avaliadas durante a resolução.

Para testar a resolução de links de um URL específico, execute o seguinte comando em uma janela de terminal:

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

A saída de diagnóstico é impressa no cabeçalho App Link Resolution Debug e contém as seções a seguir para ajudar você a entender o processo de resolução:

  • Detalhes do destino:identifica cada app candidato correspondente pelo nome do pacote e pela atividade de destino.
  • Correspondência do filtro de intent (AndroidManifest.xml): mostra quais atributos estáticos no filtro de intent do manifesto (como scheme, host, path, pathPrefix ou pathPattern) corresponderam ao URI.
  • Verificação de links do app:mostra o estado atual da verificação de domínio (como STATE_SUCCESS).
  • Links dinâmicos do app:se o app usa regras de correspondência de links dinâmicos no arquivo assetlinks.json, esta seção lista todas as regras que foram avaliadas em relação ao URI. Cada regra indica os filtros de URI correspondentes (como prefixos ou padrões de caminho) e um campo allow:
    • allow = 0: uma regra de permissão/inclusão (allow: true). Se essa regra corresponder, o app poderá abrir o URI.
    • allow = 1: uma regra de bloqueio/exclusão (allow: false / exclude: true). Se essa regra corresponder, o app não poderá abrir o URI.
    • Observação: uma string de filtro vazia (filter =) indica um prefixo de caminho vazio que corresponde a todos os caminhos no domínio (atuando como um caractere curinga ou catch-all).

Exemplo de saída de depuração

Considere um app (com.example.xyzapp) associado ao domínio https://xyz.com que define regras dinâmicas no arquivo assetlinks.json para excluir /foo* e permitir todos os outros caminhos:

[
  {
    "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},
          {"/": "*"}
        ]
      }
    }
  }
]

Ao diagnosticar o URL https://xyz.com/foo usando --debug-link:

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

O comando gera a seguinte análise 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 }

Neste exemplo, o sistema avaliou as duas regras de links dinâmicos do app de assetlinks.json:

  • Regra 0 (allow = 1, filter = /foo): gerada de {"/": "/foo*", "exclude": true}, essa é uma regra de exclusão (allow: false) que bloqueia URLs que começam com o prefixo de caminho /foo.
  • Regra 1 (allow = 0, filter =): gerada de {"/": "*"}, essa é uma regra de inclusão (allow: true) com um prefixo de caminho vazio (filter =), que corresponde a todos os caminhos em xyz.com (catch-all).

Como a resolução funciona nesse cenário:

  1. A regra 0 e a regra 1 correspondem ao URL https://xyz.com/foo.
  2. As regras de links dinâmicos do app são avaliadas em ordem sequencial, de cima para baixo (a primeira regra correspondente vence).
  3. Como a regra 0 aparece primeiro na lista de instruções e é uma regra de exclusão (allow = 1), ela tem precedência sobre a regra de permissão geral (regra 1).
  4. Portanto, o app é excluído do processamento de https://xyz.com/foo, fazendo com que o sistema volte ao navegador ou mostre uma caixa de diálogo de desambiguação.