Aby dowiedzieć się, jak Twoja aplikacja i jej zależności uzyskują dostęp do danych prywatnych użytkowników, możesz przeprowadzić sprawdzanie dostępu do danych. Ten proces, dostępny na urządzeniach z Androidem 11 (poziom interfejsu API 30) i nowszym, pozwala lepiej identyfikować potencjalnie nieoczekiwany dostęp do danych. Twoja aplikacja może zarejestrować instancję
obiektu AppOpsManager.OnOpNotedCallback, która może wykonywać
działania za każdym razem, gdy wystąpi jedno z tych zdarzeń:
- Kod aplikacji uzyskuje dostęp do danych prywatnych. Aby pomóc sobie w określeniu, która logiczna część aplikacji wywołała zdarzenie, możesz sprawdzić dostęp do danych według 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 następuje żądanie danych. Oznacza to, że jeśli pakiet SDK lub biblioteka innej firmy w Twojej aplikacji wywoła interfejs API, który uzyskuje dostęp do danych prywatnych, sprawdzanie dostępu do danych umożliwi Twojemu OnOpNotedCallback sprawdzenie informacji o wywołaniu. Zazwyczaj ten obiekt wywołania zwrotnego może określić, czy wywołanie pochodzi z Twojej aplikacji czy z pakietu SDK, sprawdzając bieżący stan aplikacji, np. zrzut stosu bieżącego wątku.
Logowanie dostępu do danych
Aby przeprowadzić sprawdzanie dostępu do danych za pomocą instancji
AppOpsManager.OnOpNotedCallback, zaimplementuj logikę wywołania zwrotnego w komponencie
gdzie chcesz sprawdzić dostęp do danych, np. w metodzie
onCreate()
aktywności lub metodzie
onCreate() aplikacji.
Ten fragment kodu definiuje AppOpsManager.OnOpNotedCallback do sprawdzania dostępu do danych w ramach jednej 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()jest wywoływana, jeśli dostęp do danych nie następuje podczas wywołania interfejsu API aplikacji. Najczęstszym przykładem jest sytuacja, gdy aplikacja rejestruje odbiornik, a dostęp do danych następuje za każdym razem, gdy wywoływane jest wywołanie zwrotne odbiornika.Argument
AsyncNotedOpprzekazywany doonAsyncNoted()zawiera metodę o nazwiegetMessage(). Ta metoda zawiera więcej informacji o dostępie do danych. W przypadku wywołań zwrotnych lokalizacji wiadomość zawiera system-identity-hash odbiornika.onSelfNoted()jest wywoływana w bardzo rzadkich przypadkach, gdy aplikacja przekazuje własny UID donoteOp().
Sprawdzanie dostępu do danych według tagu atrybucji
Twoja aplikacja może mieć kilka głównych przypadków użycia, np. umożliwiać użytkownikom robienie zdjęć i udostępnianie ich kontaktom. Jeśli tworzysz aplikację wielofunkcyjną, możesz zastosować tag atrybucji do każdej części aplikacji podczas sprawdzania jej dostępu do danych. Kontekst attributionTag jest zwracany
w obiektach przekazywanych do wywołań onNoted().
Ułatwia to śledzenie dostępu do danych w logicznych częściach kodu.
Aby zdefiniować tagi atrybucji w aplikacji, wykonaj czynności opisane w poniższych 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 w formacie pokazanym w tym fragmencie kodu. Jeśli spróbujesz użyć tagu atrybucji, który nie jest zadeklarowany w pliku manifestu aplikacji, system utworzy tag null i zarejestruje komunikat 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 metodzie
onCreate()
aktywności, w której uzyskujesz dostęp do danych, np. aktywności, w której
prosisz o lokalizację lub dostęp do listy kontaktów użytkownika, wywołaj
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 lokalizacji zdjęć:
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. } } }
Uwzględnianie tagów atrybucji w logach dostępu
Zaktualizuj wywołanie zwrotne AppOpsManager.OnOpNotedCallback, aby logi aplikacji zawierały nazwy zdefiniowanych 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); } }