diff options
author | Jinsuk Kim <jinsukkim@google.com> | 2014-04-03 01:41:13 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-04-03 01:41:13 +0000 |
commit | c9cb91388b268608c8b7f588dca8417f2661f0ed (patch) | |
tree | 349c248467f8c1e7be24d9b41197bb66ea46a845 | |
parent | a7d9b93756c8b1e61a721fe14d941ee7ab975513 (diff) | |
parent | 4c963841db60ed59fd59a6fba79f8e3e6ec7f349 (diff) | |
download | frameworks_base-c9cb91388b268608c8b7f588dca8417f2661f0ed.zip frameworks_base-c9cb91388b268608c8b7f588dca8417f2661f0ed.tar.gz frameworks_base-c9cb91388b268608c8b7f588dca8417f2661f0ed.tar.bz2 |
am 4c963841: am b1dca162: Merge "Maintain display power status for playback device" into klp-modular-dev
* commit '4c963841db60ed59fd59a6fba79f8e3e6ec7f349':
Maintain display power status for playback device
8 files changed, 217 insertions, 20 deletions
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index 38d9de4..8578a32 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -160,6 +160,12 @@ public final class HdmiCec { public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2; public static final int MESSAGE_ABORT = 0xFF; + public static final int POWER_STATUS_UNKNOWN = -1; + public static final int POWER_STATUS_ON = 0; + public static final int POWER_STATUS_STANDBY = 1; + public static final int POWER_TRANSIENT_TO_ON = 2; + public static final int POWER_TRANSIENT_TO_STANDBY = 3; + private static final int[] ADDRESS_TO_TYPE = { DEVICE_TV, // ADDR_TV DEVICE_RECORDER, // ADDR_RECORDER_1 diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java index d7f4a72..1f382e6 100644 --- a/core/java/android/hardware/hdmi/HdmiCecClient.java +++ b/core/java/android/hardware/hdmi/HdmiCecClient.java @@ -110,16 +110,20 @@ public final class HdmiCecClient { } /** - * Send <GiveDevicePowerStatus> message. + * Returns true if the TV or attached display is powered on. + * <p> + * The result of this method is only meaningful on playback devices (where the device + * type is {@link HdmiCec#DEVICE_PLAYBACK}). + * </p> * - * @param address logical address of the device to send the message to, such as - * {@link HdmiCec#ADDR_TV}. + * @return true if TV is on; otherwise false. */ - public void sendGiveDevicePowerStatus(int address) { + public boolean isTvOn() { try { - mService.sendGiveDevicePowerStatus(mBinder, address); + return mService.isTvOn(mBinder); } catch (RemoteException e) { - Log.e(TAG, "sendGiveDevicePowerStatus threw exception ", e); + Log.e(TAG, "isTvOn threw exception ", e); } + return false; } } diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java index 575785d..10b058c 100644 --- a/core/java/android/hardware/hdmi/HdmiCecManager.java +++ b/core/java/android/hardware/hdmi/HdmiCecManager.java @@ -45,6 +45,9 @@ public final class HdmiCecManager { * @return {@link HdmiCecClient} instance. {@code null} on failure. */ public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) { + if (mService == null) { + return null; + } try { IBinder b = mService.allocateLogicalDevice(type, getListenerWrapper(listener)); return HdmiCecClient.create(mService, b); diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl index 6fefcf8..727f469 100644 --- a/core/java/android/hardware/hdmi/IHdmiCecService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl @@ -34,7 +34,7 @@ interface IHdmiCecService { void sendInactiveSource(IBinder b); void sendImageViewOn(IBinder b); void sendTextViewOn(IBinder b); - void sendGiveDevicePowerStatus(IBinder b, int address); + boolean isTvOn(IBinder b); void sendMessage(IBinder b, in HdmiCecMessage message); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java index 9916435..ebb48dd 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java @@ -27,8 +27,12 @@ import java.util.ArrayList; import java.util.List; /** - * CecDevice class represents a CEC logical device characterized - * by its device type. A physical device can contain the functions of + * HdmiCecDevice class represents a CEC logical device characterized + * by its device type. It is a superclass of those serving concrete device type. + * Currently we're interested in playback(one of sources), display(sink) device type + * only. The support for the other types like recorder, audio system will come later. + * + * <p>A physical device can contain the functions of * more than one logical device, in which case it should create * as many logical devices as necessary. * @@ -41,7 +45,7 @@ import java.util.List; * * <p>Declared as package-private, accessed by HdmiCecService only. */ -final class HdmiCecDevice { +abstract class HdmiCecDevice { private static final String TAG = "HdmiCecDevice"; private final int mType; @@ -49,19 +53,39 @@ final class HdmiCecDevice { // List of listeners to the message/event coming to the device. private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>(); private final Binder mBinder = new Binder(); + private final HdmiCecService mService; private String mName; private boolean mIsActiveSource; /** + * Factory method that creates HdmiCecDevice instance to the device type. + */ + public static HdmiCecDevice create(HdmiCecService service, int type) { + if (type == HdmiCec.DEVICE_PLAYBACK) { + return new HdmiCecDevicePlayback(service, type); + } else if (type == HdmiCec.DEVICE_TV) { + return new HdmiCecDeviceTv(service, type); + } + return null; + } + + /** * Constructor. */ - public HdmiCecDevice(int type) { + public HdmiCecDevice(HdmiCecService service, int type) { + mService = service; mType = type; mIsActiveSource = false; } /** + * Called right after the class is instantiated. This method can be used to + * implement any initialization tasks for the instance. + */ + abstract public void initialize(); + + /** * Return the binder token that identifies this instance. */ public Binder getToken() { @@ -69,6 +93,13 @@ final class HdmiCecDevice { } /** + * Return the service instance. + */ + public HdmiCecService getService() { + return mService; + } + + /** * Return the type of this device. */ public int getType() { @@ -128,6 +159,7 @@ final class HdmiCecDevice { if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) { mIsActiveSource = false; } + if (mListeners.size() == 0) { return; } @@ -167,4 +199,13 @@ final class HdmiCecDevice { public void setIsActiveSource(boolean state) { mIsActiveSource = state; } + + /** + * Check if the connected sink device is in powered-on state. The default implementation + * simply returns false. Should be overriden by subclass to report the correct state. + */ + public boolean isSinkDeviceOn() { + Log.w(TAG, "Not valid for the device type: " + mType); + return false; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java new file mode 100644 index 0000000..0310264 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; + +/** + * Class for the logical device of playback type. Devices such as DVD/Blueray player + * that support 'playback' feature are classified as playback device. It is common + * that they don't have built-in display, therefore need to talk, stream their contents + * to TV/display device which is connected through HDMI cable. + * + * <p>It closely monitors the status of display device (other devices can be of interest + * too, but with much less priority), declares itself as 'active source' to have + * display show its output, switch the source state as ordered by display that may be + * talking to many other devices connected to it. It also receives commands from display + * such as remote control signal, standby, status report, playback mode. + * + * <p>Declared as package-private, accessed by HdmiCecService only. + */ +final class HdmiCecDevicePlayback extends HdmiCecDevice { + private static final String TAG = "HdmiCecDevicePlayback"; + + private int mSinkDevicePowerStatus; + + /** + * Constructor. + */ + public HdmiCecDevicePlayback(HdmiCecService service, int type) { + super(service, type); + mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN; + } + + @Override + public void initialize() { + // Playback device tries to obtain the power status of TV/display when created, + // and maintains it all through its lifecycle. CEC spec says there is + // a maximum 1 second response time. Therefore it should be kept in mind + // that there can be as much amount of period of time the power status + // of the display remains unknown after the query is sent out. + queryTvPowerStatus(); + } + + private void queryTvPowerStatus() { + getService().sendMessage(getType(), HdmiCec.ADDR_TV, + HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, HdmiCecService.EMPTY_PARAM); + } + + @Override + public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) { + // Updates power status of display. The cases are: + // 1) Response for the queried power status request arrives. Update the status. + // 2) Broadcast or direct <Standby> command from TV, which is sent as TV itself is going + // into standby mode too. + // 3) Broadcast <Report Physical Address> command from TV, which is sent while it boots up. + if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) { + mSinkDevicePowerStatus = params[0]; + } else if (srcAddress == HdmiCec.ADDR_TV) { + if (opcode == HdmiCec.MESSAGE_STANDBY) { + mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_STANDBY; + } else if (opcode == HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS) { + mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_ON; + } + } + super.handleMessage(srcAddress, dstAddress, opcode, params); + } + + @Override + public void handleHotplug(boolean connected) { + // If cable get disconnected sink device becomes unreachable. Switch the status + // to unknown, and query the status once the cable gets connected back. + if (!connected) { + mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN; + } else { + queryTvPowerStatus(); + } + super.handleHotplug(connected); + } + + @Override + public boolean isSinkDeviceOn() { + return mSinkDevicePowerStatus == HdmiCec.POWER_STATUS_ON; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java new file mode 100644 index 0000000..09ff3ca --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +/** + * Class for logical device of TV type. + */ +final class HdmiCecDeviceTv extends HdmiCecDevice { + private static final String TAG = "HdmiCecDeviceTv"; + + /** + * Constructor. + */ + public HdmiCecDeviceTv(HdmiCecService service, int type) { + super(service, type); + } + + public void initialize() { + // TODO: Do the initialization task for TV device here. + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java index 0a7236c..1c9fed1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecService.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java @@ -30,6 +30,7 @@ import android.util.Log; import android.util.SparseArray; import com.android.server.SystemService; +import libcore.util.EmptyArray; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -37,8 +38,6 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Locale; -import libcore.util.EmptyArray; - /** * Provides a service for sending and processing HDMI-CEC messages, and providing * the information on HDMI settings in general. @@ -65,7 +64,7 @@ public final class HdmiCecService extends SystemService { private static final String PERMISSION = "android.permission.HDMI_CEC"; - private static final byte[] EMPTY_PARAM = EmptyArray.BYTE; + static final byte[] EMPTY_PARAM = EmptyArray.BYTE; public HdmiCecService(Context context) { super(context); @@ -76,7 +75,9 @@ public final class HdmiCecService extends SystemService { @Override public void onStart() { mNativePtr = nativeInit(this); - publishBinderService(Context.HDMI_CEC_SERVICE, new BinderService()); + if (mNativePtr != 0) { + publishBinderService(Context.HDMI_CEC_SERVICE, new BinderService()); + } } /** @@ -88,7 +89,6 @@ public final class HdmiCecService extends SystemService { // but better be handled in service by turning off the screen // or putting the device into suspend mode. List up such messages // and handle them here. - int type = HdmiCec.getTypeFromAddress(dstAddress); synchronized (mLock) { if (dstAddress == HdmiCec.ADDR_BROADCAST) { for (int i = 0; i < mLogicalDevices.size(); ++i) { @@ -96,6 +96,7 @@ public final class HdmiCecService extends SystemService { params); } } else { + int type = HdmiCec.getTypeFromAddress(dstAddress); HdmiCecDevice device = mLogicalDevices.get(type); if (device == null) { Log.w(TAG, "logical device not found. type: " + type); @@ -205,6 +206,11 @@ public final class HdmiCecService extends SystemService { throw new IllegalArgumentException("Device not found"); } + // package-private. Used by HdmiCecDevice and its subclasses only. + void sendMessage(int type, int address, int opcode, byte[] params) { + nativeSendMessage(mNativePtr, type, address, opcode, params); + } + private final class ListenerRecord implements IBinder.DeathRecipient { private final IHdmiCecListener mListener; private final int mType; @@ -248,8 +254,13 @@ public final class HdmiCecService extends SystemService { Log.e(TAG, "Logical address was not allocated"); return null; } else { - device = new HdmiCecDevice(type); + device = HdmiCecDevice.create(HdmiCecService.this, type); + if (device == null) { + Log.e(TAG, "Device type not supported yet."); + return null; + } device.setName(HdmiCec.getDefaultDeviceName(address)); + device.initialize(); mLogicalDevices.put(type, device); } } @@ -331,12 +342,11 @@ public final class HdmiCecService extends SystemService { } @Override - public void sendGiveDevicePowerStatus(IBinder b, int address) { + public boolean isTvOn(IBinder b) { enforceAccessPermission(); synchronized (mLock) { HdmiCecDevice device = getLogicalDeviceLocked(b); - nativeSendMessage(mNativePtr, device.getType(), address, - HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, EMPTY_PARAM); + return device.isSinkDeviceOn(); } } |