Вы можете получить представление о том, как ваше приложение и его зависимости получают доступ к личным данным пользователей, выполнив аудит доступа к данным . Этот процесс, доступный на устройствах под управлением Android 11 (уровень API 30) и выше, позволяет лучше выявлять потенциально неожиданный доступ к данным. Ваше приложение может зарегистрировать экземпляр AppOpsManager.OnOpNotedCallback , который может выполнять действия каждый раз, когда происходит одно из следующих событий:
- Код вашего приложения обращается к закрытым данным. Чтобы определить, какая логическая часть вашего приложения вызвала событие, вы можете провести аудит доступа к данным с помощью тега атрибуции .
- Код в зависимой библиотеке или SDK обращается к закрытым данным.
Аудит доступа к данным запускается в потоке, где происходит запрос данных. Это означает, что если сторонний SDK или библиотека в вашем приложении вызывает API, который обращается к закрытым данным, аудит доступа к данным позволяет вашему объекту OnOpNotedCallback изучить информацию о вызове. Обычно этот объект обратного вызова может определить, поступил ли вызов из вашего приложения или из SDK, проанализировав текущий статус приложения, например, трассировку стека текущего потока.
Журнал доступа к данным
Для выполнения аудита доступа к данным с использованием экземпляра AppOpsManager.OnOpNotedCallback реализуйте логику обратного вызова в компоненте, где вы планируете проводить аудит доступа к данным, например, в методе onCreate() активности или в методе onCreate() приложения.
Приведённый ниже фрагмент кода определяет обработчик AppOpsManager.OnOpNotedCallback для аудита доступа к данным в рамках одной активности:
Котлин
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); } }
Методы onAsyncNoted() и onSelfNoted() вызываются в определенных ситуациях:
onAsyncNoted()вызывается, если доступ к данным происходит не во время вызова API вашего приложения. Наиболее распространенный пример — когда ваше приложение регистрирует слушатель, и доступ к данным происходит каждый раз, когда вызывается функция обратного вызова этого слушателя.Аргумент
AsyncNotedOp, передаваемый вonAsyncNoted()содержит методgetMessage(). Этот метод предоставляет дополнительную информацию о доступе к данным. В случае коллбэков, связанных с местоположением, сообщение содержит системный хэш идентификатора слушателя.onSelfNoted()вызывается в крайне редких случаях, когда приложение передает свой собственный UID вnoteOp().
Проверка доступа к данным по тегу атрибуции
Ваше приложение может иметь несколько основных сценариев использования, например, позволять пользователям делать фотографии и делиться ими со своими контактами. Если вы разрабатываете многоцелевое приложение, вы можете применить тег атрибуции к каждой части вашего приложения при проверке доступа к данным. Контекст attributionTag возвращается в объектах, передаваемых в вызовы метода onNoted() . Это помогает вам проще отслеживать доступ к данным до логических частей вашего кода.
Чтобы определить теги атрибуции в вашем приложении, выполните действия, описанные в следующих разделах.
Укажите теги атрибуции в манифесте.
Если ваше приложение ориентировано на Android 12 (уровень API 31) или выше, необходимо указать теги атрибуции в файле манифеста приложения, используя формат, показанный в следующем фрагменте кода. Если вы попытаетесь использовать тег атрибуции, который не указан в файле манифеста приложения, система создаст для вас null тег и запишет сообщение в 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>
Создать теги атрибуции
В методе onCreate() активности, к которой вы обращаетесь к данным, например, активности, где вы запрашиваете местоположение или получаете доступ к списку контактов пользователя, вызовите createAttributionContext() , передав в качестве параметра тег атрибуции, который вы хотите связать с частью вашего приложения.
Следующий фрагмент кода демонстрирует, как создать тег атрибуции для функции обмена фотографиями и местоположением в приложении:
Котлин
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. } } }
Включайте теги атрибуции в журналы доступа.
Обновите функцию обратного вызова AppOpsManager.OnOpNotedCallback таким образом, чтобы в логах вашего приложения отображались имена определенных вами тегов атрибуции.
Следующий фрагмент кода демонстрирует обновленную логику, которая регистрирует теги атрибуции:
Котлин
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); } }