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