Kontrolowanie dostępu do danych

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()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 funkcji onAsyncNoted() zawiera metodę o nazwie getMessage(). 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 funkcji noteOp().

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

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);
    }
}