HdmiControlService.java revision 8692fc68a413c7aca75f094bb02bcbabcc277c73
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
1438692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
1440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
145e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
146e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
147e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
148e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
149e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
150e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
151e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
152e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
153e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
154e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
155e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
156e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
157e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
158e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
159e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
160e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
161e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
16267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
163e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
164c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
165c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
16667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Add and start a new {@link FeatureAction} to the action queue.
167c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
16867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param action {@link FeatureAction} to add and start
169c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
17067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void addAndStartAction(final FeatureAction action) {
17167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: may need to check the number of stale actions.
17267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
17367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
17467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
17567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                mActions.add(action);
17667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                action.start();
17767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
17867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
179c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
180c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
1817fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    // See if we have an action of a given type in progress.
1827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
1837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (FeatureAction action : mActions) {
1847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            if (action.getClass().equals(clazz)) {
1857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                return true;
1867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            }
1877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
1887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        return false;
1897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
1907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
191c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
192c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Remove the given {@link FeatureAction} object from the action queue.
193c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
19467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param action {@link FeatureAction} to remove
195c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
19667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void removeAction(final FeatureAction action) {
19767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
19867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
19967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
20067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                mActions.remove(action);
20167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
20267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
20367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
20467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
20567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Remove all actions matched with the given Class type.
20667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
20767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
20867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
20967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
21067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                Iterator<FeatureAction> iter = mActions.iterator();
21167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                while (iter.hasNext()) {
21267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                    FeatureAction action = iter.next();
21367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                    if (action.getClass().equals(clazz)) {
21467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                        action.clear();
21567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                        mActions.remove(action);
21667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                    }
21767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                }
21867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
21967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
22067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
22167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
22267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void runOnServiceThread(Runnable runnable) {
22367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
22467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
22567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
22667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
22767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Change ARC status into the given {@code enabled} status.
22867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
22967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @return {@code true} if ARC was in "Enabled" status
23067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
23167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    boolean setArcStatus(boolean enabled) {
23267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        boolean oldStatus = mArcStatusEnabled;
23367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // 1. Enable/disable ARC circuit.
23467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: call set_audio_return_channel of hal interface.
23567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
23667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // 2. Update arc status;
23767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mArcStatusEnabled = enabled;
23867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return oldStatus;
239c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
240c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
241c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
242c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
243c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
244c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
245d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
246c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
247d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
248d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
249d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
250d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
251d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
252d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
253c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
254c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
255c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
256c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Add a new {@link HdmiCecDeviceInfo} to controller.
257c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
258c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param deviceInfo new device information object to add
259c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
260c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
261c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim        // TODO: Implement this.
262c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
263a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
264a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
265a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // Commands that queries system information replies directly instead
266a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // of creating FeatureAction because they are state-less.
267a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        switch (message.getOpcode()) {
268a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
269a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGetMenuLanguage(message);
270a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
271be9cd8eb3fe64a572f9c7dfc41f04defd46a752dJungshik Jang            case HdmiCec.MESSAGE_GIVE_OSD_NAME:
272be9cd8eb3fe64a572f9c7dfc41f04defd46a752dJungshik Jang                handleGiveOsdName(message);
273a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
274a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
275a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGivePhysicalAddress(message);
276a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
277a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
278a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGiveDeviceVendorId(message);
279a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
280a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            case HdmiCec.MESSAGE_GET_CEC_VERSION:
281a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                handleGetCecVersion(message);
282a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                return true;
28367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            case HdmiCec.MESSAGE_INITIATE_ARC:
28467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                handleInitiateArc(message);
28567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                return true;
28667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            case HdmiCec.MESSAGE_TERMINATE_ARC:
28767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                handleTerminateArc(message);
28867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                return true;
289a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            // TODO: Add remaining system information query such as
290a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            // <Give Device Power Status> and <Request Active Source> handler.
291a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            default:
292c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim                return dispatchMessageToAction(message);
293a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
294a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
295a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
29667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
29767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
29867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
29967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param port hdmi port number where hot plug event issued.
30067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
30167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
30267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
30367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: Start "RequestArcInitiationAction" if ARC port.
30467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
30567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
30602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
30702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
30802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
30902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
31002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
31102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
31202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
31302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    void pollDevices(DevicePollingCallback callback, int retryCount) {
31402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        mCecController.pollDevices(callback, retryCount);
31502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
31602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
31767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void handleInitiateArc(HdmiCecMessage message){
31867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // In case where <Initiate Arc> is started by <Request ARC Initiation>
31967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // need to clean up RequestArcInitiationAction.
32067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        removeAction(RequestArcInitiationAction.class);
32167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
32267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                message.getDestination(), message.getSource(), true);
32367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        addAndStartAction(action);
32467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
32567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
32667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void handleTerminateArc(HdmiCecMessage message) {
32767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // In case where <Terminate Arc> is started by <Request ARC Termination>
32867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // need to clean up RequestArcInitiationAction.
32967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: check conditions of power status by calling is_connected api
33067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // to be added soon.
33167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        removeAction(RequestArcTerminationAction.class);
33267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
33367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                message.getDestination(), message.getSource(), false);
33467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        addAndStartAction(action);
33567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
33667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
337a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGetCecVersion(HdmiCecMessage message) {
338a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int version = mCecController.getVersion();
339a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
340a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getSource(),
341a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                version);
342a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        sendCecCommand(cecMessage);
343a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
344a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
345a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGiveDeviceVendorId(HdmiCecMessage message) {
346a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int vendorId = mCecController.getVendorId();
347a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
348a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(), vendorId);
349a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        sendCecCommand(cecMessage);
350a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
351a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
352a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGivePhysicalAddress(HdmiCecMessage message) {
353a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int physicalAddress = mCecController.getPhysicalAddress();
354a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        int deviceType = HdmiCec.getTypeFromAddress(message.getDestination());
355a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
356a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(), physicalAddress, deviceType);
357a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        sendCecCommand(cecMessage);
358a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
359a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
360be9cd8eb3fe64a572f9c7dfc41f04defd46a752dJungshik Jang    private void handleGiveOsdName(HdmiCecMessage message) {
361a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // TODO: read device name from settings or property.
362a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        String name = HdmiCec.getDefaultDeviceName(message.getDestination());
363a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
364a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(), message.getSource(), name);
365a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        if (cecMessage != null) {
366a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            sendCecCommand(cecMessage);
367a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        } else {
368a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            Slog.w(TAG, "Failed to build <Get Osd Name>:" + name);
369a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
370a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
371a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
372a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private void handleGetMenuLanguage(HdmiCecMessage message) {
373a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // Only 0 (TV), 14 (specific use) can answer.
374a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        if (message.getDestination() != HdmiCec.ADDR_TV
375a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) {
376a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
377a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            sendCecCommand(
378a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                    HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
379a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                            message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
380a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                            HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
381a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            return;
382a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
383a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
384a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
385a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                message.getDestination(),
386a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang                Locale.getDefault().getISO3Language());
387a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // TODO: figure out how to handle failed to get language code.
388a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        if (command != null) {
389a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            sendCecCommand(command);
390a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        } else {
391a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
392a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
393a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
39478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
395c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim    private boolean dispatchMessageToAction(HdmiCecMessage message) {
396c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        for (FeatureAction action : mActions) {
397c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim            if (action.processCommand(message)) {
398c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim                return true;
399c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim            }
400c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        }
401c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        Slog.w(TAG, "Unsupported cec command:" + message);
402c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        return false;
403c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim    }
404c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim
40578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
40678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
40778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
40878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
40978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
41078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
41178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
41278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
41378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
41478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
41578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
41678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
41778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
41878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
41978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
42078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
42178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
42278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
423cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    void addCecDevice(HdmiCecDeviceInfo info) {
424cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCecController.addDeviceInfo(info);
425cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
426cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
427cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // Launch device discovery sequence.
428cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // It starts with clearing the existing device info list.
429cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // Note that it assumes that logical address of all local devices is already allocated.
430cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    void launchDeviceDiscovery() {
431cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        // At first, clear all existing device infos.
432cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCecController.clearDeviceInfoList();
433cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
434cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        // TODO: check whether TV is one of local devices.
435cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, HdmiCec.ADDR_TV,
436cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                new DeviceDiscoveryCallback() {
437cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    @Override
438cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
439cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        for (HdmiCecDeviceInfo info : deviceInfos) {
440cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                            mCecController.addDeviceInfo(info);
441cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        }
442cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
443cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        // Add device info of all local devices.
444cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
445cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                            mCecController.addDeviceInfo(device.getDeviceInfo());
446cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        }
447cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
448cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        // TODO: start hot-plug detection sequence here.
449cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                        // addAndStartAction(new HotplugDetectionAction());
450cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    }
451cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                });
452cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        addAndStartAction(action);
453cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
454cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
45578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
45678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
45778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
45878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
45978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
46078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
46178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
46278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
46378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
46478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                return mLocalDevices;
46578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
46678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
46778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
46878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4697fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
47078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4727fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
4757fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
4767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
47778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
47878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
47978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
48178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
4867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
4877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
48878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
48978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
49078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
4917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
49278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
4947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
4957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
4967fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
4977fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
4987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
49978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
50078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
50178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
5027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
50378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
5087fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5097fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
51078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
51178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
51278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
51378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void oneTouchPlay(IHdmiControlCallback callback) {
5147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (hasAction(OneTouchPlayAction.class)) {
5157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "oneTouchPlay already in progress");
5167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
5177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5187fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5197fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
5207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
5217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
5227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
5237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        // TODO: Consider the case of multiple TV sets. For now we always direct the command
5267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        //       to the primary one.
5277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        OneTouchPlayAction action = OneTouchPlayAction.create(this,
5287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getLogicalAddress(),
5297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
5307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (action == null) {
5317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Cannot initiate oneTouchPlay");
5327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
5337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        addAndStartAction(action);
53678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
53778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
53878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void queryDisplayStatus(IHdmiControlCallback callback) {
5397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (hasAction(DevicePowerStatusAction.class)) {
5407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "queryDisplayStatus already in progress");
5417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
5427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
5457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
5467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
5477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
5487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5507fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
5517fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
5527fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (action == null) {
5537fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
5547fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
5557fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5577fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        addAndStartAction(action);
55878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
55978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
56178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
56278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
56378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
56478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
56578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
56678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
56778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
56878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
56978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
57078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
57178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
57278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
57378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
57478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
57578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
57678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
57778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
57878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
57978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
58078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
58178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
58278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
58378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
58478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
58578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
5867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
5877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
5887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
5897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
5907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
5917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
5927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
5937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
5940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
595