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 <Polling Message> 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