Mit der Prüfung von Datenzugriff können Sie nachvollziehen, wie Ihre App und ihre Abhängigkeiten auf private Daten von Nutzern zugreifen. Mit diesem Prozess, der auf Geräten mit Android 11 (API-Level 30) und höher verfügbar ist, kannst du potenziell unerwartete Datenzugriffe besser erkennen. Ihre App kann eine Instanz von AppOpsManager.OnOpNotedCallback registrieren, die jedes Mal Aktionen ausführen kann, wenn eines der folgenden Ereignisse eintritt:
- Der Code Ihrer App greift auf private Daten zu. Damit Sie feststellen können, welcher logische Teil Ihrer App das Ereignis ausgelöst hat, können Sie den Datenzugriff nach Attributions-Tag prüfen.
- Code in einer abhängigen Bibliothek oder einem abhängigen SDK greift auf private Daten zu.
Die Prüfung von Datenzugriff wird in dem Thread aufgerufen, in dem die Datenanfrage stattfindet. Wenn also ein SDK oder eine Bibliothek eines Drittanbieters in deiner App eine API aufruft, die auf private Daten zugreift, kannst du mit der Prüfung von Datenzugriffen Informationen zum Aufruf in deinem OnOpNotedCallback prüfen. In der Regel kann dieses Callback-Objekt anhand des aktuellen Status der App, z. B. des Stack-Trace des aktuellen Threads, erkennen, ob der Aufruf von Ihrer App oder dem SDK stammt.
Datenzugriff protokollieren
Wenn Sie die Prüfung von Datenzugriffen mit einer Instanz von AppOpsManager.OnOpNotedCallback durchführen möchten, implementieren Sie die Callback-Logik in der Komponente, in der Sie den Datenzugriff prüfen möchten, z. B. in der Methode onCreate() einer Aktivität oder in der Methode onCreate() einer Anwendung.
Im folgenden Code-Snippet wird ein AppOpsManager.OnOpNotedCallback für die Prüfung des Datenzugriffs innerhalb einer einzelnen Aktivität definiert:
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); } }
Die Methoden onAsyncNoted() und onSelfNoted() werden in bestimmten Situationen aufgerufen:
onAsyncNoted()wird aufgerufen, wenn der Datenzugriff nicht während des API-Aufrufs Ihrer App erfolgt. Das häufigste Beispiel ist, wenn Ihre App einen Listener registriert und der Datenzugriff jedes Mal erfolgt, wenn der Callback des Listeners aufgerufen wird.Das Argument
AsyncNotedOp, das anonAsyncNoted()übergeben wird, enthält eine Methode namensgetMessage(). Diese Methode bietet mehr Informationen zum Datenzugriff. Bei den Standort-Callbacks enthält die Nachricht den Systemidentitäts-Hash des Listeners.onSelfNoted()wird in dem sehr seltenen Fall aufgerufen, dass eine App ihre eigene UID annoteOp()übergibt.
Datenzugriff nach Attributions-Tag prüfen
Deine App kann mehrere primäre Anwendungsfälle haben, z. B. das Aufnehmen von Fotos und das Teilen dieser Fotos mit Kontakten. Wenn Sie eine Mehrzweck-App entwickeln, können Sie bei der Prüfung des Datenzugriffs auf jeden Teil Ihrer App ein Attributions-Tag anwenden. Der Kontext attributionTag wird in den Objekten zurückgegeben, die an die Aufrufe von onNoted() übergeben werden.
So können Sie Datenzugriffe leichter auf logische Teile Ihres Codes zurückführen.
Führen Sie die Schritte in den folgenden Abschnitten aus, um Attributions-Tags in Ihrer Anwendung zu definieren.
Attributions-Tags im Manifest deklarieren
Wenn deine App auf Android 12 (API-Level 31) oder höher ausgerichtet ist, musst du in der Manifestdatei deiner App Attributions-Tags im Format des folgenden Code-Snippets deklarieren. Wenn Sie versuchen, ein Attributions-Tag zu verwenden, das Sie nicht in der Manifestdatei Ihrer App deklariert haben, erstellt das System ein null-Tag für Sie und protokolliert eine Nachricht in 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>
Attributions-Tags erstellen
Rufe in der Methode onCreate() der Aktivität, in der du auf Daten zugreifst, z. B. in der Aktivität, in der du den Standort anforderst oder auf die Kontaktliste des Nutzers zugreifst, createAttributionContext() auf und übergib das Attributions-Tag, das du mit einem Teil deiner App verknüpfen möchtest.
Das folgende Code-Snippet zeigt, wie Sie ein Attributions-Tag für einen Teil einer App erstellen, in dem Fotos mit Standortangabe geteilt werden:
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. } } }
Attributions-Tags in Zugriffslogs einfügen
Aktualisieren Sie den AppOpsManager.OnOpNotedCallback-Callback, damit die Logs Ihrer App die Namen der von Ihnen definierten Attributions-Tags enthalten.
Das folgende Code-Snippet zeigt die aktualisierte Logik, mit der Attributions-Tags protokolliert werden:
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.setOnOpNotedCallback(getMainExecutor(), appOpsCallback); } }