HdmiControlService.java revision cc5ef8c918e96516a5c51cc40735a1b8a24d8497
10792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/*
20792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Copyright (C) 2014 The Android Open Source Project
30792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
40792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License");
50792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * you may not use this file except in compliance with the License.
60792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * You may obtain a copy of the License at
70792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
80792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *      http://www.apache.org/licenses/LICENSE-2.0
90792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Unless required by applicable law or agreed to in writing, software
110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS,
120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * See the License for the specific language governing permissions and
140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * limitations under the License.
150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpackage com.android.server.hdmi;
180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
21a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jangimport android.hardware.hdmi.HdmiCec;
22c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
23c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecMessage;
24d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
25d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
26d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
2767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
2978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
30e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
3178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
3378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
3478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport com.android.internal.annotations.GuardedBy;
350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
36cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
3878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
3967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport java.util.Iterator;
4067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport java.util.LinkedList;
4102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
42a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jangimport java.util.Locale;
43a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
5278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
5378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
54ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_SUCCESS = 0;
55ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_NAK = -1;
56ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_FAILURE = -2;
57ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo
58d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
59d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
60d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
61d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
62d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
63d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
64d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
65ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
66ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
67ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
68ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
69d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
70d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
71d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
7302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
7402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
7502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
7602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
7702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
7802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
7902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
8002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
8102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
8202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
9067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // A collection of FeatureAction.
9167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Note that access to this collection should happen in service thread.
9267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private final LinkedList<FeatureAction> mActions = new LinkedList<>();
9367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
9478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
9578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
9678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
9778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Type of logical devices hosted in the system.
9878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    @GuardedBy("mLock")
9978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final int[] mLocalDevices;
10078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
10178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
10278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
10378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
10478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
10578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
10678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
10778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
10878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1090792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
11567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Whether ARC is "enabled" or not.
11667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // TODO: it may need to hold lock if it's accessed from others.
11767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private boolean mArcStatusEnabled = false;
11867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
11967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Handler running on service thread. It's used to run a task in service thread.
12067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private Handler mHandler = new Handler();
12167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
1220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
12478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        mLocalDevices = getContext().getResources().getIntArray(
12578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType);
1260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
1290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
1302f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
131e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
132a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        if (mCecController != null) {
13378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mCecController.initializeLocalDevices(mLocalDevices);
134a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
1350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
1360792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
138e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
1390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
1400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
1410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
14278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
14378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        // TODO: Publish the BinderService
14478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        // publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
146e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
147e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
148e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
149e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
150e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
151e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
152e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
153e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
154e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
155e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
156e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
157e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
158e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
159e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
160e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
161e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
162e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
16367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
164e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
165c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
166c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
16767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Add and start a new {@link FeatureAction} to the action queue.
168c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
16967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param action {@link FeatureAction} to add and start
170c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
17167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void addAndStartAction(final FeatureAction action) {
17267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: may need to check the number of stale actions.
17367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
17467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
17567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
17667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                mActions.add(action);
17767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                action.start();
17867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
17967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
180c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
181c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
1827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    // See if we have an action of a given type in progress.
1837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
1847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (FeatureAction action : mActions) {
1857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            if (action.getClass().equals(clazz)) {
1867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                return true;
1877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            }
1887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
1897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        return false;
1907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
1917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
192c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
193c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Remove the given {@link FeatureAction} object from the action queue.
194c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
19567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param action {@link FeatureAction} to remove
196c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
19767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void removeAction(final FeatureAction action) {
19867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
19967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
20067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
20167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                mActions.remove(action);
20267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
20367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
20467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
20567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
20667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Remove all actions matched with the given Class type.
20767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
20867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
20967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
21067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
21167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                Iterator<FeatureAction> iter = mActions.iterator();
21267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                while (iter.hasNext()) {
21367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                    FeatureAction action = iter.next();
21467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                    if (action.getClass().equals(clazz)) {
21567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                        action.clear();
21667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                        mActions.remove(action);
21767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                    }
21867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                }
21967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
22067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
22167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
22267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
22367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void runOnServiceThread(Runnable runnable) {
22467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
22567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
22667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
22767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
22867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Change ARC status into the given {@code enabled} status.
22967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
23067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @return {@code true} if ARC was in "Enabled" status
23167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
23267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    boolean setArcStatus(boolean enabled) {
23367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        boolean oldStatus = mArcStatusEnabled;
23467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // 1. Enable/disable ARC circuit.
23567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: call set_audio_return_channel of hal interface.
23667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
23767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // 2. Update arc status;
23867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mArcStatusEnabled = enabled;
23967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return oldStatus;
240c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
241c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
242c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
243c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
244c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
245c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
246d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
247c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
248d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
249d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
250d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
251d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
252d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
253d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
254c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
255c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
256c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
257c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Add a new {@link HdmiCecDeviceInfo} to controller.
258c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
259c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param deviceInfo new device information object to add
260c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
261c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
262c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim        // TODO: Implement this.
263c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
264a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
265a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
266a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // Commands that queries system information replies directly instead
267a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // of creating FeatureAction because they are state-less.
268a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        switch (message.getOpcode()) {
269a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
270a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGetMenuLanguage(message);
271a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
272be9cd8eb3fe64a572f9c7dfc41f04defd46a752dJungshik Jang            case HdmiCec.MESSAGE_GIVE_OSD_NAME:
273be9cd8eb3fe64a572f9c7dfc41f04defd46a752dJungshik Jang                handleGiveOsdName(message);
274a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
275a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
276a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGivePhysicalAddress(message);
277a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
278a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
279a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGiveDeviceVendorId(message);
280a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
281a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GET_CEC_VERSION:
282a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGetCecVersion(message);
283a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
28467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            case HdmiCec.MESSAGE_INITIATE_ARC:
28567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                handleInitiateArc(message);
28667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                return true;
28767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            case HdmiCec.MESSAGE_TERMINATE_ARC:
28867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                handleTerminateArc(message);
28967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                return true;
290a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            // TODO: Add remaining system information query such as
291a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            // <Give Device Power Status> and <Request Active Source> handler.
292a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            default:
293a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                Slog.w(TAG, "Unsupported cec command:" + message.toString());
294a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return false;
295a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
296a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
297a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
29867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
29967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
30067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
30167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param port hdmi port number where hot plug event issued.
30267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
30367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
30467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
30567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: Start "RequestArcInitiationAction" if ARC port.
30667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
30767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
30802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
30902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
31002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
31102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
31202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
31302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
31402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
31502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    void pollDevices(DevicePollingCallback callback, int retryCount) {
31602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        mCecController.pollDevices(callback, retryCount);
31702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
31802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
31967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void handleInitiateArc(HdmiCecMessage message){
32067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // In case where <Initiate Arc> is started by <Request ARC Initiation>
32167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // need to clean up RequestArcInitiationAction.
32267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        removeAction(RequestArcInitiationAction.class);
32367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
32467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                message.getDestination(), message.getSource(), true);
32567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        addAndStartAction(action);
32667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
32767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
32867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void handleTerminateArc(HdmiCecMessage message) {
32967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // In case where <Terminate Arc> is started by <Request ARC Termination>
33067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // need to clean up RequestArcInitiationAction.
33167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: check conditions of power status by calling is_connected api
33267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // to be added soon.
33367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        removeAction(RequestArcTerminationAction.class);
33467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
33567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                message.getDestination(), message.getSource(), false);
33667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        addAndStartAction(action);
33767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
33867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
339a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGetCecVersion(HdmiCecMessage message) {
340a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int version = mCecController.getVersion();
341a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
342a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getSource(),
343a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                version);
344a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        sendCecCommand(cecMessage);
345a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
346a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
347a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGiveDeviceVendorId(HdmiCecMessage message) {
348a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int vendorId = mCecController.getVendorId();
349a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
350a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(), vendorId);
351a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        sendCecCommand(cecMessage);
352a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
353a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
354a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGivePhysicalAddress(HdmiCecMessage message) {
355a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int physicalAddress = mCecController.getPhysicalAddress();
356a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int deviceType = HdmiCec.getTypeFromAddress(message.getDestination());
357a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
358a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(), physicalAddress, deviceType);
359a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        sendCecCommand(cecMessage);
360a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
361a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
362be9cd8eb3fe64a572f9c7dfc41f04defd46a752dJungshik Jang    private void handleGiveOsdName(HdmiCecMessage message) {
363a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // TODO: read device name from settings or property.
364a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        String name = HdmiCec.getDefaultDeviceName(message.getDestination());
365a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
366a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(), message.getSource(), name);
367a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        if (cecMessage != null) {
368a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            sendCecCommand(cecMessage);
369a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        } else {
370a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            Slog.w(TAG, "Failed to build <Get Osd Name>:" + name);
371a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
372a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
373a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
374a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGetMenuLanguage(HdmiCecMessage message) {
375a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // Only 0 (TV), 14 (specific use) can answer.
376a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        if (message.getDestination() != HdmiCec.ADDR_TV
377a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) {
378a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
379a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            sendCecCommand(
380a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                    HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
381a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                            message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
382a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                            HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
383a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            return;
384a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
385a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
386a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
387a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(),
388a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                Locale.getDefault().getISO3Language());
389a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // TODO: figure out how to handle failed to get language code.
390a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        if (command != null) {
391a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            sendCecCommand(command);
392a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        } else {
393a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
394a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
395a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
39678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
39778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
39878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
39978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
40078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
40178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
40278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
40378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
40478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
40578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
40678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
40778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
40878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
40978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
41078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
41178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
41278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
41378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
41478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
415cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    void addCecDevice(HdmiCecDeviceInfo info) {
416cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCecController.addDeviceInfo(info);
417cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
418cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
419cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // Launch device discovery sequence.
420cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // It starts with clearing the existing device info list.
421cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // Note that it assumes that logical address of all local devices is already allocated.
422cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    void launchDeviceDiscovery() {
423cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        // At first, clear all existing device infos.
424cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCecController.clearDeviceInfoList();
425cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
426cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        // TODO: check whether TV is one of local devices.
427cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, HdmiCec.ADDR_TV,
428cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                new DeviceDiscoveryCallback() {
429cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    @Override
430cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
431cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        for (HdmiCecDeviceInfo info : deviceInfos) {
432cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                            mCecController.addDeviceInfo(info);
433cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        }
434cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
435cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        // Add device info of all local devices.
436cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
437cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                            mCecController.addDeviceInfo(device.getDeviceInfo());
438cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        }
439cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
440cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        // TODO: start hot-plug detection sequence here.
441cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        // addAndStartAction(new HotplugDetectionAction());
442cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    }
443cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                });
444cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        addAndStartAction(action);
445cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
446cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
44778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
44878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
44978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
45078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
45178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
45278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
45378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
45478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
45578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
45678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                return mLocalDevices;
45778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
45878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
45978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
46078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
46278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
4677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
4687fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
46978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
47078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
47178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4727fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
47378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4757fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4777fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
4787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
4797fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
48078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
48178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
48278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
48478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
4897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
4907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
49178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
49278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
49378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
49578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4967fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4977fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4997fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
5007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
50278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
50378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
50478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
50578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void oneTouchPlay(IHdmiControlCallback callback) {
5067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (hasAction(OneTouchPlayAction.class)) {
5077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "oneTouchPlay already in progress");
5087fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
5097fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
5127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
5137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
5147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
5157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        // TODO: Consider the case of multiple TV sets. For now we always direct the command
5187fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        //       to the primary one.
5197fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        OneTouchPlayAction action = OneTouchPlayAction.create(this,
5207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getLogicalAddress(),
5217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
5227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (action == null) {
5237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Cannot initiate oneTouchPlay");
5247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
5257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        addAndStartAction(action);
52878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
52978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
53078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void queryDisplayStatus(IHdmiControlCallback callback) {
5317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (hasAction(DevicePowerStatusAction.class)) {
5327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "queryDisplayStatus already in progress");
5337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
5347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
5377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
5387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
5397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
5407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
5437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
5447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (action == null) {
5457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
5467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
5477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        addAndStartAction(action);
55078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
55178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
55278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
55378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
55478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
55578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
55678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
55778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
55878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
55978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
56078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
56178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
56278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
56378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
56478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
56578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
56778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
56878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
56978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
57078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
57178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
57278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
57378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
57478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
57578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
57678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
57778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
5787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
5797fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
5807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
5817fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
5827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
5837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
5847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
5860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
587