Auditoría de acceso a los datos

Para proporcionar más transparencia sobre cómo tu app y sus dependencias acceden a los datos privados de los usuarios, Android 11 incluye la auditoría de acceso a los datos. Al obtener estadísticas de este proceso, podrás identificar mejor y rectificar el acceso potencialmente inesperado a los datos. Tu app puede registrar una instancia de AppOpsManager.OnOpNotedCallback, que puede realizar acciones cada vez que ocurre uno de los siguientes eventos:

Se invoca la auditoría de acceso a los datos en el subproceso en el que se solicitan datos. Esto significa que, si un SDK o una biblioteca de terceros de tu app llama a una API que accede a datos privados, la auditoría de acceso a los datos le permite a OnOpNotedCallback examinar la información sobre la llamada. Por lo general, este objeto de devolución de llamada puede determinar si la llamada provino de tu app o del SDK observando el estado actual de la app, como el seguimiento de pila del subproceso actual.

Registro del acceso a los datos

Para realizar una auditoría de acceso a los datos con una instancia de AppOpsManager.OnOpNotedCallback, implementa la lógica de devolución de llamada en el componente donde intentas auditar el acceso a los datos (p. ej., dentro del método onCreate() de una actividad).

En el siguiente fragmento de código, se define un AppOpsManager.OnOpNotedCallback para auditar el acceso a datos dentro de una sola actividad:

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);
    }
}

Se llama a los métodos onAsyncNoted() y onSelfNoted() en situaciones específicas:

  • Se llama a onAsyncNoted() si el acceso a los datos no ocurre durante la llamada a la API de tu app. El ejemplo más común es cuando tu app registra un objeto de escucha y el acceso a los datos se produce cada vez que se invoca la devolución de llamada de ese objeto.

    El argumento AsyncNotedOp que se pasa a onAsyncNoted() contiene un método llamado getMessage(). Ese método proporciona más información sobre el acceso a los datos. En el caso de devoluciones de llamada de ubicación, el mensaje contiene el hash de identidad del sistema del objeto de escucha.

  • Se llama a onSelfNoted() en el caso poco frecuente de que una aplicación pase su propio UID a noteOp().

Cómo auditar el acceso a los datos por etiqueta de atribución

Es posible que tu app tenga varios casos prácticos principales, como permitir que los usuarios tomen fotos y las compartan con sus contactos. Si desarrollas una aplicación multiuso de este tipo, puedes aplicar una etiqueta de atribución a cada parte de la aplicación durante las auditorías de acceso a los datos. Se devuelve el contexto attributionTag en los objetos pasados en las llamadas a onNoted(). Esto te permite hacer el seguimiento de acceso a los datos hasta las partes lógicas de tu código con mayor facilidad.

Para definir etiquetas de atribución en la aplicación, completa los pasos que se indican en las siguientes secciones.

Cómo crear etiquetas de atribución

En el método onCreate() de la actividad en la que accedes a los datos, como la actividad en la que solicitas la ubicación o el acceso a la lista de contactos del usuario, llama a createAttributionContext() y pasa la etiqueta de atribución que quieras asociar con una parte de la app.

En el siguiente fragmento de código, se muestra cómo crear una etiqueta de atribución para la parte de "compartir ubicación de una foto" de una app:

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.
        }
    }
}

Cómo incluir las etiquetas de atribución en los registros de acceso

Actualiza la devolución de llamada AppOpsManager.OnOpNotedCallback para que los registros de tu app incluyan los nombres de las etiquetas de atribución que definiste.

En el siguiente fragmento de código, se muestra la lógica actualizada que registra las etiquetas de atribución:

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);
    }
}