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