HarmonyOS 6.0 AVSession Kit sendCustomData Guide: Private Media Session Data Transfer in Practice

HarmonyOS 6.0 introduces sendCustomData to AVSession Kit, enabling media session providers, controllers, and casting endpoints to exchange private business data. This solves common problems such as lyric synchronization, playlist coordination, and cross-device state recovery. Keywords: HarmonyOS 6.0, AVSession Kit, sendCustomData.

The technical specification snapshot is clear

Parameter Description
Platform HarmonyOS 6.0
API Version API Version 22
Development Language ArkTS / TypeScript
Core Capability AVSession Kit private data transfer
Key API `sendCustomData(customData: { [key: string]: Object }): Promise
`
Core Classes AVSession, AVSessionController, AVCastController
System Capability SystemCapability.Multimedia.AVSession.Core
Typical Permissions ohos.permission.MEDIA_CONTROL, ohos.permission.DISTRIBUTED_DATASYNC
Dependency Modules @kit.AVSessionKit, @kit.BasicServicesKit, @kit.AbilityKit
Stars Not provided in the original article

AVSession Kit private data transfer diagram AI Visual Insight: The image is a HarmonyOS article illustration. Its core purpose is to highlight that the new AVSession Kit capability sits at the center of the media session architecture, spanning three paths: the session provider, the controller, and cross-device casting. It emphasizes that this is a data extension channel for distributed audio and video control.

HarmonyOS 6.0 extends media control from command delivery to business data exchange

Before HarmonyOS 6.0, AVSession mainly focused on the standard control plane, handling metadata synchronization and playback control. Developers lacked a unified channel for business fields such as lyrics, danmaku overlays, video quality preferences, and playlists.

The addition of sendCustomData changes that. It uses one unified signature across three object types, allowing the app side, control side, and casting side to transmit custom key-value data. This makes it suitable for building more complete cross-device media experiences.

The responsibility boundaries across the three roles are now unified

  • AVSession: The media session provider that broadcasts private data to connected controllers.
  • AVSessionController: The media session controller that sends business commands to a specific session.
  • AVCastController: The casting controller responsible for data synchronization between the primary device and remote devices.
// The method signature is consistent across all three object types
sendCustomData(customData: { [key: string]: Object }): Promise
<void>

This means developers can reuse the same data model and exception-handling strategy, reducing the cognitive overhead of integrating multiple roles.

The session provider can actively broadcast business data to controllers

A common media app scenario is pushing lyrics, playback queue changes, or live interaction status to the playback control center. In this case, it is more appropriate for AVSession to call sendCustomData proactively.

import { avSession } from '@kit.AVSessionKit';
import { BusinessError } from '@kit.BasicServicesKit';

async function sendCustomDataFromSession(currentAVSession: avSession.AVSession) {
  try {
    const customData = {
      type: 'lyric_sync',
      timestamp: Date.now(), // Send the current timestamp
      lyricLine: '越过山丘 虽然已白了头',
      position: 125000 // Current playback position in milliseconds
    };
    await currentAVSession.sendCustomData(customData);
    console.info('sendCustomData success');
  } catch (err) {
    const error = err as BusinessError;
    console.error(`sendCustomData failed: code=${error.code}, message=${error.message}`);
  }
}

This code sends lyrics together with the timeline to the controller side. It is well suited for lock screen controls, in-vehicle integration, and companion device displays.

The controller side is better suited for sending private control commands

When the playback control center needs to notify the app to perform enhanced actions, such as switching the equalizer, triggering sound effects, or adjusting a custom volume step, AVSessionController should initiate the request.

import { BusinessError } from '@kit.BasicServicesKit';

async function sendCommandToSession(avSessionController: any) {
  try {
    const customData = {
      action: 'volume_up',
      step: 5, // Custom volume step
      requestId: Date.now().toString()
    };
    await avSessionController.sendCustomData(customData);
    console.info('Control command sent');
  } catch (err) {
    const error = err as BusinessError;
    console.error(`sendCustomData failed: ${error.message}`);
  }
}

This code sends business control instructions from the controller side to the media session, extending capabilities beyond what standard playback commands can express.

The casting controller makes cross-device state synchronization extensible

Casting needs more than just Play and Pause. Real-world scenarios often require synchronizing playlists, the current index, subtitle preferences, and video quality levels. That is exactly where AVCastController adds value.

import { BusinessError } from '@kit.BasicServicesKit';

async function sendCustomDataToRemote(currentAVSession: any) {
  try {
    const castController = await currentAVSession.getAVCastController();
    const customData = {
      type: 'playlist_sync',
      playlistId: 'playlist_12345',
      currentIndex: 3, // Playback index the remote device should switch to
      nextTrackId: 'track_67890'
    };
    await castController.sendCustomData(customData);
    console.info('Custom data sent to remote device');
  } catch (err) {
    const error = err as BusinessError;
    console.error(`sendCustomData to remote failed: ${error.message}`);
  }
}

This code synchronizes playback queue state to the remote casting device, preventing context drift between the primary controller and the target device.

A complete integration must cover permissions, session activation, and callbacks

If you only call the API without configuring permissions or listening for receive events, the private data channel cannot form a complete loop. In a production integration, declare permissions first, then create and activate the session, and finally register customDataReceive.

{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.MEDIA_CONTROL" },
      { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "backgroundModes": ["audioPlayback", "dataTransfer"]
      }
    ]
  }
}

This configuration defines the permissions required for media control and distributed data synchronization. It is the foundation for cross-device private data transfer.

Register the custom data listener only after creating and activating AVSession

import { avSession } from '@kit.AVSessionKit';
import { BusinessError } from '@kit.BasicServicesKit';

async function createSession(context: any) {
  try {
    const session = await avSession.createAVSession(context, 'MyMediaApp', 'audio');
    await session.setAVMetadata({
      assetId: 'song_001',
      title: '夜曲',
      artist: '周杰伦',
      duration: 300000 // Total media duration in milliseconds
    });
    await session.activate(); // The session must be active before it can participate in control and communication

    session.on('customDataReceive', (customData: { [key: string]: Object }) => {
      console.info(`Received: ${JSON.stringify(customData)}`);
    });

    return session;
  } catch (err) {
    const error = err as BusinessError;
    console.error(`createSession failed: ${error.code}, ${error.message}`);
  }
}

This code completes session creation, metadata setup, activation, and listener registration. It serves as the minimum viable integration skeleton.

Developers need to pay close attention to system boundaries and error handling

The original material clearly states that third-party apps mainly act as AVSession providers, while AVSessionController and parts of the full casting capability are oriented more toward system app scenarios. You should confirm role and permission boundaries before finalizing the design.

At the same time, common controller-side errors include a missing session, a missing controller, or service exceptions. It is best practice to wrap every sendCustomData call in try/catch and include foundational fields such as type, timestamp, and requestId in the payload to support idempotent handling and easier troubleshooting.

A lightweight data protocol is preferable to sending large objects directly

Although private data transfer supports object payloads, that does not mean it should carry large content packages. A safer approach is to send only events, indexes, states, and resource references, while handing off large data to files, the network, or dedicated media channels.

You can follow a simple convention where each message includes type, version, payload, and requestId, and where the receiver gracefully degrades on unknown message types instead of crashing.

FAQ structured Q&A

Q1: Can sendCustomData replace setAVMetadata and setAVPlaybackState?

No. Standard APIs handle system-level media presentation and control semantics, while sendCustomData is only suitable for business extension fields. You should use them together.

Q2: Can third-party apps fully use AVSessionController and AVCastController?

Not always. The original material emphasizes that the full controller role and some casting capabilities are more commonly exposed to system apps. Verify device permissions and app identity before integration.

Q3: Is private data transfer suitable for large payloads?

No. It is better suited for lightweight events and state synchronization data, such as lyric indexes, playback positions, sound effect parameters, and playlist identifiers.

Core Summary: This article systematically explains the new sendCustomData capability added to AVSession Kit in HarmonyOS 6.0, covering the API design across AVSession, AVSessionController, and AVCastController, along with permission setup, sample code, error handling, and casting integration practices.