Вы можете узнать, как ваше приложение и его зависимости получают доступ к личным данным пользователей, выполнив проверку доступа к данным. Этот процесс доступен на устройствах с Android 11 (уровень API 30) и более поздних версий. Он позволяет лучше выявлять потенциально неожиданный доступ к данным. Приложение может зарегистрировать экземпляр AppOpsManager.OnOpNotedCallback, который будет выполнять действия при каждом из следующих событий:
- Код вашего приложения получает доступ к личным данным. Чтобы определить, какая логическая часть приложения вызвала событие, вы можете проверить доступ к данным по тегу атрибуции.
- Код в зависимой библиотеке или SDK получает доступ к личным данным.
Проверка доступа к данным вызывается в потоке, в котором выполняется запрос данных. Это значит, что если сторонний SDK или библиотека в вашем приложении вызывает API, который обращается к личным данным, то проверка доступа к данным позволяет OnOpNotedCallback изучить информацию о вызове. Обычно этот объект обратного вызова может определить, поступил ли вызов из вашего приложения или SDK, по текущему статусу приложения, например по трассировке стека текущего потока.
Доступ к журналам данных
Чтобы выполнить проверку доступа к данным с помощью экземпляра AppOpsManager.OnOpNotedCallback, реализуйте логику обратного вызова в компоненте, в котором вы хотите выполнить проверку, например в методе onCreate() действия или методе onCreate() приложения.
В следующем фрагменте кода определяется AppOpsManager.OnOpNotedCallback для аудита доступа к данным в рамках одного действия:
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); } }
Методы 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(), передав тег атрибуции, который вы хотите связать с частью приложения.
В приведенном ниже фрагменте кода показано, как создать тег атрибуции для функции приложения, которая позволяет делиться фотографиями и данными о местоположении.
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. } } }
Как включить теги атрибуции в журналы доступа
Обновите обратный вызов AppOpsManager.OnOpNotedCallback, чтобы в журналах приложения были названия заданных вами тегов атрибуции.
В следующем фрагменте кода показана обновленная логика, которая регистрирует теги атрибуции:
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.setOnOpNotedCallback(getMainExecutor(), appOpsCallback); } }