163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo/*
263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Copyright (C) 2014 The Android Open Source Project
363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *
463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Licensed under the Apache License, Version 2.0 (the "License");
563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * you may not use this file except in compliance with the License.
663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * You may obtain a copy of the License at
763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *
863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *      http://www.apache.org/licenses/LICENSE-2.0
963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *
1063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Unless required by applicable law or agreed to in writing, software
1163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * distributed under the License is distributed on an "AS IS" BASIS,
1263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * See the License for the specific language governing permissions and
1463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * limitations under the License.
1563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo */
1663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heopackage com.android.server.hdmi;
1863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
19ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.annotation.Nullable;
2061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
21c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
22ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
23ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.os.RemoteException;
24ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.util.Slog;
2563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
26c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heoimport java.util.List;
27c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo
2863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo/**
2963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
3063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo */
31b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangabstract class SystemAudioAction extends HdmiCecFeatureAction {
3263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private static final String TAG = "SystemAudioAction";
3363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
34c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    // Transient state to differentiate with STATE_NONE where the on-finished callback
35c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    // will not be called.
36c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    private static final int STATE_CHECK_ROUTING_IN_PRGRESS = 1;
37c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo
3863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    // State in which waits for <SetSystemAudioMode>.
39c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 2;
4063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
4163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private static final int MAX_SEND_RETRY_COUNT = 2;
4263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
4363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private static final int ON_TIMEOUT_MS = 5000;
445fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim    private static final int OFF_TIMEOUT_MS = HdmiConfig.TIMEOUT_MS;
4563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
4663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    // Logical address of AV Receiver.
4763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    protected final int mAvrLogicalAddress;
4863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
4963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    // The target audio status of the action, whether to enable the system audio mode or not.
5063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    protected boolean mTargetAudioStatus;
5163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
52ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    @Nullable private final IHdmiControlCallback mCallback;
53ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
5463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private int mSendRetryCount = 0;
5563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
5663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    /**
5763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * Constructor
5863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     *
5979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param source {@link HdmiCecLocalDevice} instance
6063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param avrAddress logical address of AVR device
6163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param targetStatus Whether to enable the system audio mode or not
62ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang     * @param callback callback interface to be notified when it's done
6363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
6463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     */
65ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
66ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            IHdmiControlCallback callback) {
6779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        super(source);
6861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
6963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mAvrLogicalAddress = avrAddress;
7063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mTargetAudioStatus = targetStatus;
71ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        mCallback = callback;
7263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
7363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
74c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    // Seq #27
7563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    protected void sendSystemAudioModeRequest() {
76c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        List<RoutingControlAction> routingActions = getActions(RoutingControlAction.class);
77c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        if (!routingActions.isEmpty()) {
78a7221ce87683fab16603290378408ce92f02e88aJungshik Jang            mState = STATE_CHECK_ROUTING_IN_PRGRESS;
79c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            // Should have only one Routing Control Action
80c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            RoutingControlAction routingAction = routingActions.get(0);
81c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            routingAction.addOnFinishedCallback(this, new Runnable() {
82c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                @Override
83c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                public void run() {
84c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                    sendSystemAudioModeRequestInternal();
85c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                }
86c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            });
87c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            return;
88c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        }
89c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        sendSystemAudioModeRequestInternal();
90c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    }
91c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo
92c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    private void sendSystemAudioModeRequestInternal() {
9379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
9479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                getSourceAddress(),
95e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim                mAvrLogicalAddress, getSystemAudioModeRequestParam(), mTargetAudioStatus);
9663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        sendCommand(command, new HdmiControlService.SendMessageCallback() {
9763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            @Override
9863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            public void onSendCompleted(int error) {
99a7221ce87683fab16603290378408ce92f02e88aJungshik Jang                if (error != Constants.SEND_RESULT_SUCCESS) {
1002e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                    HdmiLogger.debug("Failed to send <System Audio Mode Request>:" + error);
10163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    setSystemAudioMode(false);
102d05f67f9721e1f9194a1f57cf7481b4be65366b3Yuncheol Heo                    finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
10363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                }
10463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            }
10563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        });
106a7221ce87683fab16603290378408ce92f02e88aJungshik Jang        mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
107a7221ce87683fab16603290378408ce92f02e88aJungshik Jang        addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
10863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
10963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
110e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim    private int getSystemAudioModeRequestParam() {
111e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        // <System Audio Mode Request> takes the physical address of the source device
112e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        // as a parameter. Get it from following candidates, in the order listed below:
113e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        // 1) physical address of the active source
114e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        // 2) active routing path
115e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        // 3) physical address of TV
116e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        if (tv().getActiveSource().isValid()) {
117e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim            return tv().getActiveSource().physicalAddress;
118e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        }
119e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        int param = tv().getActivePath();
120e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim        return param != Constants.INVALID_PHYSICAL_ADDRESS
121e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim                ? param : Constants.PATH_INTERNAL;
122e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim    }
123e65360216890886b2fa507ddb656820c166bba22Jinsuk Kim
12463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void handleSendSystemAudioModeRequestTimeout() {
12563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (!mTargetAudioStatus  // Don't retry for Off case.
12663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
1272e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.debug("[T]:wait for <Set System Audio Mode>.");
12863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            setSystemAudioMode(false);
129c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
13063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            return;
13163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
13263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        sendSystemAudioModeRequest();
13363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
13463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
13563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    protected void setSystemAudioMode(boolean mode) {
1367ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        tv().setSystemAudioMode(mode, true);
13763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
13863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
13963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    @Override
14063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    final boolean processCommand(HdmiCecMessage cmd) {
1415352081c662299b618335bf3024058fa04ef2dfdJungshik Jang        if (cmd.getSource() != mAvrLogicalAddress) {
1425352081c662299b618335bf3024058fa04ef2dfdJungshik Jang            return false;
1435352081c662299b618335bf3024058fa04ef2dfdJungshik Jang        }
14463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        switch (mState) {
14563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
146d05f67f9721e1f9194a1f57cf7481b4be65366b3Yuncheol Heo                if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
147339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang                        && (cmd.getParams()[0] & 0xFF)
148339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang                                == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
1492e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                    HdmiLogger.debug("Failed to start system audio mode request.");
150d05f67f9721e1f9194a1f57cf7481b4be65366b3Yuncheol Heo                    setSystemAudioMode(false);
151d05f67f9721e1f9194a1f57cf7481b4be65366b3Yuncheol Heo                    finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
152d05f67f9721e1f9194a1f57cf7481b4be65366b3Yuncheol Heo                    return true;
153d05f67f9721e1f9194a1f57cf7481b4be65366b3Yuncheol Heo                }
154c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (cmd.getOpcode() != Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE
15563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                        || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
15663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    return false;
15763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                }
15863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
15963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                if (receivedStatus == mTargetAudioStatus) {
16063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    setSystemAudioMode(receivedStatus);
16116321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang                    startAudioStatusAction();
16216321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang                    return true;
16363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                } else {
1642e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                    HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
16563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    // Unexpected response, consider the request is newly initiated by AVR.
16663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    // To return 'false' will initiate new SystemAudioActionFromAvr by the control
16763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    // service.
168c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
16963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    return false;
17063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                }
17116321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang            default:
17216321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang                return false;
17363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
17416321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang    }
17516321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang
17616321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang    protected void startAudioStatusAction() {
177ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback));
17816321b80077815eebeceee249ff7a29e336e04a4Jungshik Jang        finish();
17963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
18063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
18163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    protected void removeSystemAudioActionInProgress() {
18279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        removeActionExcept(SystemAudioActionFromTv.class, this);
18379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        removeActionExcept(SystemAudioActionFromAvr.class, this);
18463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
18563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
18663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    @Override
18763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    final void handleTimerEvent(int state) {
18863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (mState != state) {
18963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            return;
19063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
19163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        switch (mState) {
19263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
19363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                handleSendSystemAudioModeRequestTimeout();
19463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                return;
19563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
19663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
197ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
198ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // TODO: if IHdmiControlCallback is general to other FeatureAction,
199ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    //       move it into FeatureAction.
200ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    protected void finishWithCallback(int returnCode) {
201ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        if (mCallback != null) {
202ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            try {
203ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mCallback.onComplete(returnCode);
204ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            } catch (RemoteException e) {
205ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                Slog.e(TAG, "Failed to invoke callback.", e);
206ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
207ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
208ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        finish();
209ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
21063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo}
211