summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJinsuk Kim <jinsukkim@google.com>2014-04-02 22:39:41 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-04-02 22:39:41 +0000
commitb1dca1624dc8d853a52d1918c7672173e9b79836 (patch)
tree874b4057bd023272fbfb56cccc64419fff280cd4
parent4932a355f1594990173e0d9cd1fa38b8491c4a29 (diff)
parent4e90fcd1907e0c1f1fa1dc991fbd7fda5e49a482 (diff)
downloadframeworks_base-b1dca1624dc8d853a52d1918c7672173e9b79836.zip
frameworks_base-b1dca1624dc8d853a52d1918c7672173e9b79836.tar.gz
frameworks_base-b1dca1624dc8d853a52d1918c7672173e9b79836.tar.bz2
Merge "Maintain display power status for playback device" into klp-modular-dev
-rw-r--r--core/java/android/hardware/hdmi/HdmiCec.java6
-rw-r--r--core/java/android/hardware/hdmi/HdmiCecClient.java16
-rw-r--r--core/java/android/hardware/hdmi/HdmiCecManager.java3
-rw-r--r--core/java/android/hardware/hdmi/IHdmiCecService.aidl2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecDevice.java49
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java98
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java35
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecService.java28
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 &lt;GiveDevicePowerStatus&gt; 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();
}
}