Vous pouvez obtenir des informations sur la manière dont votre application et ses dépendances accèdent aux données privées des utilisateurs en effectuant un audit des accès aux données. Ce processus, disponible sur les appareils équipés d'Android 11 (niveau d'API 30) ou version ultérieure, vous permet de mieux identifier les éventuels accès non prévus à des données. Votre application peut enregistrer une instance de AppOpsManager.OnOpNotedCallback
, qui peut effectuer des actions chaque fois que l'un des événements suivants se produit :
- Le code de votre application accède à des données privées. Pour vous aider à déterminer la partie logique de votre application qui a appelé l'événement, vous pouvez auditer l'accès aux données par balise d'attribution.
- Le code d'une bibliothèque ou d'un SDK dépendants accède à des données privées.
L'audit des accès aux données est appelé sur le thread où la requête de données a lieu. Cela signifie que si un SDK ou une bibliothèque tiers dans votre application appelle une API qui accède à des données privées, l'audit des accès aux données permet à votre OnOpNotedCallback
d'examiner les informations concernant l'appel. Généralement, cet objet de rappel peut savoir si l'appel provient de votre application ou du SDK en consultant l'état actuel de l'application, comme la trace de la pile du thread actuel.
Journaliser l'accès aux données
Pour effectuer un audit des accès aux données à l'aide d'une instance de AppOpsManager.OnOpNotedCallback
, implémentez la logique de rappel dans le composant où vous souhaitez auditer l'accès aux données, par exemple dans la méthode onCreate()
de l'activité ou dans la méthode onCreate()
d'une application.
L'extrait de code suivant définit un AppOpsManager.OnOpNotedCallback
pour auditer l'accès aux données au sein d'une seule activité :
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); } }
Les méthodes onAsyncNoted()
et onSelfNoted()
sont appelées dans des situations spécifiques :
onAsyncNoted()
est appelé si l'accès aux données n'a pas lieu pendant l'appel d'API de votre application. L'exemple le plus courant est lorsque votre application enregistre un écouteur et que l'accès aux données se produit chaque fois que le rappel de l'écouteur est appelé.L'argument
AsyncNotedOp
transmis àonAsyncNoted()
contient une méthode appeléegetMessage()
. Cette méthode fournit davantage d'informations sur l'accès aux données. Dans le cas des rappels d'emplacement, le message contient le hachage d'identité système de l'écouteur.onSelfNoted()
est appelé dans les rares cas où une application transmet son propre UID ànoteOp()
.
Auditer l'accès aux données par balise d'attribution
Votre application peut présenter plusieurs cas d'utilisation principaux, par exemple permettre aux utilisateurs de prendre des photos et de les partager avec leurs contacts. Si vous développez une application polyvalente, vous pouvez appliquer une balise d'attribution à chaque partie de l'application lorsque vous auditez l'accès aux données. Le contexte attributionTag
est renvoyé dans les objets transmis aux appels à onNoted()
.
Cela vous permet de retracer plus facilement l'accès aux données vers des parties logiques de votre code.
Pour définir des balises d'attribution dans votre application, procédez comme indiqué dans les sections suivantes.
Déclarer des balises d'attribution dans le fichier manifeste
Si votre application cible Android 12 (niveau d'API 31) ou une version ultérieure, vous devez déclarer les balises d'attribution dans le fichier manifeste de votre application, en utilisant le format indiqué dans l'extrait de code suivant. Si vous tentez d'utiliser une balise d'attribution que vous ne déclarez pas dans le fichier manifeste de votre application, le système crée une balise null
et consigne un message dans 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>
Créer des balises d'attribution
Dans la méthode onCreate()
de l'activité où vous accédez aux données, telle que l'activité pour laquelle vous demandez la position ou accédez à la liste des contacts de l'utilisateur, appelez createAttributionContext()
en transmettant la balise d'attribution que vous souhaitez associer à une partie de votre application.
L'extrait de code suivant montre comment créer une balise d'attribution pour la partie "partage de position des photos" d'une application :
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. } } }
Inclure des balises d'attribution dans les journaux d'accès
Mettez à jour votre rappel AppOpsManager.OnOpNotedCallback
afin que les journaux de votre application incluent les noms des balises d'attribution que vous avez définies.
L'extrait de code suivant montre une nouvelle logique qui consigne les balises d'attribution :
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); } }