title: Handle bluetooth headset clicks (ACTION_VOICE_COMMAND and ACTION_WEB_SEARCH) on Android
body: <p>I’m developing an <code>Android</code> application and I want it to interact with <code>headset</code> button clicks. I’m testing it in a <strong>Nexus 5 with Android KitKat 4.4</strong>.</p>
  
  <p>I tried first with a simple headset (not wireless). The button event received was <code>KEYCODE_HEADSETHOOK</code> (79). I created a <code>MEDIA_BUTTON</code> <code>receiver</code> to handle its clicks:</p>
  
  <pre><code>&lt;receiver android:name="com.example.mytest.SearchActivity$MediaButtonIntentReceiver"&gt;
      &lt;intent-filter&gt;
          &lt;intent-filter android:priority="1000000000"&gt;
              &lt;action android:name="android.intent.action.MEDIA_BUTTON" /&gt;
          &lt;/intent-filter&gt;
      &lt;/intent-filter&gt;
  &lt;/receiver&gt;
  </code></pre>
  
  <p>This is the activity holding the receiver:</p>
  
  <pre><code>public class SearchActivity extends Activity {
  
      private AudioManager mAudioManager;
      private ComponentName mAudioReceiver;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.search);
  
          mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
          mAudioReceiver =  new ComponentName(getPackageName(),
              MediaButtonIntentReceiver.class.getName());
      }
  
      @Override
      protected void onResume() {
          super.onResume();
  
          mAudioManager.registerMediaButtonEventReceiver(mAudioReceiver);
      }
  
      @Override
      protected void onPause() {
          super.onPause();
  
          mAudioManager.unregisterMediaButtonEventReceiver(mAudioReceiver);
      }
  
      public static class MediaButtonIntentReceiver extends BroadcastReceiver {
  
          @Override
          public void onReceive(Context context, Intent intent) {
              Log.d("SA", "ON RECEIVE");
  
              ...
  
              abortBroadcast();
          }
      }
  }
  </code></pre>
  
  <p>This code works with my wired headset <strong>only if the user performs a short click</strong>. Performing a long click opens <code>Google Voice Search</code>. I’d like to capture long clicks too, but I don’t mind if it’s not possible.</p>
  
  <p>After that I tested it with a <code>bluetooth</code> headset. Specifically, I’m using Moveteck Bluetooth Headset BH119A (you can see an image at the bottom of this post). This headset only has one button, and if I press it the following "<code>activity</code>" is opened:</p>
  
  <p><img src="https://i.stack.imgur.com/FgeNF.png" alt="enter image description here"></p>
  
  <p>I’d like to capture this click event too if my <code>Activity</code> is opened. How can I do it? I tried adding the following filters to my receiver, but it’s not working either:</p>
  
  <pre><code>&lt;action android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" /&gt;
  &lt;action android:name="android.intent.action.VOICE_COMMAND" /&gt;
  &lt;action android:name="android.intent.action.CALL_BUTTON" /&gt;
  </code></pre>
  
  <p>I also tried overriding <code>onKeyDown</code> in my Activity, but it’s not triggered.</p>
  
  <p>Anybody knows how can I intercept those events?</p>
  
  <p>This is my bluetooth headset:</p>
  
  <p><img src="https://i.stack.imgur.com/zPDvy.jpg" alt="enter image description here"></p>
  
  <hr>
  
  <p>** EDIT **</p>
  
  <p>Following Toaster’s advice, I checked the whole log looking for the events my headset triggers.</p>
  
  <p><strong>Wired headset long click</strong></p>
  
  <p>This is the log when I long click in my wired headset (it opens <code>Google Voice Search</code>):</p>
  
  <pre><code>12-10 09:24:36.644: I/MediaFocusControl(740): voice-based interactions: about to use ACTION_WEB_SEARCH
  12-10 09:24:36.644: I/ActivityManager(740): START u0 {act=android.speech.action.WEB_SEARCH flg=0x10800000 cmp=com.google.android.googlequicksearchbox/.SearchActivity} from pid 740
  12-10 09:24:36.754: I/ActivityManager(740): START u0 {act=android.speech.action.WEB_SEARCH flg=0x10000000 cmp=com.google.android.googlequicksearchbox/com.google.android.launcher.GEL} from pid 10153
  12-10 09:24:36.764: I/InputDispatcher(740): Dropping event because there is no focused window or focused application.
  12-10 09:24:36.764: I/InputDispatcher(740): Dropping event because there is no focused window or focused application.
  12-10 09:24:36.774: I/GEL(1025): handleIntent(Intent { act=android.speech.action.WEB_SEARCH flg=0x10400000 cmp=com.google.android.googlequicksearchbox/com.google.android.launcher.GEL })
  12-10 09:24:36.774: V/SearchControllerCache(10153): creating SearchController
  12-10 09:24:36.804: I/AudioRouter(10153): ROUTE_NONE-&gt;ROUTE_NO_BLUETOOTH
  12-10 09:24:36.804: I/MediaFocusControl(740):  AudioFocus  requestAudioFocus() from android.media.AudioManager@4267ad58com.google.android.voicesearch.audio.AudioRouterImpl$1@42695f60
  12-10 09:24:36.804: I/Velvet.SdchManager(10153): Sdch cache load complete.
  12-10 09:24:36.814: W/IInputConnectionWrapper(18407): showStatusIcon on inactive InputConnection
  12-10 09:24:36.814: I/Icing.InternalIcingCorporaProvider(10153): Updating corpora: A: NONE, C: DELTA
  12-10 09:24:36.854: I/VS.G3EngineManager(10153): create_rm: m=GRAMMAR,l=en-US
  12-10 09:24:36.854: W/Search.ConcurrentUtils(10153): Executor queue length is now 9. Perhaps some tasks are too long, or the pool is too small. [GrecoExecutor-1]
  12-10 09:24:36.854: I/VS.G3EngineManager(10153): Brought up new g3 instance :/system/usr/srec/en-US/grammar.config for: en-USin: 9 ms
  12-10 09:24:36.864: D/audio_hw_primary(189): out_set_parameters: enter: usecase(1: low-latency-playback) kvpairs: routing=4
  12-10 09:24:36.864: D/audio_hw_primary(189): select_devices: out_snd_device(4: headphones) in_snd_device(0: )
  12-10 09:24:36.874: D/audio_hw_primary(189): select_devices: out_snd_device(0: ) in_snd_device(18: headset-mic)
  12-10 09:24:36.874: D/(189): Failed to fetch the lookup information of the device 00000008 
  12-10 09:24:36.874: E/ACDB-LOADER(189): Error: ACDB AudProc vol returned = -19
  12-10 09:24:38.864: I/LATENCY(10153): 0-4,45-2064,
  12-10 09:24:38.874: I/AudioRouter(10153): ROUTE_NO_BLUETOOTH-&gt;ROUTE_NONE
  12-10 09:24:38.874: I/MediaFocusControl(740):  AudioFocus  abandonAudioFocus() from android.media.AudioManager@4267ad58com.google.android.voicesearch.audio.AudioRouterImpl$1@42695f60
  12-10 09:24:38.874: I/MicrophoneInputStream(10153): mic_close
  </code></pre>
  
  <p>It seems it triggers an <code>ACTION_WEB_SEARCH</code> event, so I tried to add it to the filter. I tried it two ways:</p>
  
  <ol>
  <li><p>Declaring the filter in the manifest:</p>
  
  <pre><code>&lt;action android:name="android.intent.action.WEB_SEARCH" /&gt;
  </code></pre></li>
  <li><p>Declaring the filter programmatically:</p>
  
  <pre><code>protected void onResume() {
      IntentFilter f = new IntentFilter(Intent.ACTION_WEB_SEARCH);
      registerReceiver(myReceiver, f);
  }
  
  private BroadcastReceiver myReceiver = new BroadcastReceiver() {
  
      @Override
      public void onReceive(Context context, Intent intent) {
          Log.d("AA", "ON RECEIVE");
      }
  };
  </code></pre></li>
  </ol>
  
  <p>None of this options work. As I said, this scenario isn’t of much importance, I can deal with it.</p>
  
  <hr>
  
  <p><strong>Wireless headset simple click</strong></p>
  
  <p>The Wireless headset simple click is the one opening the Voice Dialer and <strong>it’s the event I really need to capture</strong>. This is the log output:</p>
  
  <pre><code>12-10 10:41:22.014: E/bt-rfcomm(21800): PORT_DataInd, p_port:0x7507a7e8, p_data_co_callback is null
  12-10 10:41:22.014: D/HeadsetStateMachine(21800): processVrEvent: state=1 mVoiceRecognitionStarted: false mWaitingforVoiceRecognition: false isInCall: false
  12-10 10:41:22.014: I/ActivityManager(740): START u0 {act=android.intent.action.VOICE_COMMAND flg=0x10000000 cmp=com.google.android.googlequicksearchbox/com.google.android.voicesearch.handsfree.HandsFreeIntentActivity} from pid 21800
  12-10 10:41:22.154: V/Avrcp(21800): New genId = 440, clearing = 1
  12-10 10:41:22.154: D/HandsFreeIntentActivity(10153): #onStart(Intent { act=android.intent.action.VOICE_COMMAND flg=0x10800000 cmp=com.google.android.googlequicksearchbox/com.google.android.voicesearch.handsfree.HandsFreeIntentActivity })
  12-10 10:41:22.154: D/HandsFreeIntentActivity(10153): Starting activity: Intent { act=android.intent.action.VOICE_COMMAND flg=0x10000000 cmp=com.google.android.googlequicksearchbox/com.google.android.voicesearch.handsfree.HandsFreeActivity }
  12-10 10:41:22.154: I/ActivityManager(740): START u0 {act=android.intent.action.VOICE_COMMAND flg=0x10000000 cmp=com.google.android.googlequicksearchbox/com.google.android.voicesearch.handsfree.HandsFreeActivity} from pid 10153
  12-10 10:41:22.204: D/OpenGLRenderer(10153): Enabling debug mode 0
  12-10 10:41:22.214: W/IInputConnectionWrapper(18895): showStatusIcon on inactive InputConnection
  12-10 10:41:22.244: I/ActivityManager(740): Displayed com.google.android.googlequicksearchbox/com.google.android.voicesearch.handsfree.HandsFreeActivity: +80ms (total +89ms)
  12-10 10:41:22.374: I/AudioRouter(10153): ROUTE_NONE-&gt;ROUTE_BLUETOOTH_WANTED
  12-10 10:41:22.384: I/MediaFocusControl(740):  AudioFocus  requestAudioFocus() from android.media.AudioManager@4267ad58com.google.android.voicesearch.audio.AudioRouterImpl$1@42695f60
  12-10 10:41:22.384: V/Avrcp(21800): New genId = 441, clearing = 1
  12-10 10:41:22.384: D/BluetoothManagerService(740): Message: 30
  12-10 10:41:22.384: D/BluetoothHeadset(10153): Proxy object connected
  12-10 10:41:22.384: I/BluetoothController(10153): BT device connected
  12-10 10:41:22.394: I/AudioRouter(10153): BT required, starting SCO
  12-10 10:41:22.394: I/BluetoothController(10153): Starting VR
  12-10 10:41:22.394: D/BluetoothHeadset(10153): startVoiceRecognition()
  12-10 10:41:22.394: D/HeadsetStateMachine(21800): Voice recognition started successfully
  12-10 10:41:22.394: D/HeadsetStateMachine(21800): Initiating audio connection for Voice Recognition
  12-10 10:41:22.394: W/bt-btm(21800): BTM Remote does not support 3-EDR eSCO
  12-10 10:41:22.434: I/TextToSpeech(10153): Sucessfully bound to com.google.android.tts
  12-10 10:41:22.454: I/TextToSpeech(10153): Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
  12-10 10:41:22.454: I/TextToSpeech(10153): Set up connection to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
  12-10 10:41:22.484: D/dalvikvm(21966): GC_CONCURRENT freed 346K, 3% free 16647K/17064K, paused 2ms+3ms, total 13ms
  12-10 10:41:22.764: D/audio_hw_primary(189): out_set_parameters: enter: usecase(1: low-latency-playback) kvpairs: routing=32
  12-10 10:41:22.774: D/audio_hw_primary(189): select_devices: out_snd_device(11: bt-sco-headset) in_snd_device(0: )
  12-10 10:41:24.874: I/EventLogService(1148): Aggregate from 1386666683008 (log), 1386666683008 (data)
  12-10 10:41:24.994: I/ServiceDumpSys(1148): dumping service [account]
  12-10 10:41:25.994: D/dalvikvm(10153): GC_CONCURRENT freed 1582K, 15% free 23868K/27920K, paused 5ms+7ms, total 60ms
  12-10 10:41:26.014: I/VS.G3EngineManager(10153): create_rm: m=GRAMMAR,l=en-US
  12-10 10:41:26.024: I/VS.G3EngineManager(10153): Brought up new g3 instance :/system/usr/srec/en-US/grammar.config for: en-USin: 4 ms
  12-10 10:41:26.024: D/audio_hw_primary(189): out_set_parameters: enter: usecase(1: low-latency-playback) kvpairs: routing=32
  12-10 10:41:26.034: D/audio_hw_primary(189): select_devices: out_snd_device(0: ) in_snd_device(25: bt-sco-mic)
  12-10 10:41:26.034: D/(189): Failed to fetch the lookup information of the device 00000015 
  12-10 10:41:26.034: E/ACDB-LOADER(189): Error: ACDB AudProc vol returned = -19
  </code></pre>
  
  <p>This time it seems it’s sending an <code>ACTION_VOICE_COMMAND</code>, so I tried to add it to the filter. I tried it two ways:</p>
  
  <ol>
  <li><p>Declaring the filter in the manifest:</p>
  
  <pre><code>&lt;action android:name="android.intent.action.VOICE_COMMAND" /&gt;
  </code></pre></li>
  <li><p>Declaring the filter programmatically:</p>
  
  <pre><code>protected void onResume() {
      IntentFilter f = new IntentFilter(Intent.ACTION_VOICE_COMMAND);
      registerReceiver(myReceiver, f);
  }
  
  private BroadcastReceiver myReceiver = new BroadcastReceiver() {
  
      @Override
      public void onReceive(Context context, Intent intent) {
          Log.d("AA", "ON RECEIVE");
      }
  };
  </code></pre></li>
  </ol>
  
  <p>Again, I’m not receiving these events, I don’t know why.</p>