Hi,
We're building a voice assistant app. The user speaks to the assistant through a Bluetooth earset; the app records the speech, transcribes it and the assistant acts on it. We want the user to start and stop that capture from the earset button while the iPhone stays locked, without the Phone call UI. We prototyped this with the PushToTalk framework and have two questions.
1. Headset play button does not reach our joined PTT channel
Setup (minimal test app, nothing else running):
- iPhone 14 Pro Max, iOS 26.6.1 (23G83). Entitlements:
com.apple.developer.push-to-talk,aps-environment. Background modes:push-to-talk,audio,bluetooth-central. PTChannelManager.channelManager(delegate:restorationDelegate:)→requestJoinChannelin the foreground →didJoinChannel→setAccessoryButtonEventsEnabled(true, channelUUID:)completes without error.- Audio session configured once before joining:
.playAndRecord, options.allowBluetoothHFP; tried mode.defaultand.voiceChat. We never callsetActive; audio starts only indidActivate(AVAudioEngine).
What works:
- The system PTT UI talk button, phone locked:
didBeginTransmitting→didActivate(earset HFP 16 kHz) → audio recorded. - A Lock Screen / Control Center control running an intent that adopts
PushToTalkTransmissionIntent, phone locked: one tap starts, one tap ends; 5+ minute transmissions; app cold-launched by the system when needed.
What doesn't: pressing the earset's play/pause button (single press) while joined and idle never produces didBeginTransmitting. Same result with AirPods and with a third-party A2DP/HFP earset, locked and unlocked, Music app closed or not. mediaremoted logs, for every press:
mediaremoted: Received command from client <... bundleIdentifier = com.apple.BTAvrcp ...>: <... command = Play ...>
mediaremoted: No context for context-sensitive command <... command = Play, SenderBundleIdentifier = <com.apple.BTAvrcp> ...>
mediaremoted: Destination app com.apple.Music not available for command <... command = Play ...>
mediaremoted: Sending previously queued command <... command = Play ...> (Music is launched and plays)
In thread 804205 you described this path as pressing "play" on an A2DP/HFP accessory "that's currently in playback mode". Questions:
- a) What exactly must be true for a headset AVRCP Play/Pause to be routed to the PTT system instead of the now-playing app? Is "playback mode" a state of our channel (e.g. only while
setActiveRemoteParticipantis set / audio is playing), of the accessory, or of the route? - b) Is an idle joined channel (no remote participant, nothing playing) expected to receive accessory events at all?
- c) Is a press-once start / press-once end mapping supported for accessory events, or is it press-and-hold only?
- d) If headset buttons cannot start a transmission from idle, is the BLE path (
requestBeginTransmittingfrom a CoreBluetooth event) the only supported option for an earset trigger? (Our earset exposes no BLE button characteristic, so that would require firmware changes.)
2. Is this an appropriate use of PushToTalk?
The audio goes from the user to our assistant service (speech-to-text, then the assistant responds in the app or by voice). There is no second human on the channel. In thread 816408 you noted the voice communication frameworks "can't be used for any other purpose". Would capturing a user's spoken requests/notes for an AI assistant through PTT be considered a supported use, or is PTT limited to person-to-person communication? If it isn't, is there any supported API that lets a user start and stop audio capture with an earset or a Lock Screen control while the device stays locked?
A sysdiagnose and the test project are available on request.
Thanks!