Unir script de shell

Ao depurar e criar perfis de apps com código nativo, geralmente é útil usar ferramentas de depuração que precisem ser ativadas na inicialização do processo. Isso requer que você execute o app em um processo novo em vez de clonar a partir do zigoto. Por exemplo:

Usar o script de shell de agrupamento

Usar wrap.sh é fácil:

  1. Compile um APK depurável personalizado que empacote os seguintes itens:
  2. Instale o APK depurável em um dispositivo.
  3. Inicie o app.

Criar o script de shell de agrupamento

Quando você inicia um APK depurável que contém wrap.sh, o sistema executa o script e transmite o comando para iniciar o app como argumentos. O script é responsável por iniciar o app, mas pode fazer com que qualquer ambiente ou argumento mude. O script precisa seguir a sintaxe MirBSD Korn shell (mksh) (link em inglês).

O snippet a seguir mostra como escrever um arquivo wrap.sh simples que só inicia o app:

#!/system/bin/sh
exec "$@"

Depuração Malloc

Para usar a depuração malloc (link em inglês) via wrap.sh, você precisa incluir a seguinte linha:

#!/system/bin/sh
LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"

ASan

Há um exemplo de como fazer isso na documentação do ASan.

Pacote wrap.sh

Para aproveitar wrap.sh, o APK precisa ser depurável. A configuração android:debuggable="true" precisa estar definida no elemento <application> no manifesto do Android ou, se você está usando o Android Studio, um build de depuração precisa estar configurado no arquivo build.gradle.

Também é necessário definir useLegacyPackaging como true no arquivo build.gradle do app. Na maioria dos casos, essa opção é definida como false por padrão. Portanto, defina-a explicitamente como true para evitar surpresas.

É preciso empacotar o script de wrap.sh com as bibliotecas nativas do app. Se o app não tiver bibliotecas nativas, adicione o diretório lib manualmente ao diretório do projeto. Para cada arquitetura compatível com o app, você precisa fornecer uma cópia do script de shell de agrupamento nesse diretório da biblioteca nativa.

O exemplo a seguir mostra o layout do arquivo para que seja compatível com arquiteturas ARMv8 e x86-64:

# App Directory
|- AndroidManifest.xml
|- …
|- lib
   |- arm64-v8a
      |- ...
      |- wrap.sh
   |- x86_64
      |- ...
      |- wrap.sh

O Android Studio só empacota arquivos .so dos diretórios lib/. Portanto, se você for usuário do Android Studio, precisará colocar seus arquivos wrap.sh no diretório src/main/resources/lib/* para que eles sejam empacotados corretamente.

Observe que resources/lib/x86 será exibido na IU como lib.x86, mas, na verdade, será um subdiretório:

Exemplo de empacotamento de wrap.sh no Android Studio

Depurar ao usar wrap.sh

Se você quiser anexar um depurador ao usar o wrap.sh, seu script de shell precisará ativar manualmente a depuração. Como isso varia entre as versões, este exemplo mostra como adicionar as opções apropriadas para todas as versões compatíveis com o wrap.sh:

#!/system/bin/sh

cmd=$1
shift

os_version=$(getprop ro.build.version.sdk)

if [ "$os_version" -eq "27" ]; then
  cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
elif [ "$os_version" -eq "28" ]; then
  cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
else
  cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
fi

exec $cmd