12918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim/* 22918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Copyright (C) 2014 The Android Open Source Project 32918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * 42918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Licensed under the Apache License, Version 2.0 (the "License"); 52918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * you may not use this file except in compliance with the License. 62918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * You may obtain a copy of the License at 72918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * 82918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * http://www.apache.org/licenses/LICENSE-2.0 92918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * 102918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Unless required by applicable law or agreed to in writing, software 112918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * distributed under the License is distributed on an "AS IS" BASIS, 122918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * See the License for the specific language governing permissions and 142918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * limitations under the License. 152918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim */ 162918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim 172918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kimpackage com.android.server.hdmi; 182918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim 19c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager; 2064bafd9108b532102bc13c47966444b073c33fafYuncheol Heoimport android.hardware.hdmi.HdmiDeviceInfo; 2179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback; 22e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kimimport android.os.PowerManager; 23e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kimimport android.os.PowerManager.WakeLock; 2479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.os.RemoteException; 25af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kimimport android.os.SystemProperties; 26e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kimimport android.provider.Settings.Global; 2779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.util.Slog; 282918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim 29795415b57b7271a11d16ca42703251a325df1097Terry Heoimport com.android.internal.app.LocalePicker; 30795415b57b7271a11d16ca42703251a325df1097Terry Heoimport com.android.internal.app.LocalePicker.LocaleInfo; 31959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport com.android.internal.util.IndentingPrintWriter; 32a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; 33a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang 34795415b57b7271a11d16ca42703251a325df1097Terry Heoimport java.io.UnsupportedEncodingException; 35795415b57b7271a11d16ca42703251a325df1097Terry Heoimport java.util.List; 368ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kimimport java.util.Locale; 37795415b57b7271a11d16ca42703251a325df1097Terry Heo 38cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kimimport java.util.List; 39cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim 402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim/** 412918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Represent a logical device of type Playback residing in Android system. 422918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim */ 432918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kimfinal class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { 4479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang private static final String TAG = "HdmiCecLocalDevicePlayback"; 452918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim 46659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim private static final boolean WAKE_ON_HOTPLUG = 47659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true); 48659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim 49a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho private static final boolean SET_MENU_LANGUAGE = 50a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false); 51a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho 5238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo private boolean mIsActiveSource = false; 5338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 54e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // Used to keep the device awake while it is the active source. For devices that 55e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // cannot wake up via CEC commands, this address the inconvenience of having to 56bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // turn them on. True by default, and can be disabled (i.e. device can go to sleep 57bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // in active device status) by explicitly setting the system property 58bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // persist.sys.hdmi.keep_awake to false. 59e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // Lazily initialized - should call getWakeLock() to get the instance. 60bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private ActiveWakeLock mWakeLock; 61e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim 62e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // If true, turn off TV upon standby. False by default. 63e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim private boolean mAutoTvOff; 64e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang HdmiCecLocalDevicePlayback(HdmiControlService service) { 6661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang super(service, HdmiDeviceInfo.DEVICE_PLAYBACK); 67e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 68e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mAutoTvOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, false); 69e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 70e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // The option is false by default. Update settings db as well to have the right 71e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // initial setting on UI. 72e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, mAutoTvOff); 738b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang } 748b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang 758b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang @Override 76a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 77fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo protected void onAddressAllocated(int logicalAddress, int reason) { 78a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( 803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang mAddress, mService.getPhysicalAddress(), mDeviceType)); 81ad515e88ee53ef0849b2e749efd87f7587514769Jinsuk Kim mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( 82ad515e88ee53ef0849b2e749efd87f7587514769Jinsuk Kim mAddress, mService.getVendorId())); 836f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim startQueuedActions(); 842918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim } 8579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 86af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @Override 87af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @ServiceThreadOnly 88af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim protected int getPreferredAddress() { 89af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim assertRunOnServiceThread(); 90af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 91af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim Constants.ADDR_UNREGISTERED); 92af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim } 93af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim 94af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @Override 95af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @ServiceThreadOnly 96af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim protected void setPreferredAddress(int addr) { 97af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim assertRunOnServiceThread(); 98af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 99af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim String.valueOf(addr)); 100af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim } 101af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim 102a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 10379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang void oneTouchPlay(IHdmiControlCallback callback) { 10479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang assertRunOnServiceThread(); 105cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class); 106cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim if (!actions.isEmpty()) { 107cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim Slog.i(TAG, "oneTouchPlay already in progress"); 108cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim actions.get(0).addCallback(callback); 10979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 11079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 111c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV, 112c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim callback); 11379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang if (action == null) { 11479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.w(TAG, "Cannot initiate oneTouchPlay"); 115c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 11679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 11779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 11879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang addAndStartAction(action); 11979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 12079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 121a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 12279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang void queryDisplayStatus(IHdmiControlCallback callback) { 12379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang assertRunOnServiceThread(); 124cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class); 125cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim if (!actions.isEmpty()) { 126cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim Slog.i(TAG, "queryDisplayStatus already in progress"); 127cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim actions.get(0).addCallback(callback); 12879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 12979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 130cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim DevicePowerStatusAction action = DevicePowerStatusAction.create(this, Constants.ADDR_TV, 131cb802870f7f855c429556643fd67179a7b5dc747Jinsuk Kim callback); 13279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang if (action == null) { 13379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.w(TAG, "Cannot initiate queryDisplayStatus"); 134c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 13579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 13679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 13779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang addAndStartAction(action); 13879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 13979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 140a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 14179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang private void invokeCallback(IHdmiControlCallback callback, int result) { 142a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 14379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang try { 14479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang callback.onComplete(result); 14579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } catch (RemoteException e) { 14679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.e(TAG, "Invoking callback failed:" + e); 14779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 14879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 14979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 15079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang @Override 151a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 15279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang void onHotplug(int portId, boolean connected) { 153a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 15479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang mCecMessageCache.flushAll(); 15589ec14e48f4a1bdf291cda9fba7b8172f55a2447Yuncheol Heo // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3. 156659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim if (WAKE_ON_HOTPLUG && connected && mService.isPowerStandbyOrTransient()) { 15738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo mService.wakeUp(); 15838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 159e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim if (!connected) { 160e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim getWakeLock().release(); 161e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 16238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 16338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 164e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @Override 165e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @ServiceThreadOnly 166e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim protected void onStandby(boolean initiatedByCec, int standbyAction) { 167e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim assertRunOnServiceThread(); 1689ccff51b54ce282d19cb13dd326dc03950be07feDonghyun Cho if (!mService.isControlEnabled() || initiatedByCec || !mAutoTvOff) { 169e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim return; 170e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 171e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim switch (standbyAction) { 172e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim case HdmiControlService.STANDBY_SCREEN_OFF: 1739ccff51b54ce282d19cb13dd326dc03950be07feDonghyun Cho mService.sendCecCommand( 1749ccff51b54ce282d19cb13dd326dc03950be07feDonghyun Cho HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); 175e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim break; 176e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim case HdmiControlService.STANDBY_SHUTDOWN: 177e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // ACTION_SHUTDOWN is taken as a signal to power off all the devices. 178e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mService.sendCecCommand( 179e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST)); 180e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim break; 181e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 182e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 183e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 184e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @Override 185e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @ServiceThreadOnly 186e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim void setAutoDeviceOff(boolean enabled) { 187e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim assertRunOnServiceThread(); 188e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mAutoTvOff = enabled; 189e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 190e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 19138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 192e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim void setActiveSource(boolean on) { 19338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 194e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim mIsActiveSource = on; 195e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim if (on) { 196e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim getWakeLock().acquire(); 197e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } else { 198e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim getWakeLock().release(); 199e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 200e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 201e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim 202e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim @ServiceThreadOnly 203bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private ActiveWakeLock getWakeLock() { 204e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim assertRunOnServiceThread(); 205e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim if (mWakeLock == null) { 206bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) { 207bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock = new SystemWakeLock(); 208bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } else { 209bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // Create a dummy lock object that doesn't do anything about wake lock, 210bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // hence allows the device to go to sleep even if it's the active source. 211bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock = new ActiveWakeLock() { 212bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 213bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void acquire() { } 214bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 215bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void release() { } 216bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 217bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public boolean isHeld() { return false; } 218bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim }; 219bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim HdmiLogger.debug("No wakelock is used to keep the display on."); 220bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 221e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 222e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim return mWakeLock; 223e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 224e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim 225e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim @Override 226e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim protected boolean canGoToStandby() { 227e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim return !getWakeLock().isHeld(); 22838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 22938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 23038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @Override 23138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 23238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo protected boolean handleActiveSource(HdmiCecMessage message) { 23338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 23438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 23564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mayResetActiveSource(physicalAddress); 23664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 23764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 23864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 23964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo private void mayResetActiveSource(int physicalAddress) { 24038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo if (physicalAddress != mService.getPhysicalAddress()) { 241e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim setActiveSource(false); 24238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 24338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 24438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 2459b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim @ServiceThreadOnly 2469b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim protected boolean handleUserControlPressed(HdmiCecMessage message) { 2479b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim assertRunOnServiceThread(); 2489b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim wakeUpIfActiveSource(); 2499b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim return super.handleUserControlPressed(message); 2509b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim } 2519b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim 25238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @Override 25338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 25438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo protected boolean handleSetStreamPath(HdmiCecMessage message) { 25538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 25638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 25764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo maySetActiveSource(physicalAddress); 2588ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo maySendActiveSource(message.getSource()); 25964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo wakeUpIfActiveSource(); 26064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 26164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 26264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 263e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // Samsung model we tested sends <Routing Change> and <Request Active Source> 264e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // in a row, and then changes the input to the internal source if there is no 265e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // <Active Source> in response. To handle this, we'll set ActiveSource aggressively. 26664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @Override 26764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @ServiceThreadOnly 26864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo protected boolean handleRoutingChange(HdmiCecMessage message) { 26964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo assertRunOnServiceThread(); 27064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); 27164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo maySetActiveSource(newPath); 27264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 27364bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 27464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 27564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @Override 27664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @ServiceThreadOnly 27764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo protected boolean handleRoutingInformation(HdmiCecMessage message) { 27864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo assertRunOnServiceThread(); 27964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 28064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo maySetActiveSource(physicalAddress); 28164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 28264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 28364bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 28464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo private void maySetActiveSource(int physicalAddress) { 285e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim setActiveSource(physicalAddress == mService.getPhysicalAddress()); 28664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 28764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 28864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo private void wakeUpIfActiveSource() { 2899b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim if (!mIsActiveSource) { 2909b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim return; 2919b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim } 2929b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim // Wake up the device if the power is in standby mode, or its screen is off - 2939b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim // which can happen if the device is holding a partial lock. 2949b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim if (mService.isPowerStandbyOrTransient() || !mService.getPowerManager().isScreenOn()) { 29564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mService.wakeUp(); 29638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 29764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 29864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 2998ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo private void maySendActiveSource(int dest) { 30064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo if (mIsActiveSource) { 30164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( 30264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mAddress, mService.getPhysicalAddress())); 3038ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo // Always reports menu-status active to receive RCP. 3048ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( 3058ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo mAddress, dest, Constants.MENU_STATE_ACTIVATED)); 30664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 30764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 30864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 30964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @Override 31064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @ServiceThreadOnly 31164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo protected boolean handleRequestActiveSource(HdmiCecMessage message) { 31264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo assertRunOnServiceThread(); 3138ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo maySendActiveSource(message.getSource()); 31464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 31538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 31638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 317795415b57b7271a11d16ca42703251a325df1097Terry Heo @ServiceThreadOnly 318795415b57b7271a11d16ca42703251a325df1097Terry Heo protected boolean handleSetMenuLanguage(HdmiCecMessage message) { 319795415b57b7271a11d16ca42703251a325df1097Terry Heo assertRunOnServiceThread(); 320a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho if (!SET_MENU_LANGUAGE) { 321a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho return false; 322a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho } 323795415b57b7271a11d16ca42703251a325df1097Terry Heo 324795415b57b7271a11d16ca42703251a325df1097Terry Heo try { 325795415b57b7271a11d16ca42703251a325df1097Terry Heo String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII"); 3268ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim Locale currentLocale = mService.getContext().getResources().getConfiguration().locale; 3278ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim if (currentLocale.getISO3Language().equals(iso3Language)) { 3288ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim // Do not switch language if the new language is the same as the current one. 3298ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim // This helps avoid accidental country variant switching from en_US to en_AU 3308ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim // due to the limitation of CEC. See the warning below. 3318ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim return true; 3328ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim } 333795415b57b7271a11d16ca42703251a325df1097Terry Heo 334795415b57b7271a11d16ca42703251a325df1097Terry Heo // Don't use Locale.getAvailableLocales() since it returns a locale 335795415b57b7271a11d16ca42703251a325df1097Terry Heo // which is not available on Settings. 336795415b57b7271a11d16ca42703251a325df1097Terry Heo final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales( 337795415b57b7271a11d16ca42703251a325df1097Terry Heo mService.getContext(), false); 338795415b57b7271a11d16ca42703251a325df1097Terry Heo for (LocaleInfo localeInfo : localeInfos) { 339795415b57b7271a11d16ca42703251a325df1097Terry Heo if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) { 340795415b57b7271a11d16ca42703251a325df1097Terry Heo // WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires 341795415b57b7271a11d16ca42703251a325df1097Terry Heo // additional country variant to pinpoint the locale. This keeps the right 342795415b57b7271a11d16ca42703251a325df1097Terry Heo // locale from being chosen. 'eng' in the CEC command, for instance, 343795415b57b7271a11d16ca42703251a325df1097Terry Heo // will always be mapped to en-AU among other variants like en-US, en-GB, 344795415b57b7271a11d16ca42703251a325df1097Terry Heo // an en-IN, which may not be the expected one. 345795415b57b7271a11d16ca42703251a325df1097Terry Heo LocalePicker.updateLocale(localeInfo.getLocale()); 346795415b57b7271a11d16ca42703251a325df1097Terry Heo return true; 347795415b57b7271a11d16ca42703251a325df1097Terry Heo } 348795415b57b7271a11d16ca42703251a325df1097Terry Heo } 349795415b57b7271a11d16ca42703251a325df1097Terry Heo Slog.w(TAG, "Can't handle <Set Menu Language> of " + iso3Language); 350795415b57b7271a11d16ca42703251a325df1097Terry Heo return false; 351795415b57b7271a11d16ca42703251a325df1097Terry Heo } catch (UnsupportedEncodingException e) { 352a09256c72f67010887f2a3801d4e1d27fa0ec2f7Donghyun Cho Slog.w(TAG, "Can't handle <Set Menu Language>", e); 353795415b57b7271a11d16ca42703251a325df1097Terry Heo return false; 354795415b57b7271a11d16ca42703251a325df1097Terry Heo } 355795415b57b7271a11d16ca42703251a325df1097Terry Heo } 356795415b57b7271a11d16ca42703251a325df1097Terry Heo 35738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @Override 3587609bc324d89ebf99ac04f21e992fdd7315d2be0Donghyun Cho protected int findKeyReceiverAddress() { 3597609bc324d89ebf99ac04f21e992fdd7315d2be0Donghyun Cho return Constants.ADDR_TV; 3607609bc324d89ebf99ac04f21e992fdd7315d2be0Donghyun Cho } 3617609bc324d89ebf99ac04f21e992fdd7315d2be0Donghyun Cho 3627609bc324d89ebf99ac04f21e992fdd7315d2be0Donghyun Cho @Override 36338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 3648d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim protected void sendStandby(int deviceId) { 3658d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim assertRunOnServiceThread(); 3668d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim 3678d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim // Playback device can send <Standby> to TV only. Ignore the parameter. 3688d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim int targetAddress = Constants.ADDR_TV; 3698d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); 3708d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim } 3718d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim 3728d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim @Override 3738d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim @ServiceThreadOnly 3744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { 3754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang super.disableDevice(initiatedByCec, callback); 3764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang 37738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 37838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo if (!initiatedByCec && mIsActiveSource) { 37938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( 38038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo mAddress, mService.getPhysicalAddress())); 38138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 382e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim setActiveSource(false); 38338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo checkIfPendingActionsCleared(); 38479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 385959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo 386959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo @Override 387959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo protected void dump(final IndentingPrintWriter pw) { 388959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo super.dump(pw); 389959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo pw.println("mIsActiveSource: " + mIsActiveSource); 390e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim pw.println("mAutoTvOff:" + mAutoTvOff); 391959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo } 392bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 393bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // Wrapper interface over PowerManager.WakeLock 394bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private interface ActiveWakeLock { 395bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim void acquire(); 396bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim void release(); 397bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim boolean isHeld(); 398bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 399bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 400bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private class SystemWakeLock implements ActiveWakeLock { 401bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private final WakeLock mWakeLock; 402bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public SystemWakeLock() { 403bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 404bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock.setReferenceCounted(false); 405bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 406bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 407bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 408bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void acquire() { 409bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock.acquire(); 410bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource); 411bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 412bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 413bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 414bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void release() { 415bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock.release(); 416bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim HdmiLogger.debug("Wake lock released"); 417bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 418bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 419bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 420bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public boolean isHeld() { 421bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim return mWakeLock.isHeld(); 422bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 423bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 42484659edd41b0bc7ecde645405e926a641e704824Jinsuk Kim} 425