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 382918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim/** 392918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Represent a logical device of type Playback residing in Android system. 402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim */ 412918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kimfinal class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { 4279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang private static final String TAG = "HdmiCecLocalDevicePlayback"; 432918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim 44659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim private static final boolean WAKE_ON_HOTPLUG = 45659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true); 46659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim 4738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo private boolean mIsActiveSource = false; 4838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 49e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // Used to keep the device awake while it is the active source. For devices that 50e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // cannot wake up via CEC commands, this address the inconvenience of having to 51bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // turn them on. True by default, and can be disabled (i.e. device can go to sleep 52bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // in active device status) by explicitly setting the system property 53bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // persist.sys.hdmi.keep_awake to false. 54e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // Lazily initialized - should call getWakeLock() to get the instance. 55bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private ActiveWakeLock mWakeLock; 56e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim 57e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // If true, turn off TV upon standby. False by default. 58e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim private boolean mAutoTvOff; 59e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang HdmiCecLocalDevicePlayback(HdmiControlService service) { 6161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang super(service, HdmiDeviceInfo.DEVICE_PLAYBACK); 62e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 63e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mAutoTvOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, false); 64e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 65e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // The option is false by default. Update settings db as well to have the right 66e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // initial setting on UI. 67e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, mAutoTvOff); 688b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang } 698b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang 708b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang @Override 71a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 72fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo protected void onAddressAllocated(int logicalAddress, int reason) { 73a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( 753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang mAddress, mService.getPhysicalAddress(), mDeviceType)); 76ad515e88ee53ef0849b2e749efd87f7587514769Jinsuk Kim mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( 77ad515e88ee53ef0849b2e749efd87f7587514769Jinsuk Kim mAddress, mService.getVendorId())); 786f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim startQueuedActions(); 792918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim } 8079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 81af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @Override 82af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @ServiceThreadOnly 83af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim protected int getPreferredAddress() { 84af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim assertRunOnServiceThread(); 85af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 86af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim Constants.ADDR_UNREGISTERED); 87af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim } 88af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim 89af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @Override 90af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim @ServiceThreadOnly 91af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim protected void setPreferredAddress(int addr) { 92af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim assertRunOnServiceThread(); 93af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 94af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim String.valueOf(addr)); 95af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim } 96af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim 97a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 9879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang void oneTouchPlay(IHdmiControlCallback callback) { 9979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang assertRunOnServiceThread(); 10079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang if (hasAction(OneTouchPlayAction.class)) { 10179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.w(TAG, "oneTouchPlay already in progress"); 102c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS); 10379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 10479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 10579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 10679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang // TODO: Consider the case of multiple TV sets. For now we always direct the command 10779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang // to the primary one. 108c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV, 109c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim callback); 11079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang if (action == null) { 11179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.w(TAG, "Cannot initiate oneTouchPlay"); 112c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 11379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 11479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 11579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang addAndStartAction(action); 11679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 11779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 118a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 11979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang void queryDisplayStatus(IHdmiControlCallback callback) { 12079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang assertRunOnServiceThread(); 12179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang if (hasAction(DevicePowerStatusAction.class)) { 12279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.w(TAG, "queryDisplayStatus already in progress"); 123c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS); 12479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 12579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 12679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang DevicePowerStatusAction action = DevicePowerStatusAction.create(this, 127c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim Constants.ADDR_TV, callback); 12879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang if (action == null) { 12979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.w(TAG, "Cannot initiate queryDisplayStatus"); 130c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 13179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return; 13279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 13379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang addAndStartAction(action); 13479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 13579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 136a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 13779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang private void invokeCallback(IHdmiControlCallback callback, int result) { 138a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 13979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang try { 14079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang callback.onComplete(result); 14179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } catch (RemoteException e) { 14279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang Slog.e(TAG, "Invoking callback failed:" + e); 14379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 14479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 14579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang 14679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang @Override 147a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 14879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang void onHotplug(int portId, boolean connected) { 149a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 15079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang mCecMessageCache.flushAll(); 15189ec14e48f4a1bdf291cda9fba7b8172f55a2447Yuncheol Heo // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3. 152659c486beb0ccdf53a9176cb5c2c3e7c9acf8f50Jinsuk Kim if (WAKE_ON_HOTPLUG && connected && mService.isPowerStandbyOrTransient()) { 15338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo mService.wakeUp(); 15438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 155e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim if (!connected) { 156e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim getWakeLock().release(); 157e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 15838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 15938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 160e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @Override 161e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @ServiceThreadOnly 162e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim protected void onStandby(boolean initiatedByCec, int standbyAction) { 163e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim assertRunOnServiceThread(); 164e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim if (!mService.isControlEnabled() || initiatedByCec) { 165e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim return; 166e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 167e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim switch (standbyAction) { 168e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim case HdmiControlService.STANDBY_SCREEN_OFF: 169e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim if (mAutoTvOff) { 170e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mService.sendCecCommand( 171e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); 172e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 173e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim break; 174e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim case HdmiControlService.STANDBY_SHUTDOWN: 175e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim // ACTION_SHUTDOWN is taken as a signal to power off all the devices. 176e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mService.sendCecCommand( 177e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST)); 178e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim break; 179e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 180e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 181e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 182e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @Override 183e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim @ServiceThreadOnly 184e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim void setAutoDeviceOff(boolean enabled) { 185e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim assertRunOnServiceThread(); 186e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim mAutoTvOff = enabled; 187e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim } 188e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim 18938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 190e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim void setActiveSource(boolean on) { 19138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 192e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim mIsActiveSource = on; 193e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim if (on) { 194e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim getWakeLock().acquire(); 195e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } else { 196e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim getWakeLock().release(); 197e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 198e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 199e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim 200e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim @ServiceThreadOnly 201bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private ActiveWakeLock getWakeLock() { 202e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim assertRunOnServiceThread(); 203e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim if (mWakeLock == null) { 204bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) { 205bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock = new SystemWakeLock(); 206bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } else { 207bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // Create a dummy lock object that doesn't do anything about wake lock, 208bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // hence allows the device to go to sleep even if it's the active source. 209bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock = new ActiveWakeLock() { 210bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 211bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void acquire() { } 212bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 213bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void release() { } 214bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 215bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public boolean isHeld() { return false; } 216bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim }; 217bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim HdmiLogger.debug("No wakelock is used to keep the display on."); 218bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 219e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 220e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim return mWakeLock; 221e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim } 222e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim 223e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim @Override 224e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim protected boolean canGoToStandby() { 225e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim return !getWakeLock().isHeld(); 22638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 22738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 22838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @Override 22938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 23038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo protected boolean handleActiveSource(HdmiCecMessage message) { 23138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 23238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 23364bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mayResetActiveSource(physicalAddress); 23464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 23564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 23664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 23764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo private void mayResetActiveSource(int physicalAddress) { 23838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo if (physicalAddress != mService.getPhysicalAddress()) { 239e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim setActiveSource(false); 24038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 24138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 24238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 2439b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim @ServiceThreadOnly 2449b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim protected boolean handleUserControlPressed(HdmiCecMessage message) { 2459b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim assertRunOnServiceThread(); 2469b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim wakeUpIfActiveSource(); 2479b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim return super.handleUserControlPressed(message); 2489b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim } 2499b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim 25038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @Override 25138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 25238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo protected boolean handleSetStreamPath(HdmiCecMessage message) { 25338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 25438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 25564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo maySetActiveSource(physicalAddress); 2568ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo maySendActiveSource(message.getSource()); 25764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo wakeUpIfActiveSource(); 25864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 25964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 26064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 261e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // Samsung model we tested sends <Routing Change> and <Request Active Source> 262e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // in a row, and then changes the input to the internal source if there is no 263e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim // <Active Source> in response. To handle this, we'll set ActiveSource aggressively. 26464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @Override 26564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @ServiceThreadOnly 26664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo protected boolean handleRoutingChange(HdmiCecMessage message) { 26764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo assertRunOnServiceThread(); 26864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); 26964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo maySetActiveSource(newPath); 27064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 27164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 27264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 27364bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @Override 27464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @ServiceThreadOnly 27564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo protected boolean handleRoutingInformation(HdmiCecMessage message) { 27664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo assertRunOnServiceThread(); 27764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 27864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo maySetActiveSource(physicalAddress); 27964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 28064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 28164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 28264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo private void maySetActiveSource(int physicalAddress) { 283e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim setActiveSource(physicalAddress == mService.getPhysicalAddress()); 28464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 28564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 28664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo private void wakeUpIfActiveSource() { 2879b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim if (!mIsActiveSource) { 2889b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim return; 2899b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim } 2909b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim // Wake up the device if the power is in standby mode, or its screen is off - 2919b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim // which can happen if the device is holding a partial lock. 2929b8507c52ae845c8eed9fd9952bf66538934b8fdJinsuk Kim if (mService.isPowerStandbyOrTransient() || !mService.getPowerManager().isScreenOn()) { 29364bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mService.wakeUp(); 29438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 29564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 29664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 2978ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo private void maySendActiveSource(int dest) { 29864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo if (mIsActiveSource) { 29964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( 30064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo mAddress, mService.getPhysicalAddress())); 3018ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo // Always reports menu-status active to receive RCP. 3028ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( 3038ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo mAddress, dest, Constants.MENU_STATE_ACTIVATED)); 30464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 30564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo } 30664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo 30764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @Override 30864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo @ServiceThreadOnly 30964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo protected boolean handleRequestActiveSource(HdmiCecMessage message) { 31064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo assertRunOnServiceThread(); 3118ecb7219dff70d31376b236e6f5a954a61a3486bYuncheol Heo maySendActiveSource(message.getSource()); 31264bafd9108b532102bc13c47966444b073c33fafYuncheol Heo return true; // Broadcast message. 31338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 31438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo 315795415b57b7271a11d16ca42703251a325df1097Terry Heo @ServiceThreadOnly 316795415b57b7271a11d16ca42703251a325df1097Terry Heo protected boolean handleSetMenuLanguage(HdmiCecMessage message) { 317795415b57b7271a11d16ca42703251a325df1097Terry Heo assertRunOnServiceThread(); 318795415b57b7271a11d16ca42703251a325df1097Terry Heo 319795415b57b7271a11d16ca42703251a325df1097Terry Heo try { 320795415b57b7271a11d16ca42703251a325df1097Terry Heo String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII"); 3218ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim Locale currentLocale = mService.getContext().getResources().getConfiguration().locale; 3228ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim if (currentLocale.getISO3Language().equals(iso3Language)) { 3238ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim // Do not switch language if the new language is the same as the current one. 3248ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim // This helps avoid accidental country variant switching from en_US to en_AU 3258ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim // due to the limitation of CEC. See the warning below. 3268ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim return true; 3278ea452e3f6e85ed847ef6461b3969c889017b277Jinsuk Kim } 328795415b57b7271a11d16ca42703251a325df1097Terry Heo 329795415b57b7271a11d16ca42703251a325df1097Terry Heo // Don't use Locale.getAvailableLocales() since it returns a locale 330795415b57b7271a11d16ca42703251a325df1097Terry Heo // which is not available on Settings. 331795415b57b7271a11d16ca42703251a325df1097Terry Heo final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales( 332795415b57b7271a11d16ca42703251a325df1097Terry Heo mService.getContext(), false); 333795415b57b7271a11d16ca42703251a325df1097Terry Heo for (LocaleInfo localeInfo : localeInfos) { 334795415b57b7271a11d16ca42703251a325df1097Terry Heo if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) { 335795415b57b7271a11d16ca42703251a325df1097Terry Heo // WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires 336795415b57b7271a11d16ca42703251a325df1097Terry Heo // additional country variant to pinpoint the locale. This keeps the right 337795415b57b7271a11d16ca42703251a325df1097Terry Heo // locale from being chosen. 'eng' in the CEC command, for instance, 338795415b57b7271a11d16ca42703251a325df1097Terry Heo // will always be mapped to en-AU among other variants like en-US, en-GB, 339795415b57b7271a11d16ca42703251a325df1097Terry Heo // an en-IN, which may not be the expected one. 340795415b57b7271a11d16ca42703251a325df1097Terry Heo LocalePicker.updateLocale(localeInfo.getLocale()); 341795415b57b7271a11d16ca42703251a325df1097Terry Heo return true; 342795415b57b7271a11d16ca42703251a325df1097Terry Heo } 343795415b57b7271a11d16ca42703251a325df1097Terry Heo } 344795415b57b7271a11d16ca42703251a325df1097Terry Heo Slog.w(TAG, "Can't handle <Set Menu Language> of " + iso3Language); 345795415b57b7271a11d16ca42703251a325df1097Terry Heo return false; 346795415b57b7271a11d16ca42703251a325df1097Terry Heo } catch (UnsupportedEncodingException e) { 347795415b57b7271a11d16ca42703251a325df1097Terry Heo return false; 348795415b57b7271a11d16ca42703251a325df1097Terry Heo } 349795415b57b7271a11d16ca42703251a325df1097Terry Heo } 350795415b57b7271a11d16ca42703251a325df1097Terry Heo 35138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @Override 35238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo @ServiceThreadOnly 3538d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim protected void sendStandby(int deviceId) { 3548d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim assertRunOnServiceThread(); 3558d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim 3568d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim // Playback device can send <Standby> to TV only. Ignore the parameter. 3578d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim int targetAddress = Constants.ADDR_TV; 3588d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); 3598d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim } 3608d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim 3618d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim @Override 3628d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim @ServiceThreadOnly 3634fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { 3644fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang super.disableDevice(initiatedByCec, callback); 3654fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang 36638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo assertRunOnServiceThread(); 36738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo if (!initiatedByCec && mIsActiveSource) { 36838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( 36938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo mAddress, mService.getPhysicalAddress())); 37038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo } 371e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim setActiveSource(false); 37238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo checkIfPendingActionsCleared(); 37379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 374959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo 375959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo @Override 376959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo protected void dump(final IndentingPrintWriter pw) { 377959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo super.dump(pw); 378959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo pw.println("mIsActiveSource: " + mIsActiveSource); 379e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim pw.println("mAutoTvOff:" + mAutoTvOff); 380959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo } 381bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 382bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim // Wrapper interface over PowerManager.WakeLock 383bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private interface ActiveWakeLock { 384bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim void acquire(); 385bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim void release(); 386bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim boolean isHeld(); 387bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 388bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 389bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private class SystemWakeLock implements ActiveWakeLock { 390bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim private final WakeLock mWakeLock; 391bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public SystemWakeLock() { 392bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 393bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock.setReferenceCounted(false); 394bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 395bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 396bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 397bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void acquire() { 398bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock.acquire(); 399bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource); 400bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 401bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 402bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 403bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public void release() { 404bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim mWakeLock.release(); 405bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim HdmiLogger.debug("Wake lock released"); 406bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 407bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim 408bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim @Override 409bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim public boolean isHeld() { 410bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim return mWakeLock.isHeld(); 411bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 412bad839386afc76ea037da022960b63683e95a7b0Jinsuk Kim } 41384659edd41b0bc7ecde645405e926a641e704824Jinsuk Kim} 414