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 aplikacji uzyskuje dostęp do danych prywatnych. Aby określić, która logiczna część aplikacji wywołuje 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. Zwykle 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 kontrolować dostęp do danych, np. w metodzie onCreate() aktywności lub aplikacji onCreate().

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, w której aplikacja rejestruje odbiornik i dostęp do danych ma miejsce za każdym razem, gdy wywoływane jest wywołanie zwrotne detektora.

    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 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. Obiekty przekazywane do wywołań funkcji onNoted() zwracają z powrotem kontekst attributionTag. 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 kierowana na Androida 12 (poziom interfejsu API 31) lub nowszego, musisz zadeklarować tagi atrybucji w pliku manifestu aplikacji, korzystając z formatu podanego poniżej we 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(), podając tag atrybucji, który chcesz powiązać z częścią aplikacji.

Ten fragment kodu pokazuje, jak utworzyć tag atrybucji na potrzeby funkcji udostępniania lokalizacji zdjęć w aplikacji:

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