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