1a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim/* 2a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Copyright (C) 2014 The Android Open Source Project 3a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 4a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Licensed under the Apache License, Version 2.0 (the "License"); 5a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * you may not use this file except in compliance with the License. 6a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * You may obtain a copy of the License at 7a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 8a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * http://www.apache.org/licenses/LICENSE-2.0 9a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 10a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Unless required by applicable law or agreed to in writing, software 11a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * distributed under the License is distributed on an "AS IS" BASIS, 12a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * See the License for the specific language governing permissions and 14a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * limitations under the License. 15a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim */ 16a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 17a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kimpackage com.android.server.hdmi; 18a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo; 20c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager; 2179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.hardware.hdmi.HdmiTvClient; 2279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback; 23a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kimimport android.os.RemoteException; 24a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kimimport android.util.Slog; 25a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 26b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kimimport com.android.server.hdmi.HdmiControlService.SendMessageCallback; 27b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim 28a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim/** 29a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Handles an action that selects a logical device as a new active source. 30a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 31a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Triggered by {@link HdmiTvClient}, attempts to select the given target device 32a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * for a new active source. It does its best to wake up the target in standby mode 33a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * before issuing the command >Set Stream path<. 34a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim */ 35b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangfinal class DeviceSelectAction extends HdmiCecFeatureAction { 36a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final String TAG = "DeviceSelect"; 37a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 38a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // Time in milliseconds we wait for the device power status to switch to 'Standby' 39a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000; 40a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 41a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // Time in milliseconds we wait for the device power status to turn to 'On'. 42a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final int TIMEOUT_POWER_ON_MS = 5 * 1000; 43a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 44a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // The number of times we try to wake up the target device before we give up 45a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // and just send <Set Stream Path>. 46a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final int LOOP_COUNTER_MAX = 20; 47a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 48a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // State in which we wait for <Report Power Status> to come in response to the command 49a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // <Give Device Power Status> we have sent. 50a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; 51a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 52a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // State in which we wait for the device power status to switch to 'Standby'. 53a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // We wait till the status becomes 'Standby' before we send <Set Stream Path> 54a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // to wake up the device again. 55a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2; 56a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 57a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // State in which we wait for the device power status to switch to 'on'. We wait 58a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>. 59a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; 60a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 6161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang private final HdmiDeviceInfo mTarget; 62a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private final IHdmiControlCallback mCallback; 63b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim private final HdmiCecMessage mGivePowerStatus; 64a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 65a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private int mPowerStatusCounter = 0; 66a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 67a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim /** 68a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Constructor. 69a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 7079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang * @param source {@link HdmiCecLocalDevice} instance 71a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * @param target target logical device that will be a new active source 72a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * @param callback callback object 73a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim */ 748333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim public DeviceSelectAction(HdmiCecLocalDeviceTv source, 7561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang HdmiDeviceInfo target, IHdmiControlCallback callback) { 7679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang super(source); 77a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mCallback = callback; 78a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mTarget = target; 79b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( 80b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim getSourceAddress(), getTargetAddress()); 81b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim } 82b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim 83b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim int getTargetAddress() { 84b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim return mTarget.getLogicalAddress(); 85a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 86a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 87a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim @Override 88a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim public boolean start() { 89b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim // Seq #9 90a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim queryDevicePowerStatus(); 91a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return true; 92a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 93a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 94a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private void queryDevicePowerStatus() { 95b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim sendCommand(mGivePowerStatus, new SendMessageCallback() { 96b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim @Override 97b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim public void onSendCompleted(int error) { 985352081c662299b618335bf3024058fa04ef2dfdJungshik Jang if (error != Constants.SEND_RESULT_SUCCESS) { 99b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim invokeCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED); 100b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim finish(); 101b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim return; 102b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim } 103b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim } 104b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim }); 105a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; 1065fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim addTimer(mState, HdmiConfig.TIMEOUT_MS); 107a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 108a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 109a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim @Override 110a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim public boolean processCommand(HdmiCecMessage cmd) { 111b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim if (cmd.getSource() != getTargetAddress()) { 112a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return false; 113a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 114a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim int opcode = cmd.getOpcode(); 115a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim byte[] params = cmd.getParams(); 116a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 117a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim switch (mState) { 118a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim case STATE_WAIT_FOR_REPORT_POWER_STATUS: 11975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo if (opcode == Constants.MESSAGE_REPORT_POWER_STATUS) { 120a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return handleReportPowerStatus(params[0]); 121a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 122a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return false; 123a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim default: 124a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim break; 125a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 126a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return false; 127a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 128a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 129a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private boolean handleReportPowerStatus(int powerStatus) { 130a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim switch (powerStatus) { 131c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case HdmiControlManager.POWER_STATUS_ON: 132a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim sendSetStreamPath(); 133a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return true; 134c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: 135a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim if (mPowerStatusCounter < 4) { 136a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY; 137a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS); 138a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } else { 139a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim sendSetStreamPath(); 140a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 141a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return true; 142c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case HdmiControlManager.POWER_STATUS_STANDBY: 143a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim if (mPowerStatusCounter == 0) { 144a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim turnOnDevice(); 145a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } else { 146a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim sendSetStreamPath(); 147a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 148a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return true; 149c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: 150a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim if (mPowerStatusCounter < LOOP_COUNTER_MAX) { 151a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mState = STATE_WAIT_FOR_DEVICE_POWER_ON; 152a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim addTimer(mState, TIMEOUT_POWER_ON_MS); 153a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } else { 154a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim sendSetStreamPath(); 155a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 156a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return true; 157a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 158a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return false; 159a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 160a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 161a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private void turnOnDevice() { 1628fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), 163210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang HdmiCecKeycode.CEC_KEYCODE_POWER); 1648fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), 165210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION); 166a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mState = STATE_WAIT_FOR_DEVICE_POWER_ON; 167a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim addTimer(mState, TIMEOUT_POWER_ON_MS); 168a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 169a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 170a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private void sendSetStreamPath() { 1717543497f8a8a8ba15edd622061f5d30dfbf6655aJinsuk Kim // Turn the active source invalidated, which remains so till <Active Source> comes from 1727543497f8a8a8ba15edd622061f5d30dfbf6655aJinsuk Kim // the selected device. 1737543497f8a8a8ba15edd622061f5d30dfbf6655aJinsuk Kim tv().getActiveSource().invalidate(); 1747543497f8a8a8ba15edd622061f5d30dfbf6655aJinsuk Kim tv().setActivePath(mTarget.getPhysicalAddress()); 175a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( 17679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang getSourceAddress(), mTarget.getPhysicalAddress())); 1778e083ec0444e52cb203228bcf1c1cd34bcc4e70dJinsuk Kim invokeCallback(HdmiControlManager.RESULT_SUCCESS); 1788e083ec0444e52cb203228bcf1c1cd34bcc4e70dJinsuk Kim finish(); 179a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 180a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 181a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim @Override 182a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim public void handleTimerEvent(int timeoutState) { 183a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim if (mState != timeoutState) { 184a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim Slog.w(TAG, "Timer in a wrong state. Ignored."); 185a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return; 186a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 187a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim switch (mState) { 188a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim case STATE_WAIT_FOR_REPORT_POWER_STATUS: 189b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim if (tv().isPowerStandbyOrTransient()) { 190b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim invokeCallback(HdmiControlManager.RESULT_INCORRECT_MODE); 191b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim finish(); 192b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim return; 193b38cd68b240d99e8c8ae6ff1802a574696b420cdJinsuk Kim } 194a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim sendSetStreamPath(); 195a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim break; 196a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY: 197a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim case STATE_WAIT_FOR_DEVICE_POWER_ON: 198a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mPowerStatusCounter++; 199a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim queryDevicePowerStatus(); 200a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim break; 201a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 202a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 203a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 204a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim private void invokeCallback(int result) { 205a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim if (mCallback == null) { 206a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return; 207a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 208a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim try { 209a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mCallback.onComplete(result); 210a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } catch (RemoteException e) { 211a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim Slog.e(TAG, "Callback failed:" + e); 212a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 213a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 214a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim} 215