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

Вы можете получить представление о том, как ваше приложение и его зависимости получают доступ к личным данным пользователей, выполнив аудит доступа к данным . Этот процесс, доступный на устройствах под управлением Android 11 (уровень API 30) и выше, позволяет лучше выявлять потенциально неожиданный доступ к данным. Ваше приложение может зарегистрировать экземпляр AppOpsManager.OnOpNotedCallback , который может выполнять действия каждый раз, когда происходит одно из следующих событий:

Аудит доступа к данным запускается в потоке, где происходит запрос данных. Это означает, что если сторонний SDK или библиотека в вашем приложении вызывает API, который обращается к закрытым данным, аудит доступа к данным позволяет вашему объекту OnOpNotedCallback изучить информацию о вызове. Обычно этот объект обратного вызова может определить, поступил ли вызов из вашего приложения или из SDK, проанализировав текущий статус приложения, например, трассировку стека текущего потока.

Журнал доступа к данным

To perform data access auditing using an instance of AppOpsManager.OnOpNotedCallback , implement the callback logic in the component where you intend to audit data access, such as within an activity's onCreate() method or an application's onCreate() method.

Приведённый ниже фрагмент кода определяет обработчик 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() is called if the data access doesn't happen during your app's API call. The most common example is when your app registers a listener and the data access happens each time the listener's callback is invoked.

    The AsyncNotedOp argument that's passed into onAsyncNoted() contains a method called getMessage() . This method provides more information about the data access. In the case of the location callbacks, the message contains the system-identity-hash of the listener.

  • onSelfNoted() вызывается в крайне редких случаях, когда приложение передает свой собственный UID в noteOp() .

Проверка доступа к данным по тегу атрибуции

Ваше приложение может иметь несколько основных сценариев использования, например, позволять пользователям делать фотографии и делиться ими со своими контактами. Если вы разрабатываете многоцелевое приложение, вы можете применить тег атрибуции к каждой части вашего приложения при проверке доступа к данным. Контекст attributionTag возвращается в объектах, передаваемых в вызовы метода onNoted() . Это помогает вам проще отслеживать доступ к данным до логических частей вашего кода.

Чтобы определить теги атрибуции в вашем приложении, выполните действия, описанные в следующих разделах.

Укажите теги атрибуции в манифесте.

If your app targets Android 12 (API level 31) or higher, you must declare attribution tags in your app's manifest file, using the format shown in the following code snippet. If you attempt to use an attribution tag that you don't declare in your app's manifest file, the system creates a null tag for you and logs a message in 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>

Создать теги атрибуции

In the onCreate() method of the activity where you access data, such as the activity where you request location or access the user's list of contacts, call createAttributionContext() , passing in the attribution tag that you wish to associate with a part of your app.

Следующий фрагмент кода демонстрирует, как создать тег атрибуции для функции обмена фотографиями и местоположением в приложении:

Котлин

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.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
    }
}