Auditar o acesso aos dados

Você pode ver insights sobre como seu app e as dependências dele acessam dados particulares de usuários executando a auditoria de acesso a dados. Esse processo, disponível em dispositivos que executam o Android 11 (nível 30 da API) e versões mais recentes, permite identificar melhor o acesso inesperado a dados. Seu app pode registrar uma instância de AppOpsManager.OnOpNotedCallback, que pode realizar ações sempre que um destes eventos ocorrer:

  • O código do seu app acessa dados particulares. Para ajudar a determinar qual parte lógica do app invocou o evento, é possível avaliar o acesso a dados pela tag de atribuição.
  • O código em uma biblioteca dependente ou o SDK acessa dados particulares.

A auditoria de acesso a dados é invocada na linha de execução em que a solicitação de dados ocorre. Isso significa que, se um SDK ou uma biblioteca de terceiros no app chamar uma API que acessa dados particulares, a auditoria de acesso a dados vai permitir que OnOpNotedCallback examine informações sobre a chamada. Normalmente, esse objeto de callback pode dizer se a chamada veio do app ou do SDK, observando o status atual do app, como o stack trace da linha de execução atual.

Registrar o acesso de dados

Para realizar a auditoria de acesso a dados usando uma instância de AppOpsManager.OnOpNotedCallback, implemente a lógica de callback no componente em que você pretende auditar o acesso aos dados, por exemplo, dentro de um método onCreate() ou o método onCreate() de um aplicativo.

O snippet de código abaixo define um AppOpsManager.OnOpNotedCallback para a auditoria de acesso a dados em uma única atividade:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {
        private fun logPrivateDataAccess(opCode: String, trace: String) {
            Log.i(MY_APP_TAG, "Private data accessed. " +
                    "Operation: $opCode\nStack Trace:\n$trace")
        }

        override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
            logPrivateDataAccess(
                    syncNotedAppOp.op, Throwable().stackTrace.toString())
        }

        override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
            logPrivateDataAccess(
                    syncNotedAppOp.op, Throwable().stackTrace.toString())
        }

        override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
            logPrivateDataAccess(asyncNotedAppOp.op, asyncNotedAppOp.message)
        }
    }

    val appOpsManager =
            getSystemService(AppOpsManager::class.java) as AppOpsManager
    appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback)
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    AppOpsManager.OnOpNotedCallback appOpsCallback =
            new AppOpsManager.OnOpNotedCallback() {
        private void logPrivateDataAccess(String opCode, String trace) {
            Log.i(MY_APP_TAG, "Private data accessed. " +
                    "Operation: $opCode\nStack Trace:\n$trace");
        }

        @Override
        public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
            logPrivateDataAccess(asyncNotedAppOp.getOp(),
                    asyncNotedAppOp.getMessage());
        }
    };

    AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
    if (appOpsManager != null) {
        appOpsManager.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
    }
}

Os métodos onAsyncNoted() e onSelfNoted() são chamados em situações específicas:

  • onAsyncNoted() será chamado se o acesso aos dados não acontecer durante a chamada da API do seu app. O exemplo mais comum é quando o app registra um listener e o acesso aos dados ocorre sempre que o callback do listener é invocado.

    O argumento AsyncNotedOp que é transmitido para onAsyncNoted() contém um método chamado getMessage(). Esse método fornece mais informações sobre o acesso aos dados. No caso dos callbacks de local, a mensagem contém o hash de identidade do sistema do listener.

  • onSelfNoted() é chamado no caso muito raro em que um app transmite o próprio UID para noteOp().

Auditar o acesso a dados por tag de atribuição

O app pode ter vários casos de uso principais, por exemplo, permitir que os usuários tirem fotos e as compartilhem com os contatos. Se você desenvolver um app para várias finalidades, poderá aplicar uma tag de atribuição em cada parte do app quando fizer a auditoria de acesso a dados. O contexto attributionTag é retornado nos objetos transmitidos às chamadas para onNoted(). Isso ajuda a rastrear com mais facilidade o acesso a dados para partes lógicas do código.

Para definir tags de atribuição no app, siga as etapas nas seções abaixo.

Declarar tags de atribuição no manifesto

Se o app for direcionado ao Android 12 (nível 31 da API) ou versões mais recentes, é necessário declarar tags de atribuição no arquivo de manifesto do app, usando o formato mostrado no snippet de código abaixo. Se você tentar usar uma tag de atribuição que não declara no arquivo de manifesto do app, o sistema vai criar uma tag null para você e registrar uma mensagem no Logcat.

<manifest ...>
    <!-- The value of "android:tag" must be a literal string, and the
         value of "android:label" must be a resource. The value of
         "android:label" is user-readable. -->
    <attribution android:tag="sharePhotos"
                 android:label="@string/share_photos_attribution_label" />
    ...
</manifest>

Criar tags de atribuição

No método onCreate() da atividade em que você acessa dados, como a atividade que solicita a localização ou acessa a lista de contatos do usuário, chame createAttributionContext(), transmitindo a tag de atribuição que você quer associar a uma parte do seu app.

O snippet de código abaixo demonstra como criar uma tag de atribuição para uma parte de "compartilhamento de local da foto" de um app:

Kotlin

class SharePhotoLocationActivity : AppCompatActivity() {
    lateinit var attributionContext: Context

    override fun onCreate(savedInstanceState: Bundle?) {
        attributionContext = createAttributionContext("sharePhotos")
    }

    fun getLocation() {
        val locationManager = attributionContext.getSystemService(
                LocationManager::class.java) as LocationManager
        // Use "locationManager" to access device location information.
    }
}

Java

public class SharePhotoLocationActivity extends AppCompatActivity {
    private Context attributionContext;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState,
            @Nullable PersistableBundle persistentState) {
        attributionContext = createAttributionContext("sharePhotos");
    }

    public void getLocation() {
        LocationManager locationManager =
                attributionContext.getSystemService(LocationManager.class);
        if (locationManager != null) {
            // Use "locationManager" to access device location information.
        }
    }
}

Incluir tags de atribuição em registros de acesso

Atualize o callback AppOpsManager.OnOpNotedCallback para que os registros do app incluam os nomes das tags de atribuição definidas.

O snippet de código a seguir mostra a lógica atualizada que registra as tags de atribuição:

Kotlin

val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {
    private fun logPrivateDataAccess(
            opCode: String, attributionTag: String, trace: String) {
        Log.i(MY_APP_TAG, "Private data accessed. " +
                    "Operation: $opCode\n " +
                    "Attribution Tag:$attributionTag\nStack Trace:\n$trace")
    }

    override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
        logPrivateDataAccess(syncNotedAppOp.op,
                syncNotedAppOp.attributionTag,
                Throwable().stackTrace.toString())
    }

    override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
        logPrivateDataAccess(syncNotedAppOp.op,
                syncNotedAppOp.attributionTag,
                Throwable().stackTrace.toString())
    }

    override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
        logPrivateDataAccess(asyncNotedAppOp.op,
                asyncNotedAppOp.attributionTag,
                asyncNotedAppOp.message)
    }
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    AppOpsManager.OnOpNotedCallback appOpsCallback =
            new AppOpsManager.OnOpNotedCallback() {
        private void logPrivateDataAccess(String opCode,
                String attributionTag, String trace) {
            Log.i("MY_APP_TAG", "Private data accessed. " +
                    "Operation: $opCode\n " +
                    "Attribution Tag:$attributionTag\nStack Trace:\n$trace");
        }

        @Override
        public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    syncNotedAppOp.getAttributionTag(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    syncNotedAppOp.getAttributionTag(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
            logPrivateDataAccess(asyncNotedAppOp.getOp(),
                    asyncNotedAppOp.getAttributionTag(),
                    asyncNotedAppOp.getMessage());
        }
    };

    AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
    if (appOpsManager != null) {
        appOpsManager.setNotedAppOpsCollector(appOpsCollector);
    }
}