Audit akses data

Untuk memberikan lebih banyak transparansi terkait cara aplikasi dan dependensinya mengakses data pribadi dari pengguna, Android 11 memperkenalkan audit akses data. Dengan memiliki laporan dari proses ini, Anda dapat mengidentifikasi dan memperbaiki potensi akses data yang tidak terduga secara lebih baik. Aplikasi Anda dapat mendaftarkan instance AppOpsManager.OnOpNotedCallback, yang dapat menjalankan tindakan setiap kali salah satu peristiwa berikut terjadi:

  • Kode aplikasi Anda akan mengakses data pribadi. Untuk membantu Anda menentukan bagian logis aplikasi yang memanggil peristiwa, Anda dapat mengaudit akses data dengan tag atribusi.
  • Kode di SDK atau library dependen akan mengakses data pribadi.

Audit akses data dipanggil pada thread tempat permintaan data dilakukan. Ini berarti, jika SDK atau library pihak ketiga di aplikasi Anda memanggil API yang mengakses data pribadi, audit akses data akan memungkinkan OnOpNotedCallback Anda memeriksa informasi tentang panggilan tersebut. Biasanya, objek callback ini dapat memberi tahu apakah panggilan berasal dari aplikasi Anda atau SDK dengan melihat status aplikasi saat ini, seperti pelacakan tumpukan thread saat ini.

Mencatat log akses data

Untuk menjalankan audit akses data menggunakan instance AppOpsManager.OnOpNotedCallback, implementasikan logika callback di komponen tempat Anda ingin mengaudit akses data, seperti dalam metode onCreate() aktivitas.

Cuplikan kode berikut akan menentukan AppOpsManager.OnOpNotedCallback untuk mengaudit akses data dalam aktivitas tunggal:

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

Metode onAsyncNoted() dan onSelfNoted() akan dipanggil dalam situasi tertentu:

  • onAsyncNoted() akan dipanggil jika akses data tidak terjadi selama panggilan API aplikasi Anda. Contoh yang paling umum adalah saat aplikasi Anda mendaftarkan pemroses dan akses data terjadi setiap kali callback pemroses dipanggil.

    Argumen AsyncNotedOp yang diteruskan ke onAsyncNoted() berisi metode yang disebut getMessage(). Metode ini akan memberikan informasi selengkapnya tentang akses data. Pesan dengan callback lokasi akan berisi hash identitas sistem pemroses.

  • onSelfNoted() akan sangat jarang dipanggil ketika aplikasi meneruskan UID-nya sendiri ke noteOp().

Mengaudit akses data dengan tag atribusi

Aplikasi Anda mungkin memiliki beberapa kasus penggunaan utama, seperti mengizinkan pengguna mengambil foto dan membagikan foto tersebut kepada kontak mereka. Jika Anda mengembangkan aplikasi multitujuan semacam itu, Anda dapat menerapkan tag atribusi ke setiap bagian aplikasi saat mengaudit akses datanya. Konteks attributionTag dikembalikan ke objek yang diteruskan ke panggilan untuk onNoted(). Hal ini akan mempermudah Anda melacak akses data kembali ke bagian logis kode Anda.

Untuk menetapkan tag atribusi dalam aplikasi Anda, selesaikan langkah-langkah di bagian berikut ini.

Membuat tag atribusi

Dalam metode onCreate() dari aktivitas tempat Anda mengakses data, seperti aktivitas tempat Anda meminta lokasi atau mengakses daftar kontak pengguna, panggil createAttributionContext(), yang meneruskan tag atribusi yang ingin Anda kaitkan dengan bagian dari aplikasi.

Cuplikan kode berikut akan menunjukkan cara membuat tag atribusi untuk bagian "berbagi lokasi foto" pada aplikasi:

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.
        }
    }
}

Menyertakan tag atribusi di log akses

Perbarui callback AppOpsManager.OnOpNotedCallback agar log aplikasi menyertakan nama tag atribusi yang telah Anda tetapkan.

Cuplikan kode berikut menunjukkan logika terbaru yang membuat log tag atribusi:

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