Możesz uzyskać informacje o tym, jak Twoja aplikacja i jej zależności uzyskują dostęp do prywatnych danych użytkowników, wykonując audyt dostępu do danych. Ten proces jest dostępny na urządzeniach z Androidem 11 (poziom interfejsu API 30) i nowszym i umożliwia lepsze wykrywanie potencjalnie nieoczekiwanego dostępu do danych. Aplikacja może zarejestrować instancję AppOpsManager.OnOpNotedCallback
, która może wykonywać działania za każdym razem, gdy wystąpi jedno z tych zdarzeń:
- Kod Twojej aplikacji uzyskuje dostęp do danych prywatnych. Aby dowiedzieć się, która część logiczna aplikacji wywołała zdarzenie, możesz sprawdzić dostęp do danych za pomocą tagu atrybucji.
- Kod w bibliotece zależnej lub pakiecie SDK uzyskuje dostęp do danych prywatnych.
Sprawdzanie dostępu do danych jest wywoływane w wątku, w którym odbywa się proces żądania danych. Oznacza to, że jeśli zewnętrzny pakiet SDK lub biblioteka w Twojej aplikacji wywołuje interfejs API, który ma dostęp do danych prywatnych, audyt dostępu do danych umożliwia OnOpNotedCallback
sprawdzenie informacji o tym wywołaniu. Zazwyczaj ten obiekt metody wywołania zwrotnego może określić, czy wywołanie pochodzi z aplikacji, czy z pakietu SDK, na podstawie bieżącego stanu aplikacji, np. ścieżki stosu bieżącego wątku.
Rejestrowanie dostępu do danych
Aby przeprowadzić kontrolę dostępu do danych za pomocą instancji AppOpsManager.OnOpNotedCallback
, zaimplementuj logikę wywołania zwrotnego w komponencie, w którym chcesz przeprowadzić kontrolę dostępu do danych, np. w metodzie onCreate()
aktywności lub onCreate()
aplikacji.
Ten fragment kodu definiuje funkcję AppOpsManager.OnOpNotedCallback
do sprawdzania dostępu do danych w ramach pojedynczej aktywności:
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); } }
Metody onAsyncNoted()
i onSelfNoted()
są wywoływane w określonych sytuacjach:
onAsyncNoted()
zostaje wywołana, jeśli dostęp do danych nie jest uzyskiwany podczas wywołania interfejsu API aplikacji. Najczęstszym przykładem jest sytuacja, gdy aplikacja rejestruje detektory zdarzeń, a dostęp do danych jest uzyskiwany za każdym razem, gdy wywoływane jest wywołanie zwrotne detektorów.Argument
AsyncNotedOp
przekazany do funkcjionAsyncNoted()
zawiera metodę o nazwiegetMessage()
. Ta metoda zapewnia więcej informacji o dostępie do danych. W przypadku wywołań zwrotnych lokalizacji wiadomość zawiera hasz tożsamości systemowej odbiorcy.onSelfNoted()
jest wywoływany w bardzo rzadkich przypadkach, gdy aplikacja przekazuje swój własny identyfikator UID do funkcjinoteOp()
.
Sprawdzanie dostępu do danych według tagu atrybucji
Aplikacja może mieć kilka podstawowych zastosowań, np. umożliwiać użytkownikom robienie zdjęć i udostępnianie ich kontaktom. Jeśli opracowujesz aplikację wielofunkcyjną, możesz zastosować tag atrybucji do każdej jej części podczas sprawdzania dostępu do danych. Kontekst attributionTag
jest zwracany w obiektach przekazywanych do wywołań funkcji onNoted()
.
Dzięki temu łatwiej będzie Ci śledzić dostęp do danych w logicznych częściach kodu.
Aby zdefiniować tagi atrybucji w aplikacji, wykonaj czynności opisane w tych sekcjach.
Deklarowanie tagów atrybucji w pliku manifestu
Jeśli Twoja aplikacja jest przeznaczona na Androida 12 (poziom interfejsu API 31) lub nowszego, musisz zadeklarować tagi atrybucji w pliku manifestu aplikacji, używając formatu pokazanego w tym fragmencie kodu. Jeśli spróbujesz użyć tagu atrybucji, którego nie zadeklarujesz w pliku manifestu aplikacji, system utworzy dla Ciebie tag null
i zarejestruje wiadomość w 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>
Tworzenie tagów atrybucji
W onCreate()
metodzie działania, w której uzyskujesz dostęp do danych, np. w działaniu, w którym żądasz lokalizacji lub uzyskujesz dostęp do listy kontaktów użytkownika, wywołaj metodęcreateAttributionContext()
, przekazując tag atrybucji, który chcesz powiązać z częścią aplikacji.
Ten fragment kodu pokazuje, jak utworzyć tag atrybucji dla części aplikacji, która umożliwia udostępnianie zdjęć i lokalizacji:
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. } } }
Dołączanie tagów atrybucji w dziennikach dostępu
Zaktualizuj wywołanie zwrotne AppOpsManager.OnOpNotedCallback
, aby dzienniki aplikacji zawierały nazwy zdefiniowanych przez Ciebie tagów atrybucji.
Ten fragment kodu pokazuje zaktualizowaną logikę, która rejestruje tagi atrybucji:
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); } }