Вы можете получить представление о том, как ваше приложение и его зависимости получают доступ к личным данным пользователей, выполнив аудит доступа к данным . Этот процесс, доступный на устройствах под управлением 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) }
Ява
@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. } }
Ява
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) } }
Ява
@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); } }