Adicionar a Play Integrity ao seu app Android

1. Introdução

Última atualização: 4 de janeiro de 2023

O que é a Play Integrity?

A API Play Integrity ajuda a proteger apps e jogos contra interações possivelmente perigosas e fraudulentas. Você pode usar a API Play Integrity para receber vereditos de integridade do seu app e dispositivo, o que pode ajudar a responder com as ações adequadas para reduzir ataques e abusos, como fraude, trapaças e acesso não autorizado.

Soluções anteriores

A API Play Integrity substitui duas soluções anteriores: a biblioteca App Licensing e a API SafetyNet Attestation. A Play Integrity é recomendada para novos aplicativos. Recomendamos atualizar os aplicativos atuais que usam as soluções anteriores para usar a Play Integrity.

Como escolher um caminho

A Play Integrity inclui opções de biblioteca para diferentes tipos de apps:

  • Jogos ou outros apps criados em C/C++.
  • Apps criados na linguagem de programação Kotlin ou Java.
  • Jogos desenvolvidos com o mecanismo do Unity.

Este codelab inclui caminhos para as três opções. Você pode escolher os caminhos que são relevantes para suas necessidades de desenvolvimento. O codelab envolve a criação e a implantação de um servidor de back-end. Esse servidor é compartilhado nos três tipos de app.

O que você vai criar

Neste codelab, você vai integrar a Play Integrity a um app de exemplo e usar a API para verificar a integridade do dispositivo e do app. Você implantará um pequeno aplicativo de servidor de back-end usado para dar suporte ao processo de verificação de integridade.

O que você vai aprender

  • Como integrar a biblioteca Play Integrity a um app.
  • Como usar a API Play Integrity para fazer uma verificação de integridade.
  • Como processar um veredito de integridade com segurança usando um servidor.
  • Como interpretar os resultados do veredito de integridade.

O foco deste codelab é a Play Integrity. Conceitos não relevantes e blocos de código não são explicados em detalhes e são fornecidos para que você apenas copie e cole.

O que é necessário

  • Uma Conta do Google com um registro de desenvolvedor Android ativo, acesso ao Play Console e ao console do Google Cloud.
  • Para os caminhos C++ ou Kotlin, use o Android Studio 2021.1.1 ou mais recente.
  • Para o caminho do Unity, use o Unity 2020 LTS ou uma versão mais recente.
  • Um dispositivo Android conectado ao computador com as Opções do desenvolvedor e a Depuração USB ativadas.

Este codelab inclui links para recursos sobre como assinar e fazer upload de builds para o Play Console, mas é necessário que você tenha alguma familiaridade com esse processo.

2. Acessar o código

Os projetos do codelab estão disponíveis em um repositório Git. Há quatro diretórios no diretório pai do repositório:

  • O diretório server contém o código do servidor de exemplo que você implantará.
  • O diretório cpp contém um projeto do Android Studio para adicionar a Play Integrity a um jogo ou app C++.
  • O diretório kotlin contém um projeto do Android Studio para adicionar a Play Integrity a um app Android padrão.
  • O diretório unity contém um projeto criado com a versão 2020 LTS do mecanismo do Unity para adicionar a Play Integrity a um projeto do Unity.

Em cada um dos diretórios de exemplo do cliente há dois subdiretórios:

  • O diretório start, que tem a versão do projeto que vamos modificar para este codelab.
  • O diretório final, que tem uma versão do projeto que corresponde à aparência que ele vai ter após a conclusão do codelab.

Como clonar o repositório

Na linha de comando, mude para o diretório que você quer que contenha o diretório raiz add-play-integrity-codelab e, em seguida, clone o projeto do GitHub usando esta sintaxe:

git clone https://github.com/android/add-play-integrity-codelab.git

Como adicionar dependências (somente C++)

Se você pretende usar o caminho C++, é necessário inicializar os submódulos do repositório para configurar a biblioteca Dear ImGui (link em inglês), usada para a interface do usuário. Para fazer isso, na linha de comando:

  1. Mude o diretório de trabalho para: add-play-integrity-codelab/cpp/start/third-party
  2. git submodule update --init

3. Entender as funções de cliente e servidor

Um elemento importante do modelo de segurança da Play Integrity é mover as operações de validação do dispositivo para um servidor seguro que você controla. A execução dessas operações no servidor protege contra situações como um dispositivo comprometido tentando implantar um ataque de repetição ou adulterando o conteúdo da mensagem.

O servidor do codelab é um exemplo e, para simplificar, não contém muitos recursos que seriam desejáveis em um ambiente de produção. A lista de valores gerados é armazenada apenas na memória e não tem suporte do armazenamento permanente. Os endpoints do servidor não estão configurados para exigir autenticação (link em inglês).

O endpoint performCommand

O servidor fornece um endpoint /performCommand acessado por uma solicitação HTTP POST. O corpo da solicitação precisa ser um payload JSON com estes pares de chave-valor:

{
   "commandString": "command-here",
   "tokenString": "token-here"
}

O endpoint /performCommand retorna um payload JSON com estes pares de chave-valor:

{
   "commandSuccess": true/false,
   "diagnosticMessage": "summary text",
   "expressToken": "token-here"
}

O conteúdo real do parâmetro commandString não importa. O servidor valida a integridade da commandString ao usar a Play Integrity, mas não usa o valor do comando. Todas as versões de cliente usam o valor "TRANSFER FROM alice TO bob CURRENCY gems QUANTITY 1000".

O valor do parâmetro tokenString precisa ser:

  • Um token gerado pela API Play Integrity.
  • Um token expresso retornado por uma chamada anterior para /performCommand.

O servidor descriptografa e valida o token fornecido pela API Play Integrity. O resultado da descriptografia é um payload JSON de sinais de integridade. Dependendo do valor dos sinais, o servidor pode aprovar ou rejeitar o comando. Se o token for descriptografado, uma descrição resumida dos indicadores será retornada em diagnosticMessage. Normalmente, essas informações não são retornadas ao cliente em um aplicativo de produção, mas são usadas pelos clientes do codelab para mostrar o resultado da operação sem a necessidade de analisar os registros do servidor. Se ocorrer uma condição de erro durante o processamento do token, o erro será retornado em diagnosticMessage.

Geração de valores de uso único

Para fazer uma solicitação à Play Integrity, gere e associe um valor de uso único à solicitação. O valor de uso único é usado para ajudar a garantir que uma solicitação de integridade seja única e processada somente uma vez. Ele também é usado para verificar se o conteúdo da mensagem associada à solicitação de integridade não foi adulterado. Para mais informações sobre valores de uso único da Play Integrity, consulte a documentação.

Neste codelab, o valor de uso único é gerado combinando dois valores:

  • Um número aleatório de 128 bits gerado por um gerador de números aleatórios criptograficamente seguro
  • Um hash SHA-256 do valor commandString

A API Play Integrity espera que o valor de uso único seja uma string Base64 sem padding codificada em URL. Para criar a string de valor de uso único, este codelab converte as matrizes de bytes do número aleatório e dos valores de hash em strings hexadecimais e as concatena. A string resultante é uma string Base64 válida, mas não é codificada nem decodificada dessa forma.

Os clientes do codelab extraem o número aleatório chamando um endpoint /getRandom no servidor com uma solicitação HTTP GET. Esse é o método mais seguro de geração aleatória, já que o servidor pode verificar se ele foi a origem do número aleatório usado na solicitação de comando. No entanto, isso envolve uma chamada de ida e volta extra ao servidor. Os clientes podem eliminar essa etapa extra gerando o número aleatório por conta própria, abrindo mão de um pouco de segurança.

Token expresso

Como chamar a PIA é caro, o servidor também fornece um token expresso, um método alternativo de autenticação que fornece segurança mais baixa a um custo computacional menor. Um token expresso é retornado no campo expressToken por uma chamada para /serverCommand com uma verificação de integridade aprovada. Chamar a API Play Integrity é algo computacionalmente caro e tem como objetivo proteger as operações de alto valor. Ao retornar o token expresso, o servidor fornece uma autenticação de segurança mais baixa para a execução de operações que podem ser menos importantes ou ocorrer com muita frequência para justificar a validação completa com a API Play Integrity. Um token expresso pode ser usado em vez de um token da Play Integrity ao chamar /serverCommand. Cada token expresso é de uso único. Chamadas para /serverCommand que usam um token expresso válido retornam um novo token expresso.

Na implementação do codelab, como o token expresso é gerado de maneira exclusiva no servidor, os comandos ainda estão protegidos contra ataques de repetição. No entanto, os tokens expressos são menos seguros, já que omitem a proteção contra hash da modificação do comando e não detectam modificações do dispositivo que ocorreram após a chamada inicial para a API Play Integrity.

Arquitetura do servidor do codelab

Neste codelab, você pode instanciar um servidor usando o programa de servidor de exemplo criado em Kotlin usando o framework Ktor (link em inglês). O projeto inclui arquivos de configuração para implantar no Google Cloud App Engine. As instruções deste codelab abrangem a criação e a implantação no App Engine. Se você usa outro provedor de nuvem, o Ktor tem instruções de implantação (em inglês) para vários serviços de nuvem. É possível modificar o projeto e implantá-lo no serviço que preferir. A implantação na sua própria instância de servidor local também é uma opção.

Não é necessário usar o código de exemplo para seu servidor. Se você tiver um framework de aplicativos da Web preferido, é possível implementar os endpoints /getRandom e /performCommand no seu próprio framework usando o servidor de exemplo do codelab como guia.

Design do cliente

As três versões do cliente (C++, Kotlin e Unity) apresentam uma interface do usuário semelhante.

O botão Request random (solicitar aleatório) chama o endpoint /getRandom no servidor. O resultado aparece em um campo de texto. Isso pode ser usado para verificar a conexão e a função do servidor antes de adicionar a integração da Play Integrity.

O botão Call server with integrity check (chamar servidor para verificação de integridade) não faz nada no início do codelab. Siga as etapas para adicionar o código nestas operações:

  • Chamar /getRandom para receber um número aleatório.
  • Gerar um valor de uso único.
  • Criar uma solicitação da Play Integrity com o valor de uso único.
  • Chamar /performCommand usando o token gerado pela solicitação de Play Integrity.
  • Mostrar os resultados de /performCommand.

Os resultados do comando aparecem em um campo de texto. Para um comando bem-sucedido, um resumo das informações de veredito do dispositivo retornadas pela verificação de Play Integrity é mostrado.

O botão Call server with express token (chamar servidor com token expresso) aparece após uma operação bem-sucedida do botão Call server with integrity check. Ele chama /performCommand usando o token expresso do /performCommand anterior. Um campo de texto é usado para mostrar o sucesso ou a falha do comando. O valor de um token expresso retornado aparece no campo de texto usado para números aleatórios.

As funções da API Play Integrity que informam erros fazem isso retornando um código de erro. Para mais detalhes sobre esses códigos de erro, consulte a documentação da Play Integrity. Alguns erros podem ser causados por condições do ambiente, como uma conexão de Internet instável ou um dispositivo sobrecarregado. Para esses erros, pense em incluir uma opção para tentar de novo com espera exponencial. Neste codelab, os erros da Play Integrity são mostrados no Logcat.

4. Como configurar o Google Cloud App Engine

Para usar o Google Cloud, siga estas etapas:

  1. Inscreva-se no Google Cloud Platform. Use a mesma Conta do Google registrada no Play Console.
  2. Crie uma conta de faturamento.
  3. Instale e inicialize o SDK Google Cloud.

Use o CLI do Google Cloud recém-instalado para executar estes comandos:

  1. Instale a extensão do App Engine para Java: gcloud components install app-engine-java
  2. Crie um novo projeto do Cloud, substituindo $yourname por um identificador exclusivo neste comando: gcloud projects create $yourprojectname --set-as-default
  3. Crie um aplicativo do App Engine no projeto do Cloud: gcloud app create

5. Implantar o servidor

Definir o nome do pacote do aplicativo

Nesta etapa, você vai adicionar o nome do pacote do aplicativo ao código do servidor. Em um editor de texto, abra o arquivo de origem ValidateCommand.kt. Ele está localizado neste diretório:

add-play-integrity-codelab/server/src/main/kotlin/com/google/play/integrity/codelab/server/util

Encontre a linha abaixo, substitua o texto do marcador de posição por um identificador de pacote exclusivo e salve o arquivo:

const val APPLICATION_PACKAGE_IDENTIFIER = "com.your.app.package"

Mais adiante, você definirá esse identificador no projeto do cliente antes de fazer upload do app no Play Console.

Criar o servidor e implantar no App Engine

Use o Google Cloud CLI para executar o comando abaixo no diretório add-play-integrity/server para criar e implantar o servidor:

No Linux ou no macOS:

./gradlew appengineDeploy

No Microsoft Windows:

gradlew.bat appengineDeploy

Anote o local do Serviço implantado que aparecerá na saída de uma implantação bem-sucedida. Você precisará desse URL para configurar o cliente para se comunicar com o servidor.

Verificar a implantação

Use o Google Cloud CLI para executar o comando abaixo e verificar se o servidor está funcionando corretamente:

gcloud app browse

Esse comando abrirá um navegador da Web e o URL raiz. O servidor de exemplo mostrará uma mensagem Hello World! quando acessado pelo URL raiz.

6. Configurar o app no Play Console

Configurar a integridade do app no Play Console

Se você já tiver uma entrada de app no Play Console, poderá usá-la para este codelab. Como alternativa, siga as etapas para criar um novo app no Play Console. Depois de selecionar ou criar o app no Play Console, você precisará configurar a integridade do app. No menu do Play Console à esquerda, acesse Integridade do app na seção Versão.

Clique no botão Vincular projeto do Cloud. Selecione o projeto do Google Cloud usado com o servidor e clique no botão Vincular projeto.

Acesso ao Google Cloud para seu servidor

O servidor de back-end precisa descriptografar o token de integridade gerado no cliente pela API Play Integrity. A Play Integrity oferece duas opções de gerenciamento: chaves geradas e gerenciadas pelo Google ou chaves fornecidas pelo desenvolvedor. Este codelab usa o comportamento padrão recomendado das chaves gerenciadas pelo Google.

Com chaves gerenciadas pelo Google, seu servidor de back-end transmite o token de integridade criptografado aos servidores do Google Play para descriptografia. O servidor do codelab usa a biblioteca de cliente das APIs do Google (link em inglês) para se comunicar com os servidores do Google Play.

Agora que o servidor está funcionando e você configurou o app no Play Console, comece a personalizar os clientes correspondentes às plataformas escolhidas. Todas as etapas de uma determinada plataforma são agrupadas para que você possa pular as instruções para as plataformas que não estiver usando.

7. Criar e executar o cliente (C++)

Execute o Android Studio. Na janela Welcome to Android Studio, clique no botão Open e abra o projeto do Android Studio localizado em add-play-integrity-codelab/cpp/start.

Atualizar o ID do aplicativo

Antes de fazer upload de uma versão para o Google Play, é necessário alterar o ID do aplicativo do padrão para algo único. Siga estas etapas:

  1. No painel Project do Android Studio, encontre e abra o arquivo build.gradle em start/app.
  2. Encontre a instrução applicationId.
  3. Mude com.google.play.integrity.codelab.cpp para o nome do pacote escolhido ao implantar o servidor e salve o arquivo.
  4. Na parte de cima do arquivo, um banner aparecerá informando que os arquivos do Gradle mudaram. Clique em Sync Now para recarregar e sincronizar o arquivo de novo.
  5. No painel Project do Android Studio, abra o arquivo AndroidManifest.xml em start/app/src/main.
  6. Encontre a instrução package="com.example.google.codelab.playintegritycpp".
  7. Substitua com.example.google.codelab.playintegritycpp pelo nome do pacote exclusivo e salve o arquivo.
  8. No painel Project do Android Studio, abra o arquivo PlayIntegrityCodelabActivity em start/app/src/main/java/com.example.google.codelab.playintegritycpp.
  9. Encontre a instrução package com.example.google.codelab.playintegritycpp.
  10. Substitua com.example.google.codelab.playintegritycpp pelo nome de pacote exclusivo.
  11. Clique com o botão direito do mouse no nome do novo pacote e escolha Show Context Actions.
  12. Escolha Move to (novo nome do pacote).
  13. Se aparecer, escolha o botão Sync Now na parte de cima do arquivo.

Atualizar os URLs do servidor

O projeto precisa ser atualizado para apontar para os locais do URL em que você implantou o servidor.

  1. No painel Project do Android Studio, abra o arquivo server_urls.hpp em start/app/src/main/cpp.
  2. Adicione o URL raiz mostrado quando você implantou o servidor nas definições de GET_RANDOM_URL e PERFORM_COMMAND_URL e salve o arquivo.

O resultado será semelhante a este:

constexpr char GET_RANDOM_URL[] = "https://your-play-integrity-server.uc.r.appspot.com/getRandom";
constexpr char PERFORM_COMMAND_URL[] = "https://your-play-integrity-server.uc.r.appspot.com/performCommand";

O URL específico varia de acordo com o nome do projeto e a região do Google Cloud usada para implantar o servidor.

Criar e executar

Conectar um dispositivo Android configurado para desenvolvimento. No Android Studio, crie o projeto e execute no dispositivo conectado. O app ficará assim:

429ccc112f78d454.png

Toque no botão Request Random para executar um código que faz uma solicitação HTTP ao seu servidor para solicitar um número aleatório. Após um breve atraso, o número aleatório aparecerá na tela:

62acee42ba1fa80.png

Se uma mensagem de erro aparecer, a saída do painel Logcat poderá conter mais detalhes.

Depois de verificar se está se comunicando com o servidor e extraindo um valor aleatório, você pode começar a integrar a API Play Integrity.

8. Adicionar a Play Integrity ao projeto (C++)

Fazer o download do SDK

Você precisará fazer o download e extrair o SDK do Play Core. Siga estas etapas:

  1. Faça o download do SDK da Play Core empacotado em um arquivo .zip na página do SDK nativo da Play Core.
  2. Extraia o arquivo ZIP.
  3. Verifique se o diretório recém-extraído chama play-core-native-sdk e copie ou mova-o para o diretório add-play-integrity-codelab/cpp/start.

Atualizar o build.gradle

No painel Project do Android Studio, abra o arquivo build.gradle do módulo no diretório start/app.

Adicione, abaixo da linha apply plugin: 'com.android.application', a linha mostrada no snippet:

def playcoreDir = file("../play-core-native-sdk")

Localize o bloco externalNativeBuild no bloco defaultConfig e mude a instrução arguments no bloco cmake para corresponder ao seguinte:

                arguments "-DANDROID_STL=c++_shared",
                          "-DPLAYCORE_LOCATION=$playcoreDir"

Adicione o código abaixo ao final do bloco android:

    buildTypes {
        release {
            proguardFiles getDefaultProguardFile("proguard-android.txt"),
                          "proguard-rules.pro",
                          "$playcoreDir/proguard/common.pgcfg",
                          "$playcoreDir/proguard/integrity.pgcfg"
        }
    }

Adicione a linha abaixo dentro e ao final do bloco dependencies:

    implementation files("$playcoreDir/playcore.aar")

Salve as mudanças. Na parte de cima do arquivo, um banner aparecerá informando que os arquivos do Gradle mudaram. Clique em Sync Now para recarregar e sincronizar o arquivo de novo.

Atualizar CMakeList.txt

No painel Project do Android Studio, abra o arquivo CMakeLists.txt no diretório start/app/src/main/cpp.

Adicione, abaixo dos comandos find_package, estas linhas:

include("${PLAYCORE_LOCATION}/playcore.cmake")
add_playcore_static_library()

Localize a linha target_include_directories(game PRIVATE e adicione esta linha abaixo dela:

        ${PLAYCORE_LOCATION}/include

Localize a linha target_link_libraries(game e adicione esta linha abaixo dela:

        playcore

Salve o arquivo. Na parte de cima do arquivo, vai aparecer um banner informando que os arquivos de build externos mudaram. Clique em Sync Now para recarregar e sincronizar o arquivo de novo.

Escolha Make Project no menu Build e verifique se o projeto foi criado.

9. Fazer uma solicitação de integridade (C++)

O app recebe informações de integridade usando a API Play Integrity para solicitar um token, que você envia ao servidor para descriptografia e verificação. Agora você vai adicionar um código ao projeto para inicializar a API Play Integrity e usá-la para fazer uma solicitação de integridade.

Adicionar um botão de comando

Em um app ou jogo real, você pode realizar uma verificação de integridade antes de atividades específicas, como fazer uma compra na loja ou participar de uma sessão de jogo multiplayer. Neste codelab, adicionaremos um botão à interface para acionar manualmente uma verificação de integridade e chamar o servidor, transmitindo o token da Play Integrity gerado.

O projeto do codelab contém uma classe ClientManager, definida nos arquivos de origem client_manager.cpp e client_manager.hpp. Por conveniência, esse arquivo já foi adicionado ao projeto, mas não tem o código de implementação que você vai adicionar agora.

Para adicionar o botão da interface, comece abrindo o arquivo demo_scene.cpp no painel Project do Android Studio, no diretório start/app/src/main/cpp. Primeiro, localize a função DemoScene::GenerateCommandIntegrity() vazia e adicione este código:

    const auto commandResult =
            NativeEngine::GetInstance()->GetClientManager()->GetOperationResult();
    if (commandResult != ClientManager::SERVER_OPERATION_PENDING) {
        if (ImGui::Button("Call server with integrity check")) {
            DoCommandIntegrity();
        }
    }

Em seguida, localize a função DemoScene::DoCommandIntegrity() vazia. Adicione o código abaixo.

    ClientManager *clientManager = NativeEngine::GetInstance()->GetClientManager();
    clientManager->StartCommandIntegrity();
    mServerRandom = clientManager->GetCurrentRandomString();

Salve o arquivo. Agora, você vai atualizar a classe ClientManager do exemplo para adicionar a funcionalidade da Play Integrity.

Atualizar o arquivo principal do gerenciador

Abra o arquivo client_manager.hpp no painel Project do Android Studio, no diretório start/app/src/main/cpp.

Inclua o arquivo principal da API Play Integrity adicionando a seguinte linha abaixo da linha #include "util.hpp":

#include "play/integrity.h"

A classe ClientManager precisará conter referências a objetos IntegrityTokenRequest e IntegrityTokenResponse. Adicione as linhas mostradas no snippet à parte de baixo da definição da classe ClientManager:

    IntegrityTokenRequest *mTokenRequest;
    IntegrityTokenResponse *mTokenResponse;

Salve o arquivo.

Inicializar e encerrar a Play Integrity

No painel Project do Android Studio, abra o arquivo client_manager.cpp no diretório start/app/src/main/cpp.

Encontre o construtor ClientManager::ClientManager(). Substitua a instrução mInitialized = false; por este código:

    mTokenRequest = nullptr;
    mTokenResponse = nullptr;

    const android_app *app = NativeEngine::GetInstance()->GetAndroidApp();
    const IntegrityErrorCode errorCode = IntegrityManager_init(app->activity->vm,
                                                               app->activity->javaGameActivity);
    if (errorCode == INTEGRITY_NO_ERROR) {
        mInitialized = true;
    } else {
        mInitialized = false;
        ALOGE("Play Integrity initialization failed with error: %d", errorCode);
        ALOGE("Fatal Error: Play Integrity is unavailable and cannot be used.");
    }

Adicione o código abaixo ao destrutor ClientManager::~ClientManager():

    if (mInitialized) {
        IntegrityManager_destroy();
        mInitialized = false;
    }

Solicitar um token de integridade

A solicitação de um token de integridade da API Play Integrity é uma operação assíncrona. Você precisará criar um objeto de solicitação de token, atribuir um valor de uso único a ele e fazer a solicitação de token. Para fazer isso, adicione o código abaixo à função ClientManager::StartCommandIntegrity() vazia:

    // Only one request can be in-flight at a time
    if (mStatus != CLIENT_MANAGER_REQUEST_TOKEN) {
        mResult = SERVER_OPERATION_PENDING;
        // Request a fresh random
        RequestRandom();
        if (mValidRandom) {
            GenerateNonce();
            IntegrityTokenRequest_create(&mTokenRequest);
            IntegrityTokenRequest_setNonce(mTokenRequest, mCurrentNonce.c_str());

            const IntegrityErrorCode errorCode =
                    IntegrityManager_requestIntegrityToken(mTokenRequest, &mTokenResponse);
            if (errorCode != INTEGRITY_NO_ERROR) {
                // Log the error, in a real application, for potentially
                // transient errors such as network connectivity, you should
                // add retry with an exponential backoff
                ALOGE("Play Integrity returned error: %d", errorCode);
                CleanupRequest();
                mStatus = CLIENT_MANAGER_IDLE;
            } else {
                mStatus = CLIENT_MANAGER_REQUEST_TOKEN;
            }
        }
    }

Como a solicitação de token opera de forma assíncrona, será necessário verificar a conclusão dela. A classe ClientManager tem uma função Update(), que é chamada como parte da repetição de atualização do app. Adicione o código abaixo à função ClientManager::Update() para verificar o status da solicitação de token e processar o resultado depois que ele for concluído:

    if (mStatus == CLIENT_MANAGER_REQUEST_TOKEN) {
        IntegrityResponseStatus responseStatus = INTEGRITY_RESPONSE_UNKNOWN;
        const IntegrityErrorCode errorCode =
                IntegrityTokenResponse_getStatus(mTokenResponse, &responseStatus);
        if (errorCode != INTEGRITY_NO_ERROR) {
            // Log the error, in a real application, for potentially
            // transient errors such as network connectivity, you should
            // add retry with an exponential backoff
            ALOGE("Play Integrity returned error: %d", errorCode);
            CleanupRequest();
            mStatus = CLIENT_MANAGER_IDLE;
        } else if (responseStatus == INTEGRITY_RESPONSE_COMPLETED) {
            std::string tokenString = IntegrityTokenResponse_getToken(mTokenResponse);
            SendCommandToServer(tokenString);
            CleanupRequest();
            mStatus = CLIENT_MANAGER_RESPONSE_AVAILABLE;
        }
    }

Limpar os objetos de solicitação

Você precisa informar à API Play Integrity quando terminar de lidar com objetos de solicitação e resposta de token para que ela possa destruí-los e recuperar os recursos. Adicione o código abaixo à função ClientManager::CleanupRequest():

    if (mTokenResponse != nullptr) {
        IntegrityTokenResponse_destroy(mTokenResponse);
        mTokenResponse = nullptr;
    }
    if (mTokenRequest != nullptr) {
        IntegrityTokenRequest_destroy(mTokenRequest);
        mTokenRequest = nullptr;
    }

Escolha Make Project no menu Build e verifique se o projeto foi criado.

10. Enviar o token para o servidor (C++)

Agora você vai adicionar um código para enviar um comando ao seu servidor que inclui o token de integridade. Você também vai adicionar um código para processar o resultado.

Adicione o código abaixo à função ClientManager::SendCommandToServer():

// Note that for simplicity, we are doing HTTP operations as
// synchronous blocking instead of managing them from a
// separate network thread
HTTPClient client;
std::string errorString;

// Manually construct the json payload for ServerCommand
std::string payloadString = COMMAND_JSON_PREFIX;
payloadString += TEST_COMMAND;
payloadString += COMMAND_JSON_TOKEN;
payloadString += token;
payloadString += COMMAND_JSON_SUFFIX;

auto result = client.Post(PERFORM_COMMAND_URL, payloadString, &errorString);
if (!result) {
   ALOGE("SendCommandToServer Curl reported error: %s", errorString.c_str());
   mResult = SERVER_OPERATION_NETWORK_ERROR;
} else {
   ALOGI("SendCommandToServer result: %s", (*result).c_str())
   // Preset to success, ParseResult will set a failure result if the parsing
   // errors.
   mResult = SERVER_OPERATION_SUCCESS;
   ParseResult(*result);
}

Adicione o código abaixo à função ClientManager::ParseResult():

    bool validJson = false;
    JsonLookup jsonLookup;
    if (jsonLookup.ParseJson(resultJson)) {
        // Look for all of our needed fields in the returned json
        auto commandSuccess = jsonLookup.GetBoolValueForKey(COMMANDSUCCESS_KEY);
        if (commandSuccess) {
            auto diagnosticString = jsonLookup.GetStringValueForKey(DIAGNOSTICMESSAGE_KEY);
            if (diagnosticString) {
                auto expressString = jsonLookup.GetStringValueForKey(EXPRESSTOKEN_KEY);
                if (expressString) {
                    if (*commandSuccess) {
                        // Express token only valid if the server reports the command succeeded
                        mValidExpressToken = true;
                    } else {
                        mValidExpressToken = false;
                        mResult = SERVER_OPERATION_REJECTED_VERDICT;
                    }
                    mCurrentSummary = *diagnosticString;
                    mCurrentExpressToken = *expressString;
                    validJson = true;
                }
            }
        }
    }
    if (!validJson) {
        mResult = SERVER_OPERATION_INVALID_RESULT;
    }

Agora você vai gerar um pacote de app assinado e fazer upload dele ao Play Console para testar o app.

11. Criar e fazer upload (C++)

Criar e configurar um keystore para o app

O Android exige que todos os apps sejam assinados digitalmente com um certificado antes de serem instalados ou atualizados em um dispositivo.

Criaremos um Keystore para o app neste codelab. Se você estiver publicando uma atualização de um jogo atual, reutilize o mesmo Keystore que usou para lançar versões anteriores do app.

Criar um keystore e um pacote de app de lançamento

Siga as etapas em Keystore com o Android Studio para criar um keystore e usá-lo para gerar um build de lançamento assinado do jogo. No Android Studio, escolha Generate Signed Bundle / APK no menu Build para iniciar o processo de build. Escolha App Bundle quando aparecer a opção para selecionar um Android App Bundle ou APK. No final do processo, você terá um arquivo .aab adequado para upload ao Google Play Console.

Fazer upload para o Play Console

Depois de criar um arquivo de pacote de app, faça upload dele para o Play Console. Recomendamos usar a faixa de teste interno para facilitar o acesso rápido ao build.

Executar o build de teste

Agora, faça o download e execute o build de teste na Play Store. No momento, há um código marcador de posição que mostra o sucesso na função que enviará o token de integridade para seu servidor. Assim, a inicialização de uma verificação de integridade precisa ser bem-sucedida e ter este resultado:

ef5f55d73f808791.png

Parabéns, você integrou a Play Integrity a um app C++. Continue para outros exemplos de cliente ou vá para o final deste codelab.

12. Criar e executar o cliente (Unity)

O projeto Unity do codelab foi criado usando o Unity 2020 LTS (2020.3.31f1), mas é compatível com versões mais recentes do Unity. O plug-in da Play Integrity para Unity é compatível com o Unity 2018 LTS e versões mais recentes.

Configuração do projeto

Siga estas etapas:

  1. No Unity Hub ou Unity Editor, abra o projeto do Unity localizado em add-play-integrity-codelab/unity/start.
  2. Depois que o projeto for carregado, selecione Build Settings… no menu File do Unity.
  3. Na janela Build Settings, mude a plataforma para Android.
  4. Na janela Build Settings, clique no botão Player Settings….
  5. Na janela Project Settings, com a categoria Player selecionada, procure a seção Settings for Android. Abra a lista Other Settings.

b994587b808c7be4.png

  1. Encontre a entrada Package Name em Identification.

d036e5be73096083.png

  1. Mude o nome do pacote para o identificador que você escolheu ao implantar o servidor.
  2. Feche a janela Project Settings.
  3. Selecione Save Project no menu File do Unity.

Atualizar URLs do servidor

O projeto precisa ser atualizado para apontar para os locais do URL em que você implantou o servidor. Para fazer isso, siga estas etapas:

  1. Abra o arquivo PlayIntegrityController.cs na pasta Scripts em um ambiente de desenvolvimento integrado ou em um editor de texto.
  2. Mude os valores das variáveis URL_GETRANDOM e URL_PERFORMCOMMAND para apontar para seu servidor.
  3. Salve o arquivo.

O resultado será semelhante a este:

    private readonly string URL_GETRANDOM = "https://your-play-integrity-server.uc.r.appspot.com/getRandom";
    private readonly string URL_PERFORMCOMMAND = "https://your-play-integrity-server.uc.r.appspot.com/performCommand";

O URL específico varia de acordo com o nome do projeto e a região do Google Cloud usada para implantar o servidor.

Testar a funcionalidade do servidor

Você pode testar a funcionalidade do servidor executando o projeto no editor do Unity. Siga estas etapas:

  1. Abra o arquivo de cena SampleScene localizado na pasta Scenes.
  2. Clique no botão Play no editor.
  3. Clique no botão Request Random na tela Game.

Após um breve atraso, o valor aleatório vai aparecer na tela, semelhante ao seguinte:

f22c56cdd2e56050.png

13. Adicionar o plug-in da Play Integrity ao projeto (Unity)

Fazer o download do plug-in

Em um navegador da Web, abra a página de versões (link em inglês) do repositório GitHub dos plug-ins do Unity para o Google Play. Use a versão mais recente dos plug-ins. Faça o download do arquivo com.google.play.integrity-<version>.unitypackage para a Play Integrity na lista Assets.

Instalar o plug-in

Na barra de menus do editor do Unity, selecione Assets -> Import Package -> Custom Package… e abra o arquivo .unitypackage que você transferiu por download. Clique no botão Import depois que a janela Import Unity Package aparecer.

O plug-in inclui o External Dependency Manager (link em inglês) para Unity (EDM4U). O EDM4U implementa a resolução de dependências automática para os componentes Java necessários para usar a Play Integrity. Quando for solicitado que você ative a resolução automática de dependências, clique no botão Enable.

5bf0be9139fab036.png

É altamente recomendável usar a resolução automática. Problemas de dependência podem resultar na falha da criação ou execução do projeto.

14. Fazer uma solicitação de integridade (Unity)

Criar uma solicitação de integridade

Para criar uma solicitação de integridade, siga estas etapas.

  1. Abra o arquivo PlayIntegrityController.cs na pasta Scripts em um ambiente de desenvolvimento integrado ou em um editor de texto.
  2. Adicione a linha abaixo ao bloco de instruções using na parte de cima do arquivo:
using Google.Play.Integrity;
  1. Encontre a função RunIntegrityCommand() e substitua a instrução yield return null; por este código:
        // Call our server to retrieve a random number.
        yield return GetRandomRequest();
        if (!string.IsNullOrEmpty(_randomString))
        {
            // Create an instance of an integrity manager.
            var integrityManager = new IntegrityManager();

            // Request the integrity token by providing a nonce.
            var tokenRequest = new IntegrityTokenRequest(GenerateNonceString(_randomString,
                TEST_COMMAND));
            var requestIntegrityTokenOperation =
                integrityManager.RequestIntegrityToken(tokenRequest);

            // Wait for PlayAsyncOperation to complete.
            yield return requestIntegrityTokenOperation;

            // Check the resulting error code.
            if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
            {
                // Log the error, in a real application, for potentially
                // transient errors such as network connectivity, you should
                // add retry with an exponential backoff
                Debug.Log($@"IntegrityAsyncOperation failed with error: 
                    {requestIntegrityTokenOperation.Error.ToString()}");
                yield break;
            }

            // Get the response.
            var tokenResponse = requestIntegrityTokenOperation.GetResult();

            // Send the command to our server with a POST request, including the
            // token, which will be decrypted and verified on the server.
            yield return PostServerCommand(tokenResponse.Token);
        }

Enviar o comando ao servidor

Continue editando o arquivo PlayIntegrityController.cs, encontre a função PostServerCommand() substitua a instrução yield return null; por este código:

        // Start a HTTP POST request to the performCommand URL, sending it the
        // command and integrity token data provided by Play Integrity.
        var serverCommand = new ServerCommand(TEST_COMMAND, tokenResponse);
        var commandRequest = new UnityWebRequest(URL_PERFORMCOMMAND, "POST");
        string commandJson = JsonUtility.ToJson(serverCommand);
        byte[] jsonBuffer = Encoding.UTF8.GetBytes(commandJson);
        commandRequest.uploadHandler = new UploadHandlerRaw(jsonBuffer);
        commandRequest.downloadHandler = new DownloadHandlerBuffer();
        commandRequest.SetRequestHeader(CONTENT_TYPE, JSON_CONTENT);
        yield return commandRequest.SendWebRequest();

        if (commandRequest.result == UnityWebRequest.Result.Success)
        {
            // Parse the command result Json
            var commandResult = JsonUtility.FromJson<CommandResult>(
                commandRequest.downloadHandler.text);
            if (commandResult != null)
            {
                resultLabel.text = commandResult.diagnosticMessage;
                _expressToken = commandResult.expressToken;
                if (commandResult.commandSuccess)
                {
                    resultLabel.color = Color.green;
                    expressButton.SetActive(true);
                }
                else
                {
                    resultLabel.color = Color.black;
                    expressButton.SetActive(false);
                }
            }
            else
            {
                Debug.Log("Invalid CommandResult json");
            }
        }
        else
        {
            Debug.Log($"Web request error on processToken: {commandRequest.error}");
        }

Salve o arquivo.

15. Criar e fazer upload (Unity)

Use o editor Android Keystore Manager (link em inglês) do Unity para configurar a assinatura do seu build para upload no Play Console.

Depois de configurar as informações de assinatura, siga estas etapas:

  1. Selecione Build -> Build Settings… no menu File do Unity.
  2. Confira se a SampleScene está incluída na lista Scenes in Build.
  3. Verifique se a caixa Build App Bundle (Google Play) está marcada.
  4. Clique no botão Build e escolha um nome para o arquivo de exportação.

Depois de criar um arquivo de pacote de app, faça upload dele para o Play Console. Recomendamos usar a faixa de teste interno para facilitar o acesso rápido ao build.

Agora você pode fazer o download e instalar seu build para executar uma verificação de integridade. Os resultados serão semelhantes a estes:

fa83cdb1a700ca0b.png

Parabéns! Você integrou a Play Integrity a um projeto do mecanismo do Unity. Continue para outros exemplos de cliente ou vá para o final deste codelab.

16. Criar e executar o projeto (Kotlin)

Execute o Android Studio. Na janela Welcome to Android Studio, clique no botão Open e abra o projeto do Android Studio localizado em add-play-integrity-codelab/kotlin/start.

Atualizar o ID do aplicativo

Antes de fazer upload de uma versão para o Google Play, é necessário alterar o ID do aplicativo do padrão para algo único. Siga estas etapas:

  1. No painel Project do Android Studio, abra o arquivo build.gradle do módulo PlayIntegrityCodelab.app.
  2. Encontre a instrução applicationId.
  3. Mude com.example.google.codelab.playintegritykotlin para o identificador que você escolheu ao implantar o servidor e salve o arquivo.
  4. Na parte de cima do arquivo, um banner aparecerá informando que os arquivos do Gradle mudaram. Clique em Sync Now para recarregar e sincronizar o arquivo de novo.

Atualizar os URLs do servidor

O projeto precisa ser atualizado para apontar para o local do URL em que você implantou o servidor. Para fazer isso, siga estas etapas:

  1. No painel Project do Android Studio, abra o arquivo IntegrityServer em start/app/src/main/java/com.example.google.codelab.playintegritykotlin/integrity.
  2. Mude o URL de ‘https://your.play-integrity.server.com' para o URL de base do seu servidor e salve o arquivo.

O resultado será semelhante a este:

    private val SERVER_URL: String = "https://your-play-integrity-server.uc.r.appspot.com"

O URL específico varia de acordo com o nome do projeto e a região do Google Cloud usada para implantar o servidor.

Criar e executar

Conectar um dispositivo Android configurado para desenvolvimento. No Android Studio, crie o projeto e execute no dispositivo conectado. O app ficará assim:

d77ca71dc209452f.png

Na inicialização, o app chama o endpoint getRandom no servidor e mostra o resultado. Se ocorrer um erro, como um URL incorreto ou o servidor não estiver funcionando, uma caixa de diálogo de erro vai aparecer. Selecione o botão Request Random para receber um novo número aleatório do servidor. O botão Call server with integrity check ainda não faz nada. Você vai adicionar essa funcionalidade nas próximas seções.

17. Adicionar a Play Integrity ao projeto (Kotlin)

Para adicionar a biblioteca Play Integrity e as dependências de suporte ao projeto, siga estas etapas:

  1. No painel Project do Android Studio, abra o arquivo build.gradle em start/app.
  2. Localize o bloco dependencies na parte de baixo do arquivo.
  3. Adicione estas linhas à parte de baixo do bloco dependencies:
    implementation "com.google.android.play:integrity:1.0.1"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.1"
  1. Salve o arquivo.
  2. Na parte de cima do arquivo, um banner aparecerá informando que os arquivos do Gradle mudaram. Clique em Sync Now para recarregar e sincronizar o arquivo de novo.

O exemplo de Kotlin usa corrotinas. A biblioteca kotlinx-coroutines-play-services adiciona extensões que facilitam o trabalho com objetos Task assíncronos da Play Integrity de dentro de corrotinas Kotlin.

18. Fazer uma solicitação de integridade (Kotlin)

No painel Project do Android Studio, abra o arquivo IntegrityServer em start/app/src/main/java/com.example.google.codelab.playintegritykotlin/integrity. Na parte de baixo do arquivo, há uma função integrityCommand vazia. A interface chama essa função quando o botão Call server with integrity check é pressionado.

Adicione o código à função integrityCommand para realizar estas operações:

  1. Extrair um novo número aleatório do servidor para usar ao criar um valor de uso único para associar à verificação de integridade.
  2. Chamar a API Play Integrity para fazer uma solicitação de integridade e receber um token de integridade com os resultados.
  3. Enviar o comando e o token de integridade ao servidor usando uma solicitação HTTP POST.
  4. Processar e mostrar os resultados.

Adicione o código abaixo à função integrityCommand vazia:

        // Set our state to working to trigger a switch to the waiting UI
        _serverState.emit(ServerState(
            ServerStatus.SERVER_STATUS_WORKING))
        // Request a fresh random from the server as part
        // of the nonce we will generate for the request
        var integrityRandom = IntegrityRandom("", 0U)
        try {
            val returnedRandom = httpClient.get<IntegrityRandom>(
                SERVER_URL + "/getRandom")
            integrityRandom = returnedRandom
        } catch (t: Throwable) {
            Log.d(TAG, "getRandom exception " + t.message)
            _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_UNREACHABLE,
                IntegrityRandom("", 0U)))
        }

        // If we have a random, we are ready to request an integrity token
        if (!integrityRandom.random.isNullOrEmpty()) {
            val nonceString = GenerateNonce.GenerateNonceString(TEST_COMMAND,
                integrityRandom.random)
            // Create an instance of an IntegrityManager
            val integrityManager = IntegrityManagerFactory.create(context)

            // Use the nonce to configure a request for an integrity token
            try {
                val integrityTokenResponse: Task<IntegrityTokenResponse> =
                    integrityManager.requestIntegrityToken(
                        IntegrityTokenRequest.builder()
                            .setNonce(nonceString)
                            .build()
                    )
                // Wait for the integrity token to be generated
                integrityTokenResponse.await()
                if (integrityTokenResponse.isSuccessful && integrityTokenResponse.result != null) {
                    // Post the received token to our server
                    postCommand(integrityTokenResponse.result!!.token(), integrityRandom)
                } else {
                    Log.d(TAG, "requestIntegrityToken failed: " +
                            integrityTokenResponse.result.toString())
                    _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_FAILED_TO_GET_TOKEN))
                }
            } catch (t: Throwable) {
                Log.d(TAG, "requestIntegrityToken exception " + t.message)
                _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_FAILED_TO_GET_TOKEN))
            }
        }

O código para POST (enviar) o comando para seu servidor foi dividido em uma função postCommand separada. Adicione o código abaixo à função postCommand vazia:

        try {
            val commandResult = httpClient.post<CommandResult>(
                SERVER_URL + "/performCommand") {
                contentType(ContentType.Application.Json)
                body = ServerCommand(TEST_COMMAND, tokenString)
            }
            _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_REACHABLE,
                integrityRandom,
                commandResult.diagnosticMessage,
                commandResult.commandSuccess,
                commandResult.expressToken))
        } catch (t: Throwable) {
            Log.d(TAG, "performCommand exception " + t.message)
            _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_UNREACHABLE))
        }

Resolva todas as importações ausentes e salve o arquivo.

19. Mostrar os resultados (Kotlin)

Atualizar a interface com o resumo do veredito

No momento, a interface mostra o texto do marcador de posição para o resumo do veredito de integridade. Para substituir o marcador de posição pelo resumo real, siga estas etapas:

  1. No painel Project do Android Studio, abra o arquivo MainView.kt em start/app/src/main/java/com.example.google.codelab.playintegritykotlin/ui/main.
  2. No final da função MainUI, localize e substitua a instrução text = "None", por este código:
                        text = state.serverState.serverVerdict,
  1. Resolva todas as importações ausentes e salve o arquivo.

20. Criar e fazer upload (Kotlin)

Criar e configurar um keystore para o app

O Android exige que todos os apps sejam assinados digitalmente com um certificado antes de serem instalados ou atualizados em um dispositivo.

Criaremos um Keystore para o app neste codelab. Se você estiver publicando uma atualização de um jogo atual, reutilize o mesmo Keystore que usou para lançar versões anteriores do app.

Criar um keystore e um pacote de app de lançamento

Siga as etapas em Keystore com o Android Studio para criar um keystore e usá-lo para gerar um build de lançamento assinado do jogo. No Android Studio, escolha Generate Signed Bundle / APK no menu Build para iniciar o processo de build. Escolha App Bundle quando aparecer a opção para selecionar um Android App Bundle ou APK. No final do processo, você terá um arquivo .aab adequado para upload ao Google Play Console.

Fazer upload para o Play Console

Depois de criar um arquivo de pacote de app, faça upload dele para o Play Console. Recomendamos usar a faixa de teste interno para facilitar o acesso rápido ao build.

Executar o build de teste

Agora, faça o download e execute o build de teste na Play Store. A seleção do botão Call server with integrity check vai funcionar e resultará nesta tela:

3291795e192396c9.png

21. Parabéns

Parabéns! Você adicionou a Play Integrity a um app Android.

Leia mais