Kontrolowanie dostępu do danych

Możesz dowiedzieć się, jak aplikacja i jej zależności uzyskują dostęp do prywatnych danych użytkowników, przeprowadzając sprawdzanie dostępu do danych. Ten proces, dostępny na urządzeniach z Androidem 11 (poziom interfejsu API 30) lub 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 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 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łuje interfejs API, który uzyskuje dostęp do danych prywatnych, sprawdzanie dostępu do danych pozwala OnOpNotedCallback sprawdzić informacje o 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. zrzut stosu bieżącego wątku.

Dzienniki dostępu do danych

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

Poniższy fragment kodu definiuje AppOpsManager.OnOpNotedCallback do kontrolowania 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 przez aplikację. Najczęstszym przykładem jest sytuacja, gdy 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 lokalizacji wiadomość zawiera skrót tożsamości systemowej odbiorcy.

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

Sprawdzanie dostępu do danych według tagu atrybucji

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ą, podczas sprawdzania dostępu do danych możesz zastosować tag atrybucji do każdej części aplikacji. 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 kierowana 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óry nie został zadeklarowany w pliku manifestu aplikacji, system utworzy dla Ciebie 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 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.

Poniższy fragment kodu pokazuje, jak utworzyć tag atrybucji dla części aplikacji, która umożliwia udostępnianie lokalizacji zdjęcia:

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 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.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
    }
}