Como implementar a biblioteca Java SDK

A plataforma Android contém um grande número de bibliotecas Java compartilhadas, que podem ser incluídas no caminho de classe dos apps com a tag <uses-library> no manifesto do app. Os apps estão vinculados a essas bibliotecas. Portanto, elas precisam ser tratadas como o restante da API do Android em termos de compatibilidade, revisão e suporte a ferramentas. No entanto, a maioria das bibliotecas não têm esses recursos.

O tipo de módulo java_sdk_library ajuda a gerenciar bibliotecas desse tipo. Os fabricantes de dispositivos podem usar esse mecanismo nas próprias bibliotecas Java compartilhadas para manter a compatibilidade com versões anteriores das APIs deles. Se os fabricantes de dispositivos usam as próprias bibliotecas com a tag <uses-library> no lugar do caminho bootclass, a java_sdk_library pode verificar se essas bibliotecas Java funcionam de forma estável com a API.

A java_sdk_library implementa APIs opcionais do SDK para os apps usarem. As bibliotecas implementadas pela java_sdk_library no seu arquivo de build (Android.bp) realizam estas operações:

  • As bibliotecas de stubs são geradas para incluir stubs, stubs.system e stubs.test. Essas bibliotecas de stubs são criadas com o reconhecimento das anotações@hide, @SystemApi e @TestApi.
  • O java_sdk_library gerencia os arquivos de especificação da API (como current.txt) em um subdiretório da API. Esses arquivos são comparados com o código mais recente para garantir que estão nas versões mais atuais. Caso não estejam, você vai receber uma mensagem de erro explicando como atualizar. Analise todas as mudanças de atualização manualmente, para garantir que elas atendem às suas expectativas.

    Para atualizar todas as APIs, use m update-api. Para verificar se uma API está atualizada, use m checkapi.
  • Os arquivos de especificação da API são verificados em relação às versões do Android publicadas mais recentes para garantir que a API seja compatível com versões anteriores. Os módulos java_sdk_library fornecidos como parte do AOSP colocam as versões de lançamentos anteriores em prebuilts/sdk/<latest number>.
  • Com relação às verificações de arquivos de especificação de API, é possível executar uma das três ações abaixo:
    • Permitir que as verificações continuem (não fazer nada).
    • Desativar as verificações adicionando o código abaixo à java_sdk_library:
      unsafe_ignore_missing_latest_api: true,
    • Fornecer APIs vazias para os novos módulos da java_sdk_library criando arquivos de texto vazios com o nome module_name.txt no diretório version/scope/api.
  • Após a instalação da biblioteca de implementação de execução, um arquivo XML é gerado e instalado.

Como a java_sdk_library funciona

Uma java_sdk_library conhecida como X cria o seguinte:

  1. Duas cópias da biblioteca de implementação: uma com o nome X e outra com o nome X.impl. A biblioteca X vai ser instalada no dispositivo. A biblioteca X.impl vai estar disponível apenas se o acesso explícito à biblioteca de implementação for necessário para outros módulos, como para uso em testes. O acesso explícito quase nunca é necessário.
  2. Os escopos podem ser ativados e desativados para personalizar o acesso. Assim como os modificadores de acesso de palavras-chave Java, um escopo público oferece uma ampla variedade de acessos, enquanto um escopo de teste contém apenas APIs usadas em testes. Para cada escopo ativado, a biblioteca cria:
    • Um módulo de origem de stubs (do tipo de módulo droidstubs) consome a fonte da implementação e gera um conjunto de origens stub com o arquivo de especificação da API.
    • Uma biblioteca de stubs (do tipo de módulo java_library) é a versão compilada dos stubs As bibliotecas usadas para compilar não são iguais às fornecidas à java_sdk_library, o que garante que os detalhes da implementação não sejam vazados para os stubs da API.
    • Se você precisar de mais bibliotecas para compilar stubs, use as propriedades stub_only_libs e stub_only_static_libs para fornecê-las.

Se uma java_sdk_library tem o nome "X" e está sendo compilada como "X", não se esqueça de sempre se referir a ela dessa forma e não faça nenhuma modificação no nome. O build vai selecionar uma biblioteca adequada. Para garantir que você está usando a biblioteca mais adequada, inspecione seus stubs para saber se o build apresentou erros. Faça as correções necessárias seguindo estas orientações:

  • É possível verificar se você tem uma biblioteca adequada ao observar a linha de comando e inspecionar quais stubs estão listados para determinar o escopo:
    • O escopo é grande demais: a biblioteca dependente precisa de um determinado escopo de APIs. No entanto, algumas APIs incluídas na biblioteca estão fora desse escopo, como as APIs do sistema incluídas nas APIs públicas.
    • O escopo é muito restrito: a biblioteca dependente não tem acesso a todas as bibliotecas necessárias. Por exemplo, a biblioteca dependente precisa usar a API do sistema, mas recebe a API pública. Isso geralmente resulta em um erro de compilação porque as APIs necessárias estão ausentes.
  • Para trocar a biblioteca, siga um dos métodos abaixo:
    • Mude a sdk_version para a versão que você precisa. OU
    • Especifique a biblioteca correta de forma explícita, por exemplo, <X>.stubs ou <X>.stubs.system.

Como usar a java_sdk_library X

A biblioteca de implementação X é usada quando é referenciada em apex.java_libs. No entanto, devido a uma limitação do Soong, quando a biblioteca X é referenciada em outro módulo java_sdk_library, dentro da mesma biblioteca APEX, a X.impl precisa ser usada de forma explícita e não a biblioteca X.

Uma biblioteca de stubs é usada quando a java_sdk_library é referenciada em outro lugar. Essa biblioteca é selecionada de acordo com a configuração da propriedade sdk_version do módulo dependente. Por exemplo, um módulo que especifica sdk_version: "current" usa os stubs públicos, enquanto um módulo que especifica sdk_version: "system_current" usa os stubs do sistema. Se não é possível encontrar uma correspondência exata, a biblioteca de stubs mais próxima será usada. Uma java_sdk_library que oferecer apenas uma API pública, vai fornecer os stubs públicos para todos.

Fluxo de criação com a biblioteca Java SDK
Figura 1. Fluxo de criação com a biblioteca Java SDK

Exemplos e origens

As propriedades srcs e api_packages precisam estar presentes na java_sdk_library.

java_sdk_library {
        name: "com.android.future.usb.accessory",
        srcs: ["src/**/*.java"],
        api_packages: ["com.android.future.usb"],
    }

O AOSP recomenda (mas não exige) que as novas instâncias da java_sdk_library ativem os escopos da API que eles querem usar de forma explícita. Também é possível migrar (opcional) as instâncias java_sdk_library atuais para ativar os escopos da API que serão usados:

java_sdk_library {
         name: "lib",
         public: {
           enabled: true,
         },
         system: {
           enabled: true,
         },
         …
    }

Para configurar a biblioteca impl usada na execução, use todas as propriedades java_library normais, como hostdex, compile_dex e errorprone.

java_sdk_library {
        name: "android.test.base",

        srcs: ["src/**/*.java"],

        errorprone: {
          javacflags: ["-Xep:DepAnn:ERROR"],
        },

        hostdex: true,

        api_packages: [
            "android.test",
            "android.test.suitebuilder.annotation",
            "com.android.internal.util",
            "junit.framework",
        ],

        compile_dex: true,
    }

Use as propriedades abaixo para configurar bibliotecas de stubs:

  • merge_annotations_dirs e merge_inclusion_annotations_dirs.
  • api_srcs: a lista de arquivos de origem opcionais que fazem parte da API, mas não da biblioteca de execução.
  • stubs_only_libs: a lista das bibliotecas Java que estão no caminho de classe durante a criação de stubs.
  • hidden_api_packages: a lista de nomes de pacotes que precisam estar ocultos para a API.
  • droiddoc_options: argumento extra para a metalava.
  • droiddoc_option_files: lista os arquivos que podem ser referenciados dentro das droiddoc_options usando $(location <label>), em que <file> é uma entrada na lista.
  • annotations_enabled:

A java_sdk_library é uma java_library, mas não é um módulo droidstubs. Ela não oferece suporte a todas as propriedades droidstubs. O exemplo abaixo foi retirado do arquivo de build de biblioteca android.test.mock.

java_sdk_library {
        name: "android.test.mock",

        srcs: [":android-test-mock-sources"],
        api_srcs: [
            // Note: The following aren’t APIs of this library. Only APIs under the
            // android.test.mock package are taken. These do provide private APIs
            // to which android.test.mock APIs reference. These classes are present
            // in source code form to access necessary comments that disappear when
            // the classes are compiled into a Jar library.
            ":framework-core-sources-for-test-mock",
            ":framework_native_aidl",
        ],

        libs: [
            "framework",
            "framework-annotations-lib",
            "app-compat-annotations",
            "Unsupportedappusage",
        ],

        api_packages: [
            "android.test.mock",
        ],
        permitted_packages: [
            "android.test.mock",
        ],
        compile_dex: true,
        default_to_stubs: true,
    }

Manter a compatibilidade com versões anteriores

O sistema de build confirma se as APIs mantiveram a compatibilidade com versões anteriores comparando os arquivos de API mais recentes com os arquivos de API gerados durante a compilação. A java_sdk_library executa essa verificação de compatibilidade usando as informações fornecidas pelas prebuilt_apis. Os arquivos de API de todas as bibliotecas criadas com a java_sdk_library precisam estar atualizados com a versão mais recente de api_dirs nas prebuilt_apis. Após você lançar a versão, você poderá acessar a lista de arquivos de API e bibliotecas de stubs com um build dist usando PRODUCT=sdk_phone_armv7-sdk.

A propriedade api_dirs é uma lista de diretórios de versão da API nas prebuilt_apis. Os diretórios de versão da API precisam estar localizados no nível do diretório Android.bp.

prebuilt_apis {
       name: "foo",
       api_dirs: [
           "1",
           "2",
             ....
           "30",
           "current",
       ],
    }

Configure os diretórios com a estrutura version/scope/api/ no diretório pré-criado. version corresponde ao nível da API, e scope define se o diretório é público, do sistema ou de teste.

  • version/scope contém bibliotecas Java.
  • version/scope/api contém arquivos .txt da API. Nesse diretório, crie arquivos de texto vazios com os nomes module_name.txt e module_name-removed.txt.
     ├── 30
          │   ├── public
          │   │   ├── api
          │   │   │   ├── android.test.mock-removed.txt
          │   │   │   └── android.test.mock.txt
          │   │   └── android.test.mock.jar
          │   ├── system
          │   │   ├── api
          │   │   │   ├── android.test.mock-removed.txt
          │   │   │   └── android.test.mock.txt
          │   │   └── android.test.mock.jar
          │   └── test
          │       ├── api
          │       │   ├── android.test.mock-removed.txt
          │       │   └── android.test.mock.txt
          │       └── android.test.mock.jar
          └── Android.bp