Mengaudit akses ke data

Anda dapat memperoleh analisis tentang cara aplikasi dan dependensinya mengakses data pribadi dari pengguna dengan melakukan audit akses data. Proses ini, yang tersedia di perangkat yang menjalankan Android 11 (level API 30) dan yang lebih tinggi, membantu Anda mengidentifikasi potensi akses data yang tidak diinginkan dengan lebih baik. Aplikasi Anda dapat mendaftarkan instance AppOpsManager.OnOpNotedCallback yang dapat menjalankan tindakan setiap kali salah satu peristiwa berikut terjadi:

  • Kode aplikasi Anda 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 stack trace thread saat ini.

Membuat log akses data

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

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 memungkinkan pengguna mengambil foto dan membagikan foto tersebut kepada kontak mereka. Jika Anda mengembangkan aplikasi multi-tujuan, 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.

Mendeklarasikan tag atribusi dalam manifes

Jika aplikasi menargetkan Android 12 (level API 31) atau yang lebih baru, Anda harus mendeklarasikan tag atribusi dalam file manifes aplikasi, menggunakan format yang ditampilkan dalam cuplikan kode berikut. Jika Anda mencoba menggunakan tag atribusi yang tidak dideklarasikan dalam file manifes aplikasi, sistem akan membuat tag null untuk Anda dan mencatat pesan dalam 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>

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 menunjukkan cara membuat tag atribusi untuk bagian "berbagi lokasi foto" dari suatu 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

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