1  /*
2   * Copyright (C) 2014 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.android.tv.settings.accessories;
18  
19  import android.app.Activity;
20  import android.app.FragmentManager;
21  import android.bluetooth.BluetoothDevice;
22  import android.content.Intent;
23  import android.hardware.hdmi.HdmiControlManager;
24  import android.hardware.hdmi.HdmiPlaybackClient;
25  import android.hardware.input.InputManager;
26  import android.os.Bundle;
27  import android.os.Handler;
28  import android.os.Message;
29  import android.os.SystemClock;
30  import android.transition.TransitionManager;
31  import android.util.Log;
32  import android.view.KeyEvent;
33  import android.view.View;
34  import android.view.ViewGroup;
35  import android.view.WindowManager;
36  
37  import androidx.annotation.NonNull;
38  
39  import com.android.tv.settings.R;
40  
41  import java.lang.ref.WeakReference;
42  import java.util.ArrayList;
43  import java.util.List;
44  
45  /**
46   * Activity for detecting and adding (pairing) new bluetooth devices.
47   */
48  public class AddAccessoryActivity extends Activity implements BluetoothDevicePairer.EventListener {
49  
50      private static final boolean DEBUG = false;
51      private static final String TAG = "AddAccessoryActivity";
52  
53      private static final String ACTION_CONNECT_INPUT =
54              "com.google.android.intent.action.CONNECT_INPUT";
55  
56      private static final String INTENT_EXTRA_NO_INPUT_MODE = "no_input_mode";
57  
58      private static final String SAVED_STATE_PREFERENCE_FRAGMENT =
59              "AddAccessoryActivity.PREFERENCE_FRAGMENT";
60      private static final String SAVED_STATE_CONTENT_FRAGMENT =
61              "AddAccessoryActivity.CONTENT_FRAGMENT";
62      private static final String SAVED_STATE_BLUETOOTH_DEVICES =
63              "AddAccessoryActivity.BLUETOOTH_DEVICES";
64  
65      private static final String ADDRESS_NONE = "NONE";
66  
67      private static final int AUTOPAIR_COUNT = 10;
68  
69      private static final int MSG_UPDATE_VIEW = 1;
70      private static final int MSG_REMOVE_CANCELED = 2;
71      private static final int MSG_PAIRING_COMPLETE = 3;
72      private static final int MSG_OP_TIMEOUT = 4;
73      private static final int MSG_RESTART = 5;
74      private static final int MSG_TRIGGER_SELECT_DOWN = 6;
75      private static final int MSG_TRIGGER_SELECT_UP = 7;
76      private static final int MSG_AUTOPAIR_TICK = 8;
77      private static final int MSG_START_AUTOPAIR_COUNTDOWN = 9;
78  
79      private static final int CANCEL_MESSAGE_TIMEOUT = 3000;
80      private static final int DONE_MESSAGE_TIMEOUT = 3000;
81      private static final int PAIR_OPERATION_TIMEOUT = 120000;
82      private static final int CONNECT_OPERATION_TIMEOUT = 60000;
83      private static final int RESTART_DELAY = 3000;
84      private static final int LONG_PRESS_DURATION = 3000;
85      private static final int KEY_DOWN_TIME = 150;
86      private static final int TIME_TO_START_AUTOPAIR_COUNT = 5000;
87      private static final int EXIT_TIMEOUT_MILLIS = 90 * 1000;
88  
89      private AddAccessoryPreferenceFragment mPreferenceFragment;
90      private AddAccessoryContentFragment mContentFragment;
91  
92      // members related to Bluetooth pairing
93      private BluetoothDevicePairer mBluetoothPairer;
94      private int mPreviousStatus = BluetoothDevicePairer.STATUS_NONE;
95      private boolean mPairingSuccess = false;
96      private boolean mPairingBluetooth = false;
97      private List<BluetoothDevice> mBluetoothDevices;
98      private String mCancelledAddress = ADDRESS_NONE;
99      private String mCurrentTargetAddress = ADDRESS_NONE;
100      private String mCurrentTargetStatus = "";
101      private boolean mPairingInBackground = false;
102  
103      private boolean mDone = false;
104  
105      private boolean mHwKeyDown;
106      private boolean mHwKeyDidSelect;
107      private boolean mNoInputMode;
108  
109      // Internal message handler
110      private final MessageHandler mMsgHandler = new MessageHandler();
111  
112      private static class MessageHandler extends Handler {
113  
114          private WeakReference<AddAccessoryActivity> mActivityRef = new WeakReference<>(null);
115  
setActivity(AddAccessoryActivity activity)116          public void setActivity(AddAccessoryActivity activity) {
117              mActivityRef = new WeakReference<>(activity);
118          }
119  
120          @Override
handleMessage(Message msg)121          public void handleMessage(Message msg) {
122              final AddAccessoryActivity activity = mActivityRef.get();
123              if (activity == null) {
124                  return;
125              }
126              switch (msg.what) {
127                  case MSG_UPDATE_VIEW:
128                      activity.updateView();
129                      break;
130                  case MSG_REMOVE_CANCELED:
131                      activity.mCancelledAddress = ADDRESS_NONE;
132                      activity.updateView();
133                      break;
134                  case MSG_PAIRING_COMPLETE:
135                      activity.finish();
136                      break;
137                  case MSG_OP_TIMEOUT:
138                      activity.handlePairingTimeout();
139                      break;
140                  case MSG_RESTART:
141                      if (activity.mBluetoothPairer != null) {
142                          activity.mBluetoothPairer.start();
143                          activity.mBluetoothPairer.cancelPairing();
144                      }
145                      break;
146                  case MSG_TRIGGER_SELECT_DOWN:
147                      activity.sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER, true);
148                      activity.mHwKeyDidSelect = true;
149                      sendEmptyMessageDelayed(MSG_TRIGGER_SELECT_UP, KEY_DOWN_TIME);
150                      activity.cancelPairingCountdown();
151                      break;
152                  case MSG_TRIGGER_SELECT_UP:
153                      activity.sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER, false);
154                      break;
155                  case MSG_START_AUTOPAIR_COUNTDOWN:
156                      activity.setPairingText(
157                              activity.getString(R.string.accessories_autopair_msg, AUTOPAIR_COUNT));
158                      sendMessageDelayed(obtainMessage(MSG_AUTOPAIR_TICK,
159                              AUTOPAIR_COUNT, 0, null), 1000);
160                      break;
161                  case MSG_AUTOPAIR_TICK:
162                      int countToAutoPair = msg.arg1 - 1;
163                      if (countToAutoPair <= 0) {
164                          activity.setPairingText(null);
165                          // AutoPair
166                          activity.startAutoPairing();
167                      } else {
168                          activity.setPairingText(
169                                  activity.getString(R.string.accessories_autopair_msg,
170                                          countToAutoPair));
171                          sendMessageDelayed(obtainMessage(MSG_AUTOPAIR_TICK,
172                                  countToAutoPair, 0, null), 1000);
173                      }
174                      break;
175                  default:
176                      super.handleMessage(msg);
177              }
178          }
179      }
180  
181      private final Handler mAutoExitHandler = new Handler();
182  
183      private final Runnable mAutoExitRunnable = this::finish;
184  
185      @Override
onCreate(Bundle savedInstanceState)186      public void onCreate(Bundle savedInstanceState) {
187          super.onCreate(savedInstanceState);
188  
189          setContentView(R.layout.lb_dialog_fragment);
190  
191          mMsgHandler.setActivity(this);
192  
193          getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
194                  WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
195  
196          mNoInputMode = getIntent().getBooleanExtra(INTENT_EXTRA_NO_INPUT_MODE, false);
197          mHwKeyDown = false;
198  
199          if (savedInstanceState == null) {
200              mBluetoothDevices = new ArrayList<>();
201          } else {
202              mBluetoothDevices =
203                      savedInstanceState.getParcelableArrayList(SAVED_STATE_BLUETOOTH_DEVICES);
204          }
205  
206          final FragmentManager fm = getFragmentManager();
207          if (savedInstanceState == null) {
208              mPreferenceFragment = AddAccessoryPreferenceFragment.newInstance();
209              mContentFragment = AddAccessoryContentFragment.newInstance();
210              fm.beginTransaction()
211                      .add(R.id.action_fragment, mPreferenceFragment)
212                      .add(R.id.content_fragment, mContentFragment)
213                      .commit();
214          } else {
215              mPreferenceFragment = (AddAccessoryPreferenceFragment)
216                      fm.getFragment(savedInstanceState,
217                              SAVED_STATE_PREFERENCE_FRAGMENT);
218              mContentFragment = (AddAccessoryContentFragment)
219                      fm.getFragment(savedInstanceState,
220                              SAVED_STATE_CONTENT_FRAGMENT);
221          }
222          sendCecOtpCommand((result) -> {
223              if (result == HdmiControlManager.RESULT_SUCCESS) {
224                  Log.i(TAG, "One Touch Play successful");
225              } else {
226                  Log.i(TAG, "One Touch Play failed");
227              }
228          });
229  
230          rearrangeViews();
231      }
232  
233      @Override
onSaveInstanceState(@onNull Bundle outState)234      protected void onSaveInstanceState(@NonNull Bundle outState) {
235          super.onSaveInstanceState(outState);
236          getFragmentManager().putFragment(outState,
237                  SAVED_STATE_PREFERENCE_FRAGMENT, mPreferenceFragment);
238          getFragmentManager().putFragment(outState,
239                  SAVED_STATE_CONTENT_FRAGMENT, mContentFragment);
240          outState.putParcelableList(SAVED_STATE_BLUETOOTH_DEVICES, mBluetoothDevices);
241      }
242  
243      @Override
onStart()244      protected void onStart() {
245          super.onStart();
246  
247          if (DEBUG) {
248              Log.d(TAG, "onStart() mPairingInBackground = " + mPairingInBackground);
249          }
250  
251          // Only do the following if we are not coming back to this activity from
252          // the Secure Pairing activity.
253          if (!mPairingInBackground) {
254              startBluetoothPairer();
255          }
256  
257          mPairingInBackground = false;
258      }
259  
260      @Override
onResume()261      public void onResume() {
262          super.onResume();
263          if (mNoInputMode) {
264              // Start timer count down for exiting activity.
265              if (DEBUG) Log.d(TAG, "starting auto-exit timer");
266              mAutoExitHandler.postDelayed(mAutoExitRunnable, EXIT_TIMEOUT_MILLIS);
267          }
268      }
269  
270      @Override
onPause()271      public void onPause() {
272          super.onPause();
273          if (DEBUG) Log.d(TAG, "stopping auto-exit timer");
274          mAutoExitHandler.removeCallbacks(mAutoExitRunnable);
275      }
276  
277  
278      @Override
onStop()279      public void onStop() {
280          if (DEBUG) {
281              Log.d(TAG, "onStop()");
282          }
283          if (!mPairingBluetooth) {
284              stopBluetoothPairer();
285              mMsgHandler.removeCallbacksAndMessages(null);
286          } else {
287              // allow activity to remain in the background while we perform the
288              // BT Secure pairing.
289              mPairingInBackground = true;
290          }
291  
292          super.onStop();
293      }
294  
295      @Override
onDestroy()296      protected void onDestroy() {
297          super.onDestroy();
298          stopBluetoothPairer();
299          mMsgHandler.removeCallbacksAndMessages(null);
300      }
301  
302      @Override
onKeyUp(int keyCode, @NonNull KeyEvent event)303      public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
304          if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
305              if (mPairingBluetooth && !mDone) {
306                  cancelBtPairing();
307              }
308          }
309          return super.onKeyUp(keyCode, event);
310      }
311  
312      @Override
onNewIntent(Intent intent)313      public void onNewIntent(Intent intent) {
314          if (ACTION_CONNECT_INPUT.equals(intent.getAction()) &&
315                  (intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) == 0) {
316  
317              KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
318              if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_PAIRING) {
319                  if (event.getAction() == KeyEvent.ACTION_UP) {
320                      onHwKeyEvent(false);
321                  } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
322                      onHwKeyEvent(true);
323                  }
324              }
325          } else {
326              setIntent(intent);
327          }
328      }
329  
onActionClicked(String address)330      public void onActionClicked(String address) {
331          cancelPairingCountdown();
332          if (!mDone) {
333              btDeviceClicked(address);
334          }
335      }
336  
337      // Events related to a device HW key
onHwKeyEvent(boolean keyDown)338      private void onHwKeyEvent(boolean keyDown) {
339          if (!mHwKeyDown) {
340              // HW key was in UP state before
341              if (keyDown) {
342                  // Back key pressed down
343                  mHwKeyDown = true;
344                  mHwKeyDidSelect = false;
345                  mMsgHandler.sendEmptyMessageDelayed(MSG_TRIGGER_SELECT_DOWN, LONG_PRESS_DURATION);
346              }
347          } else {
348              // HW key was in DOWN state before
349              if (!keyDown) {
350                  // HW key released
351                  mHwKeyDown = false;
352                  mMsgHandler.removeMessages(MSG_TRIGGER_SELECT_DOWN);
353                  if (!mHwKeyDidSelect) {
354                      // key wasn't pressed long enough for selection, move selection
355                      // to next item.
356                      mPreferenceFragment.advanceSelection();
357                  }
358                  mHwKeyDidSelect = false;
359              }
360          }
361      }
362  
sendKeyEvent(int keyCode, boolean down)363      private void sendKeyEvent(int keyCode, boolean down) {
364          InputManager iMgr = (InputManager) getSystemService(INPUT_SERVICE);
365          if (iMgr != null) {
366              long time = SystemClock.uptimeMillis();
367              KeyEvent evt = new KeyEvent(time, time,
368                      down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
369                      keyCode, 0);
370              iMgr.injectInputEvent(evt, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
371          }
372      }
373  
updateView()374      protected void updateView() {
375          if (mPreferenceFragment == null || isFinishing()) {
376              // view not yet ready, update will happen on first layout event
377              // or alternately we're done and don't need to do anything
378              return;
379          }
380  
381          int prevNumDevices = mPreferenceFragment.getPreferenceScreen().getPreferenceCount();
382  
383          mPreferenceFragment.updateList(mBluetoothDevices, mCurrentTargetAddress,
384                  mCurrentTargetStatus, mCancelledAddress);
385  
386          if (mNoInputMode) {
387              if (DEBUG) Log.d(TAG, "stopping auto-exit timer");
388              mAutoExitHandler.removeCallbacks(mAutoExitRunnable);
389              if (mBluetoothDevices.size() == 1 && prevNumDevices == 0) {
390                  // first device added, start counter for autopair
391                  mMsgHandler.sendEmptyMessageDelayed(MSG_START_AUTOPAIR_COUNTDOWN,
392                          TIME_TO_START_AUTOPAIR_COUNT);
393              } else {
394  
395                  // Start timer count down for exiting activity.
396                  if (DEBUG) Log.d(TAG, "starting auto-exit timer");
397                  mAutoExitHandler.postDelayed(mAutoExitRunnable, EXIT_TIMEOUT_MILLIS);
398  
399                  if (mBluetoothDevices.size() > 1) {
400                      // More than one device found, cancel auto pair
401                      cancelPairingCountdown();
402                  }
403             }
404          }
405  
406          TransitionManager.beginDelayedTransition(findViewById(R.id.content_frame));
407  
408          rearrangeViews();
409      }
410  
rearrangeViews()411      private void rearrangeViews() {
412          final boolean empty = mBluetoothDevices.isEmpty();
413  
414          final View contentView = findViewById(R.id.content_fragment);
415          final ViewGroup.LayoutParams contentLayoutParams = contentView.getLayoutParams();
416          contentLayoutParams.width = empty ? ViewGroup.LayoutParams.MATCH_PARENT :
417                  getResources().getDimensionPixelSize(R.dimen.lb_content_section_width);
418          contentView.setLayoutParams(contentLayoutParams);
419  
420          mContentFragment.setContentWidth(empty
421                  ? getResources().getDimensionPixelSize(R.dimen.progress_fragment_content_width)
422                  : getResources().getDimensionPixelSize(R.dimen.bt_progress_width_narrow));
423      }
424  
setPairingText(CharSequence text)425      private void setPairingText(CharSequence text) {
426          if (mContentFragment != null) {
427              mContentFragment.setExtraText(text);
428          }
429      }
430  
cancelPairingCountdown()431      private void cancelPairingCountdown() {
432          // Cancel countdown
433          mMsgHandler.removeMessages(MSG_AUTOPAIR_TICK);
434          mMsgHandler.removeMessages(MSG_START_AUTOPAIR_COUNTDOWN);
435          setPairingText(null);
436      }
437  
setTimeout(int timeout)438      private void setTimeout(int timeout) {
439          cancelTimeout();
440          mMsgHandler.sendEmptyMessageDelayed(MSG_OP_TIMEOUT, timeout);
441      }
442  
cancelTimeout()443      private void cancelTimeout() {
444          mMsgHandler.removeMessages(MSG_OP_TIMEOUT);
445      }
446  
startAutoPairing()447      protected void startAutoPairing() {
448          if (mBluetoothDevices.size() > 0) {
449              onActionClicked(mBluetoothDevices.get(0).getAddress());
450          }
451      }
452  
btDeviceClicked(String clickedAddress)453      private void btDeviceClicked(String clickedAddress) {
454          if (mBluetoothPairer != null && !mBluetoothPairer.isInProgress()) {
455              if (mBluetoothPairer.getStatus() == BluetoothDevicePairer.STATUS_WAITING_TO_PAIR &&
456                      mBluetoothPairer.getTargetDevice() != null) {
457                  cancelBtPairing();
458              } else {
459                  if (DEBUG) {
460                      Log.d(TAG, "Looking for " + clickedAddress +
461                              " in available devices to start pairing");
462                  }
463                  for (BluetoothDevice target : mBluetoothDevices) {
464                      if (target.getAddress().equalsIgnoreCase(clickedAddress)) {
465                          if (DEBUG) {
466                              Log.d(TAG, "Found it!");
467                          }
468                          mCancelledAddress = ADDRESS_NONE;
469                          setPairingBluetooth(true);
470                          mBluetoothPairer.startPairing(target);
471                          break;
472                      }
473                  }
474              }
475          }
476      }
477  
cancelBtPairing()478      private void cancelBtPairing() {
479          // cancel current request to pair
480          if (mBluetoothPairer != null) {
481              if (mBluetoothPairer.getTargetDevice() != null) {
482                  mCancelledAddress = mBluetoothPairer.getTargetDevice().getAddress();
483              } else {
484                  mCancelledAddress = ADDRESS_NONE;
485              }
486              mBluetoothPairer.cancelPairing();
487          }
488          mPairingSuccess = false;
489          setPairingBluetooth(false);
490          mMsgHandler.sendEmptyMessageDelayed(MSG_REMOVE_CANCELED,
491                  CANCEL_MESSAGE_TIMEOUT);
492      }
493  
setPairingBluetooth(boolean pairing)494      private void setPairingBluetooth(boolean pairing) {
495          if (mPairingBluetooth != pairing) {
496              mPairingBluetooth = pairing;
497          }
498      }
499  
startBluetoothPairer()500      private void startBluetoothPairer() {
501          stopBluetoothPairer();
502          mBluetoothPairer = new BluetoothDevicePairer(this, this);
503          mBluetoothPairer.start();
504  
505          mBluetoothPairer.disableAutoPairing();
506  
507          mPairingSuccess = false;
508          statusChanged();
509      }
510  
stopBluetoothPairer()511      private void stopBluetoothPairer() {
512          if (mBluetoothPairer != null) {
513              mBluetoothPairer.setListener(null);
514              mBluetoothPairer.dispose();
515              mBluetoothPairer = null;
516          }
517      }
518  
getMessageForStatus(int status)519      private String getMessageForStatus(int status) {
520          final int msgId;
521          String msg;
522  
523          switch (status) {
524              case BluetoothDevicePairer.STATUS_WAITING_TO_PAIR:
525              case BluetoothDevicePairer.STATUS_PAIRING:
526                  msgId = R.string.accessory_state_pairing;
527                  break;
528              case BluetoothDevicePairer.STATUS_CONNECTING:
529                  msgId = R.string.accessory_state_connecting;
530                  break;
531              case BluetoothDevicePairer.STATUS_ERROR:
532                  msgId = R.string.accessory_state_error;
533                  break;
534              default:
535                  return "";
536          }
537  
538          msg = getString(msgId);
539  
540          return msg;
541      }
542  
543      @Override
statusChanged()544      public void statusChanged() {
545          if (mBluetoothPairer == null) return;
546  
547          int numDevices = mBluetoothPairer.getAvailableDevices().size();
548          int status = mBluetoothPairer.getStatus();
549          int oldStatus = mPreviousStatus;
550          mPreviousStatus = status;
551  
552          String address = mBluetoothPairer.getTargetDevice() == null ? ADDRESS_NONE :
553                  mBluetoothPairer.getTargetDevice().getAddress();
554  
555          if (DEBUG) {
556              String state = "?";
557              switch (status) {
558                  case BluetoothDevicePairer.STATUS_NONE:
559                      state = "BluetoothDevicePairer.STATUS_NONE";
560                      break;
561                  case BluetoothDevicePairer.STATUS_SCANNING:
562                      state = "BluetoothDevicePairer.STATUS_SCANNING";
563                      break;
564                  case BluetoothDevicePairer.STATUS_WAITING_TO_PAIR:
565                      state = "BluetoothDevicePairer.STATUS_WAITING_TO_PAIR";
566                      break;
567                  case BluetoothDevicePairer.STATUS_PAIRING:
568                      state = "BluetoothDevicePairer.STATUS_PAIRING";
569                      break;
570                  case BluetoothDevicePairer.STATUS_CONNECTING:
571                      state = "BluetoothDevicePairer.STATUS_CONNECTING";
572                      break;
573                  case BluetoothDevicePairer.STATUS_ERROR:
574                      state = "BluetoothDevicePairer.STATUS_ERROR";
575                      break;
576              }
577              long time = mBluetoothPairer.getNextStageTime() - SystemClock.elapsedRealtime();
578              Log.d(TAG, "Update received, number of devices:" + numDevices + " state: " +
579                      state + " target device: " + address + " time to next event: " + time);
580          }
581  
582          mBluetoothDevices.clear();
583          mBluetoothDevices.addAll(mBluetoothPairer.getAvailableDevices());
584  
585          cancelTimeout();
586  
587          switch (status) {
588              case BluetoothDevicePairer.STATUS_NONE:
589                  // if we just connected to something or just tried to connect
590                  // to something, restart scanning just in case the user wants
591                  // to pair another device.
592                  if (oldStatus == BluetoothDevicePairer.STATUS_CONNECTING) {
593                      if (mPairingSuccess) {
594                          // Pairing complete
595                          mCurrentTargetStatus = getString(R.string.accessory_state_paired);
596                          mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW);
597                          mMsgHandler.sendEmptyMessageDelayed(MSG_PAIRING_COMPLETE,
598                                  DONE_MESSAGE_TIMEOUT);
599                          mDone = true;
600  
601                          // Done, return here and just wait for the message
602                          // to close the activity
603                          return;
604                      }
605                      if (DEBUG) {
606                          Log.d(TAG, "Invalidating and restarting.");
607                      }
608  
609                      mBluetoothPairer.invalidateDevice(mBluetoothPairer.getTargetDevice());
610                      mBluetoothPairer.start();
611                      mBluetoothPairer.cancelPairing();
612                      setPairingBluetooth(false);
613  
614                      // if this looks like a successful connection run, reflect
615                      // this in the UI, otherwise use the default message
616                      if (!mPairingSuccess && BluetoothDevicePairer.hasValidInputDevice(this)) {
617                          mPairingSuccess = true;
618                      }
619                  }
620                  break;
621              case BluetoothDevicePairer.STATUS_SCANNING:
622                  mPairingSuccess = false;
623                  break;
624              case BluetoothDevicePairer.STATUS_WAITING_TO_PAIR:
625                  break;
626              case BluetoothDevicePairer.STATUS_PAIRING:
627                  // reset the pairing success value since this is now a new
628                  // pairing run
629                  mPairingSuccess = true;
630                  setTimeout(PAIR_OPERATION_TIMEOUT);
631                  break;
632              case BluetoothDevicePairer.STATUS_CONNECTING:
633                  setTimeout(CONNECT_OPERATION_TIMEOUT);
634                  break;
635              case BluetoothDevicePairer.STATUS_ERROR:
636                  mPairingSuccess = false;
637                  setPairingBluetooth(false);
638                  if (mNoInputMode) {
639                      clearDeviceList();
640                  }
641                  break;
642          }
643  
644          mCurrentTargetAddress = address;
645          mCurrentTargetStatus = getMessageForStatus(status);
646          mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW);
647      }
648  
clearDeviceList()649      private void clearDeviceList() {
650          mBluetoothDevices.clear();
651          mBluetoothPairer.clearDeviceList();
652      }
653  
handlePairingTimeout()654      private void handlePairingTimeout() {
655          if (mPairingInBackground) {
656              finish();
657          } else {
658              // Either Pairing or Connecting timeout out.
659              // Display error message and post delayed message to the scanning process.
660              mPairingSuccess = false;
661              if (mBluetoothPairer != null) {
662                  mBluetoothPairer.cancelPairing();
663              }
664              mCurrentTargetStatus = getString(R.string.accessory_state_error);
665              mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW);
666              mMsgHandler.sendEmptyMessageDelayed(MSG_RESTART, RESTART_DELAY);
667          }
668      }
669  
sendCecOtpCommand(HdmiPlaybackClient.OneTouchPlayCallback callback)670      private void sendCecOtpCommand(HdmiPlaybackClient.OneTouchPlayCallback callback) {
671          HdmiControlManager hdmiControlManager =
672                  (HdmiControlManager) getSystemService(HDMI_CONTROL_SERVICE);
673          if (hdmiControlManager == null) {
674              Log.wtf(TAG, "no HdmiControlManager");
675              return;
676          }
677          HdmiPlaybackClient client = hdmiControlManager.getPlaybackClient();
678          if (client == null) {
679              if (DEBUG) Log.d(TAG, "no HdmiPlaybackClient");
680              return;
681          }
682          client.oneTouchPlay(callback);
683      }
684  
getBluetoothDevices()685      List<BluetoothDevice> getBluetoothDevices() {
686          return mBluetoothDevices;
687      }
688  
getCurrentTargetAddress()689      String getCurrentTargetAddress() {
690          return mCurrentTargetAddress;
691      }
692  
getCurrentTargetStatus()693      String getCurrentTargetStatus() {
694          return mCurrentTargetStatus;
695      }
696  
getCancelledAddress()697      String getCancelledAddress() {
698          return mCancelledAddress;
699      }
700  }
701