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 paraonAsyncNoted()
contém um método chamadogetMessage()
. 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 paranoteOp()
.
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); } }