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