Kontrolowanie dostępu do danych

Dzięki audytowi dostępu do danych możesz dowiedzieć się, w jaki sposób Twoja aplikacja i jej zależności uzyskują dostęp do danych prywatnych użytkowników. 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. 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 aplikacji uzyskuje dostęp do danych prywatnych. Aby określić, która logiczna część aplikacji wywołała zdarzenie, możesz sprawdzić dostęp do danych według tagu atrybucji.
  • Kod w zależnej bibliotece 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, audyt dostępu do danych umożliwi Ci OnOpNotedCallback sprawdzenie informacji o tym wywołaniu. Zwykle ten obiekt wywołania zwrotnego może określić, czy wywołanie pochodzi z aplikacji, czy z pakietu SDK, sprawdzając bieżący stan aplikacji, np. ślad stosu bieżącego wątku.

Rejestrowanie dostępu do danych

Aby przeprowadzić audyt dostępu do danych za pomocą instancji AppOpsManager.OnOpNotedCallback, zaimplementuj logikę wywołania zwrotnego w komponencie, w którym chcesz przeprowadzić audyt dostępu do danych, np. w metodzie onCreate() aktywności lub w metodzie onCreate() aplikacji.

Ten fragment kodu definiuje AppOpsManager.OnOpNotedCallback na potrzeby audytu 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()onSelfNoted() są wywoływane w określonych sytuacjach:

  • onAsyncNoted() jest wywoływana, jeśli dostęp do danych nie nastąpi podczas wywołania interfejsu API przez aplikację. Najczęstszy przykład to sytuacja, w której aplikacja rejestruje detektor, a dostęp do danych następuje za każdym razem, gdy wywoływane jest wywołanie zwrotne detektora.

    Argument AsyncNotedOp przekazywany do funkcji onAsyncNoted() zawiera metodę o nazwie getMessage(). Ta metoda dostarcza więcej informacji o dostępie do danych. W przypadku wywołań zwrotnych dotyczących lokalizacji wiadomość zawiera hash tożsamości systemu odbiorcy.

  • onSelfNoted() jest wywoływana w bardzo rzadkich przypadkach, gdy aplikacja przekazuje swój własny identyfikator UID do funkcji noteOp().

Sprawdzanie dostępu do danych według tagu atrybucji

Aplikacja może mieć kilka głównych zastosowań, 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ń funkcji 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 sekcjach poniżej.

Deklarowanie tagów atrybucji w pliku manifestu

Jeśli Twoja aplikacja jest kierowana na Androida 12 (poziom interfejsu API 31) lub nowszego, musisz zadeklarować tagi atrybucji w pliku manifestu aplikacji, używając formatu pokazanego w poniższym fragmencie kodu. Jeśli spróbujesz użyć tagu atrybucji, którego nie zadeklarujesz w pliku manifestu aplikacji, system utworzy za 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 metodzie działania, w której uzyskujesz dostęp do danych, np. w działaniu, w którym prosisz o lokalizację lub uzyskujesz dostęp do listy kontaktów użytkownika, wywołaj createAttributionContext(), przekazując tag atrybucji, który chcesz powiązać z częścią aplikacji.onCreate()

Ten fragment kodu pokazuje, jak utworzyć tag atrybucji dla części aplikacji służącej do udostępniania 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.
        }
    }
}

Uwzględnianie tagów atrybucji w dziennikach dostępu

Zaktualizuj wywołanie zwrotne AppOpsManager.OnOpNotedCallback, aby w logach aplikacji były widoczne nazwy zdefiniowanych przez Ciebie tagów atrybucji.

Poniższy 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);
    }
}