Script de shell de agrupamento

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. Alguns exemplos:

Usar o script wrap shell

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 wrap shell

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.

Empacotamento de wrap.sh

Para aproveitar o 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 android:extractNativeLibs="true" no <application>. Esse é o padrão, mas se você defini-lo explicitamente como false, o script de wrap.sh não funcionará.

É 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 $@"
fi

exec $cmd