数据访问审核

为了让应用及其依赖项访问用户私密数据的过程更加透明,Android 11 引入了数据访问审核功能。借助此流程得出的见解,您可以更好地识别和纠正可能出现的意外数据访问。您的应用可以注册 AppOpsManager.OnOpNotedCallback 实例,该实例可在每次发生以下任一事件时执行相应操作:

  • 应用的代码访问私密数据。为了帮助您确定应用的哪个逻辑部分调用了事件,您可以按归因标记审核数据访问
  • 依赖库或 SDK 中的代码访问私密数据。

数据访问审核是在发生数据请求的线程上调用的。这意味着,如果应用中的第三方 SDK 或库调用访问私密数据的 API,您的 OnOpNotedCallback 可以调用数据访问审核检查有关该调用的信息。通常,此回调对象可以通过查看应用的当前状态(例如当前线程的堆栈轨迹)以判断调用是来自您的应用还是来自 SDK。

记录数据访问

如需使用 AppOpsManager.OnOpNotedCallback 实例执行数据访问审核,请在您打算审核数据访问的组件中实现回调逻辑,例如在某个 Activity 的 onCreate() 方法中。

以下代码段定义了在单个 Activity 中用于审核数据访问的 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() 方法:

  • 如果数据访问并非发生在应用调用 API 期间,需要调用 onAsyncNoted()。最常见的一个例子就是,在您的应用注册了监听器后,每次调用该监听器的回调时都会发生数据访问。

    传递到 onAsyncNoted() 中的 AsyncNotedOp 参数包含名为 getMessage() 的方法。此方法提供了有关数据访问的详细信息。如果是位置信息回调,该消息将包含监听器的系统身份哈希值。

  • 在极少数情况下,如果应用将自身的 UID 传递到 noteOp(),需要调用 onSelfNoted()

按归因标记审核数据访问

您的应用可能有几种主要用例,例如允许用户拍照并与联系人分享这些照片。如果您开发的是一款多用途应用,可以在审核应用的数据访问时对应用的各部分使用归因标记。在传递到 onNoted() 调用的对象中会返回 attributionTag 上下文。这可以帮助您更轻松地将数据访问回溯至代码的逻辑部分。

如需在应用中定义归因标记,请完成以下部分中的步骤。

创建归因标记

如果您在某个 Activity 中访问数据(例如请求位置信息或访问用户的联系人列表),请在该 Activity 的 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.setNotedAppOpsCollector(appOpsCollector);
    }
}