Аудит доступа к данным

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