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 (API de nível 30) 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 dos seguintes 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 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 acesso de dados
Para executar 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()
da atividade.
O snippet de código a seguir 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 callback do 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 capturem fotos e compartilhem essas fotos com os contatos. Se você desenvolver um app para várias finalidades, é possível aplicar uma tag de atribuição em cada parte do app quando fizer a auditoria de acesso a dados. O contexto attributionTag
é retornado novamente 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 a seguir.
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 a seguir 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); } }