diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 56fc1d6dcb93f..521a439c2c57d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -69,6 +69,76 @@ public final class HdmiControlManager {
public static final int RESULT_INCORRECT_MODE = 6;
public static final int RESULT_COMMUNICATION_FAILED = 7;
+ // -- Message ids for display osd.
+
+ /** Place holder for recording status message. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_STATUS_MESSAGE_START = 0x100;
+ /** Recording currently selected source. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x101;
+ /** Recording Digital Service. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_DIGITAL_SERVICE = 0x102;
+ /** Recording Analogue Service. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_ANALOGUE_SERVICE = 0x103;
+ /** Recording External input. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_EXTERNAL_INPUT = 0x104;
+ /** No recording – unable to record Digital Service. No suitable tuner. */
+ public static final int MESSAGE_NO_RECORDNIG_UNABLE_DIGITAL_SERVICE = 0x105;
+ /** No recording – unable to record Analogue Service. No suitable tuner. */
+ public static final int MESSAGE_NO_RECORDNIG_UNABLE_ANALOGUE_SERVICE = 0x106;
+ /**
+ * No recording – unable to select required service. as suitable tuner, but the requested
+ * parameters are invalid or out of range for that tuner.
+ */
+ public static final int MESSAGE_NO_RECORDNIG_UNABLE_SELECTED_SERVICE = 0x107;
+ /** No recording – invalid External plug number */
+ public static final int MESSAGE_NO_RECORDNIG_INVALID_EXTERNAL_PLUG_NUMBER = 0x109;
+ /** No recording – invalid External Physical Address */
+ public static final int MESSAGE_NO_RECORDNIG_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x10A;
+ /** No recording – CA system not supported */
+ public static final int MESSAGE_NO_RECORDNIG_UNSUPPORTED_CA = 0x10B;
+ /** No Recording – No or Insufficient CA Entitlements” */
+ public static final int MESSAGE_NO_RECORDNIG_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x10C;
+ /** No recording – Not allowed to copy source. Source is “copy never”. */
+ public static final int MESSAGE_NO_RECORDNIG_DISALLOW_TO_COPY = 0x10D;
+ /** No recording – No further copies allowed */
+ public static final int MESSAGE_NO_RECORDNIG_DISALLOW_TO_FUTHER_COPIES = 0x10E;
+ /** No recording – No media */
+ public static final int MESSAGE_NO_RECORDNIG_NO_MEDIA = 0x110;
+ /** No recording – playing */
+ public static final int MESSAGE_NO_RECORDNIG_PLAYING = 0x111;
+ /** No recording – already recording */
+ public static final int MESSAGE_NO_RECORDNIG_ALREADY_RECORDING = 0x112;
+ /** No recording – media protected */
+ public static final int MESSAGE_NO_RECORDNIG_MEDIA_PROTECTED = 0x113;
+ /** No recording – no source signal */
+ public static final int MESSAGE_NO_RECORDNIG_NO_SOURCE_SIGNAL = 0x114;
+ /** No recording – media problem */
+ public static final int MESSAGE_NO_RECORDNIG_MEDIA_PROBLEM = 0x115;
+ /** No recording – not enough space available */
+ public static final int MESSAGE_NO_RECORDNIG_NOT_ENOUGH_SPACE = 0x116;
+ /** No recording – Parental Lock On */
+ public static final int MESSAGE_NO_RECORDNIG_PARENT_LOCK_ON = 0x117;
+ /** Recording terminated normally */
+ public static final int MESSAGE_RECORDING_TERMINATED_NORMALLY = 0x11A;
+ /** Recording has already terminated */
+ public static final int MESSAGE_RECORDING_ALREADY_TERMINATED = 0x11B;
+ /** No recording – other reason */
+ public static final int MESSAGE_NO_RECORDNIG_OTHER_REASON = 0x11F;
+ // From here extra message for recording that is not mentioned in CEC spec
+ /** No recording. Previous recording request in progress. */
+ public static final int MESSAGE_NO_RECORDING_PREVIOUS_RECORDING_IN_PROGRESS = 0x130;
+ /** No recording. Please check recorder and connection. */
+ public static final int MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION = 0x131;
+ /** Cannot record currently displayed source. */
+ public static final int MESSAGE_NO_RECORDING_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x132;
+
+ /** Timer recording type for digital service source. */
+ public static final int TIMER_RECORDING_TYPE_DIGITAL = 1;
+ /** Timer recording type for analogue service source. */
+ public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2;
+ /** Timer recording type for external source. */
+ public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3;
+
// True if we have a logical device of type playback hosted in the system.
private final boolean mHasPlaybackDevice;
// True if we have a logical device of type TV hosted in the system.
diff --git a/core/java/android/hardware/hdmi/HdmiRecordSources.java b/core/java/android/hardware/hdmi/HdmiRecordSources.java
index 685eb170fddb6..296cae6253d91 100644
--- a/core/java/android/hardware/hdmi/HdmiRecordSources.java
+++ b/core/java/android/hardware/hdmi/HdmiRecordSources.java
@@ -103,8 +103,10 @@ public final class HdmiRecordSources {
*/
@SystemApi
public static final class OwnSource extends RecordSource {
- protected OwnSource() {
- super(RECORD_SOURCE_TYPE_OWN_SOURCE, 0);
+ private static final int EXTRA_DATA_SIZE = 0;
+
+ private OwnSource() {
+ super(RECORD_SOURCE_TYPE_OWN_SOURCE, EXTRA_DATA_SIZE);
}
@Override
@@ -744,4 +746,27 @@ public final class HdmiRecordSources {
byteArray[index + 1] = (byte) (value & 0xFF);
return 2;
}
+
+ /**
+ * Check the byte array of record source.
+ * @hide
+ */
+ public static boolean checkRecordSource(byte[] recordSource) {
+ int recordSourceType = recordSource[0];
+ int extraDataSize = recordSource.length - 1;
+ switch (recordSourceType) {
+ case RECORD_SOURCE_TYPE_OWN_SOURCE:
+ return extraDataSize == OwnSource.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_DIGITAL_SERVICE:
+ return extraDataSize == DigitalServiceSource.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_ANALOGUE_SERVICE:
+ return extraDataSize == AnalogueServiceSource.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_EXTERNAL_PLUG:
+ return extraDataSize == ExternalPlugData.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_EXTERNAL_PHYSICAL_ADDRESS:
+ return extraDataSize == ExternalPhysicalAddress.EXTRA_DATA_SIZE;
+ default:
+ return false;
+ }
+ }
}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index c02ff8ae653f1..6080914e59363 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -15,6 +15,9 @@
*/
package android.hardware.hdmi;
+import static android.hardware.hdmi.HdmiRecordSources.RecordSource;
+import static android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource;
+
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.util.Log;
@@ -162,7 +165,7 @@ public final class HdmiTvClient extends HdmiClient {
*
* @return {@link HdmiRecordSources} to be used to set recording info
*/
- HdmiRecordSources.RecordSource onRecordRequestReceived(int recorderAddress);
+ RecordSource onRecordRequestReceived(int recorderAddress);
}
/**
@@ -187,7 +190,7 @@ public final class HdmiTvClient extends HdmiClient {
* tvClient.startOneTouchRecord(recorderAddress, ownSource);
*
*/
- public void startOneTouchRecord(int recorderAddress, HdmiRecordSources.RecordSource source) {
+ public void startOneTouchRecord(int recorderAddress, RecordSource source) {
try {
byte[] data = new byte[source.getDataSize(true)];
source.toByteArray(true, data, 0);
@@ -197,6 +200,19 @@ public final class HdmiTvClient extends HdmiClient {
}
}
+ /**
+ * Stop one touch record.
+ *
+ * @param recorderAddress recorder address where recoding will be stopped
+ */
+ public void stopOneTouchRecord(int recorderAddress) {
+ try {
+ mService.stopOneTouchRecord(recorderAddress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to stop record: ", e);
+ }
+ }
+
/**
* Start timer recording with the given recoder address and recorder source.
*
@@ -211,13 +227,47 @@ public final class HdmiTvClient extends HdmiClient {
* TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
* tvClient.startTimerRecording(recorderAddress, source);
*
+ *
+ * @param recorderAddress target recorder address
+ * @param sourceType type of record source. It should be one of
+ * {@link HdmiControlManager#TIMER_RECORDING_TYPE_DIGITAL},
+ * {@link HdmiControlManager#TIMER_RECORDING_TYPE_ANALOGUE},
+ * {@link HdmiControlManager#TIMER_RECORDING_TYPE_EXTERNAL}.
+ * @param source record source to be used
*/
- public void startTimerRecording(int recorderAddress,
- HdmiTimerRecordSources.TimerRecordSource source) {
+ public void startTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) {
+ checkTimerRecordingSourceType(sourceType);
+
try {
byte[] data = new byte[source.getDataSize()];
source.toByteArray(data, 0);
- mService.startTimerRecording(recorderAddress, data);
+ mService.startTimerRecording(recorderAddress, sourceType, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to start record: ", e);
+ }
+ }
+
+ private void checkTimerRecordingSourceType(int sourceType) {
+ switch (sourceType) {
+ case HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL:
+ case HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE:
+ case HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL:
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid source type:" + sourceType);
+ }
+ }
+
+ /**
+ * Clear timer recording with the given recorder address and recording source.
+ * For more details, please refer {@link #startTimerRecording(int, int, TimerRecordSource)}.
+ */
+ public void clearTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) {
+ checkTimerRecordingSourceType(sourceType);
+ try {
+ byte[] data = new byte[source.getDataSize()];
+ source.toByteArray(data, 0);
+ mService.clearTimerRecording(recorderAddress, sourceType, data);
} catch (RemoteException e) {
Log.e(TAG, "failed to start record: ", e);
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 53b8b3f153eb6..95e0ee0a1307f 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -64,5 +64,7 @@ interface IHdmiControlService {
void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener);
void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
- void startTimerRecording(int recorderAddress, in byte[] recordSource);
+ void stopOneTouchRecord(int recorderAddress);
+ void startTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource);
+ void clearTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index f1e7ff2c17650..dbcdfc01e6e47 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -200,6 +200,8 @@ abstract class HdmiCecLocalDevice {
return handleVendorCommandWithId(message);
case Constants.MESSAGE_SET_OSD_NAME:
return handleSetOsdName(message);
+ case Constants.MESSAGE_RECORD_TV_SCREEN:
+ return handleRecordTvScreen(message);
default:
return false;
}
@@ -403,6 +405,13 @@ abstract class HdmiCecLocalDevice {
return true;
}
+ protected boolean handleRecordTvScreen(HdmiCecMessage message) {
+ // The default behavior of is replying with "Refused".
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
+ message.getSource(), message.getOpcode(), Constants.ABORT_REFUSED));
+ return true;
+ }
+
@ServiceThreadOnly
final void handleAddressAllocated(int logicalAddress, boolean fromBootup) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index e985e354f5663..bbecafa8f7797 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
import android.content.Intent;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -35,6 +36,7 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -808,6 +810,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return true;
}
+ // Seq #53
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRecordTvScreen(HdmiCecMessage message) {
+ List actions = getActions(OneTouchRecordAction.class);
+ if (!actions.isEmpty()) {
+ // Assumes only one OneTouchRecordAction.
+ OneTouchRecordAction action = actions.get(0);
+ if (action.getRecorderAddress() != message.getSource()) {
+ displayOsd(HdmiControlManager.MESSAGE_NO_RECORDING_PREVIOUS_RECORDING_IN_PROGRESS);
+ }
+ return super.handleRecordTvScreen(message);
+ }
+
+ int recorderAddress = message.getSource();
+ byte[] recordSource = mService.invokeRecordRequestListener(recorderAddress);
+ startOneTouchRecord(recorderAddress, recordSource);
+ return true;
+ }
+
private boolean isMessageForSystemAudio(HdmiCecMessage message) {
if (message.getSource() != Constants.ADDR_AUDIO_SYSTEM
|| message.getDestination() != Constants.ADDR_TV
@@ -1129,6 +1151,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery().
removeAction(DeviceDiscoveryAction.class);
removeAction(HotplugDetectionAction.class);
+ // Remove one touch record action.
+ removeAction(OneTouchRecordAction.class);
disableSystemAudioIfExist();
disableArcIfExist();
@@ -1210,4 +1234,72 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
HdmiControlService.PERMISSION);
}
+
+ // Seq #54 and #55
+ @ServiceThreadOnly
+ void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+ assertRunOnServiceThread();
+ if (!mService.isControlEnabled()) {
+ Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
+ return;
+ }
+
+ if (!checkRecorder(recorderAddress)) {
+ Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ return;
+ }
+
+ if (!checkRecordSource(recordSource)) {
+ Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
+ return;
+ }
+
+ addAndStartAction(new OneTouchRecordAction(this, recorderAddress, recordSource));
+ Slog.i(TAG, "Start new [One Touch Record]-Target:" + recorderAddress + ", recordSource:"
+ + Arrays.toString(recordSource));
+ }
+
+ @ServiceThreadOnly
+ void stopOneTouchRecord(int recorderAddress) {
+ assertRunOnServiceThread();
+ if (!mService.isControlEnabled()) {
+ Slog.w(TAG, "Can not stop one touch record. CEC control is disabled.");
+ return;
+ }
+
+ if (!checkRecorder(recorderAddress)) {
+ Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ return;
+ }
+
+ // Remove one touch record action so that other one touch record can be started.
+ removeAction(OneTouchRecordAction.class);
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRecordOff(mAddress, recorderAddress));
+ Slog.i(TAG, "Stop [One Touch Record]-Target:" + recorderAddress);
+ }
+
+ private boolean checkRecorder(int recorderAddress) {
+ HdmiCecDeviceInfo device = getDeviceInfo(recorderAddress);
+ return (device != null)
+ && (HdmiUtils.getTypeFromAddress(recorderAddress)
+ == HdmiCecDeviceInfo.DEVICE_RECORDER);
+ }
+
+ private boolean checkRecordSource(byte[] recordSource) {
+ return (recordSource != null) && HdmiRecordSources.checkRecordSource(recordSource);
+ }
+
+ @ServiceThreadOnly
+ void startTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
+ assertRunOnServiceThread();
+
+ // TODO: implement this.
+ }
+
+ @ServiceThreadOnly
+ void clearTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
+ assertRunOnServiceThread();
+
+ // TODO: implement this.
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 0b6c3c5f105bc..79f1964812669 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -455,6 +455,29 @@ public class HdmiCecMessageBuilder {
return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
}
+ /**
+ * Build <Record On> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params parameter of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRecordOn(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_RECORD_ON, params);
+ }
+
+ /**
+ * Build <Record Off> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRecordOff(int src, int dest) {
+ return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF);
+ }
+
/***** Please ADD new buildXXX() methods above. ******/
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 95cd7ef6038e3..76722325a1822 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -55,6 +55,8 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+import libcore.util.EmptyArray;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -157,6 +159,12 @@ public final class HdmiControlService extends SystemService {
@GuardedBy("mLock")
private InputChangeListenerRecord mInputChangeListenerRecord;
+ @GuardedBy("mLock")
+ private IHdmiRecordRequestListener mRecordRequestListener;
+
+ @GuardedBy("mLock")
+ private HdmiRecordRequestListenerRecord mRecordRequestListenerRecord;
+
// Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
// handling will be disabled and no request will be handled.
@GuardedBy("mLock")
@@ -684,6 +692,15 @@ public final class HdmiControlService extends SystemService {
}
}
+ private class HdmiRecordRequestListenerRecord implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mRecordRequestListener = null;
+ }
+ }
+ }
+
private void enforceAccessPermission() {
getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
}
@@ -1014,17 +1031,65 @@ public final class HdmiControlService extends SystemService {
@Override
public void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
- // TODO: implement this.
+ HdmiControlService.this.setOneTouchRecordRequestListener(listener);
}
@Override
- public void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
- // TODO: implement this.
+ public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().startOneTouchRecord(recorderAddress, recordSource);
+ }
+ });
}
@Override
- public void startTimerRecording(int recorderAddress, byte[] recordSource) {
- // TODO: implement this.
+ public void stopOneTouchRecord(final int recorderAddress) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().stopOneTouchRecord(recorderAddress);
+ }
+ });
+ }
+
+ @Override
+ public void startTimerRecording(final int recorderAddress, final int sourceType,
+ final byte[] recordSource) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().startTimerRecording(recorderAddress, sourceType, recordSource);
+ }
+ });
+ }
+
+ @Override
+ public void clearTimerRecording(final int recorderAddress, final int sourceType,
+ final byte[] recordSource) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
+ }
+ });
}
}
@@ -1169,6 +1234,32 @@ public final class HdmiControlService extends SystemService {
}
}
+ private void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
+ synchronized (mLock) {
+ mRecordRequestListenerRecord = new HdmiRecordRequestListenerRecord();
+ try {
+ listener.asBinder().linkToDeath(mRecordRequestListenerRecord, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Listener already died", e);
+ return;
+ }
+ mRecordRequestListener = listener;
+ }
+ }
+
+ byte[] invokeRecordRequestListener(int recorderAddress) {
+ synchronized (mLock) {
+ try {
+ if (mRecordRequestListener != null) {
+ return mRecordRequestListener.onRecordRequestReceived(recorderAddress);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to start record.", e);
+ }
+ return EmptyArray.BYTE;
+ }
+ }
+
private void invokeCallback(IHdmiControlCallback callback, int result) {
try {
callback.onComplete(result);
diff --git a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
index 51aa984369d57..9ecbc5ebb338a 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
@@ -16,51 +16,118 @@
package com.android.server.hdmi;
-import static com.android.server.hdmi.Constants.RECORDING_TYPE_ANALOGUE_RF;
-import static com.android.server.hdmi.Constants.RECORDING_TYPE_DIGITAL_RF;
-import static com.android.server.hdmi.Constants.RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS;
-import static com.android.server.hdmi.Constants.RECORDING_TYPE_OWN_SOURCE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_ANALOGUE_SERVICE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_DIGITAL_SERVICE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_EXTERNAL_INPUT;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_STATUS_MESSAGE_START;
+
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
/**
* Feature action that performs one touch record. This class only provides a skeleton of one touch
* play and has no detail implementation.
*/
public class OneTouchRecordAction extends FeatureAction {
- private final int mRecorderAddress;
- private final int mRecordingType;
+ private static final String TAG = "OneTouchRecordAction";
- OneTouchRecordAction(HdmiCecLocalDevice source, int recorderAddress, int recordingType) {
+ // Timer out for waiting
+ private static final int RECORD_STATUS_TIMEOUT = 120000;
+
+ // State that waits for once sending
+ private static final int STATE_WAITING_FOR_RECORD_STATUS = 1;
+ // State that describes recording in progress.
+ private static final int STATE_RECORDING_IN_PROGRESS = 2;
+
+ private final int mRecorderAddress;
+ private final byte[] mRecordSource;
+
+ OneTouchRecordAction(HdmiCecLocalDevice source, int recorderAddress, byte[] recordSource) {
super(source);
mRecorderAddress = recorderAddress;
- mRecordingType = recordingType;
+ mRecordSource = recordSource;
}
@Override
boolean start() {
- return false;
+ sendRecordOn();
+ return true;
}
- private void sendRecordOn(int recordingType) {
- switch (recordingType) {
- case RECORDING_TYPE_DIGITAL_RF:
- break;
- case RECORDING_TYPE_ANALOGUE_RF:
- break;
- case RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS:
- break;
- case RECORDING_TYPE_OWN_SOURCE:
- break;
- // TODO: implement this.
- }
+ private void sendRecordOn() {
+ sendCommand(HdmiCecMessageBuilder.buildRecordOn(getSourceAddress(), mRecorderAddress,
+ mRecordSource),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ // if failed to send , display error message and finish action.
+ if (error != Constants.SEND_RESULT_SUCCESS) {
+ tv().displayOsd(MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION);
+ finish();
+ return;
+ }
+
+ mState = STATE_WAITING_FOR_RECORD_STATUS;
+ addTimer(mState, RECORD_STATUS_TIMEOUT);
+ }
+ });
}
@Override
boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_RECORD_STATUS) {
+ return false;
+ }
+
+ switch (cmd.getOpcode()) {
+ case Constants.MESSAGE_RECORD_STATUS:
+ return handleRecordStatus(cmd);
+
+ }
return false;
}
+ private boolean handleRecordStatus(HdmiCecMessage cmd) {
+ // Only handle message coming from original recorder.
+ if (cmd.getSource() != mRecorderAddress) {
+ return false;
+ }
+
+ int recordStatus = cmd.getParams()[0];
+ Slog.i(TAG, "Got record status:" + recordStatus + " from " + cmd.getSource());
+
+ int recordStatusMessageCode = recordStatus + MESSAGE_RECORDING_STATUS_MESSAGE_START;
+ tv().displayOsd(recordStatusMessageCode);
+
+ // If recording started successfully, change state and keep this action until
+ // received. Otherwise, finish action.
+ switch (recordStatusMessageCode) {
+ case MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE:
+ case MESSAGE_RECORDING_DIGITAL_SERVICE:
+ case MESSAGE_RECORDING_ANALOGUE_SERVICE:
+ case MESSAGE_RECORDING_EXTERNAL_INPUT:
+ mState = STATE_RECORDING_IN_PROGRESS;
+ mActionTimer.clearTimerMessage();
+ break;
+ default:
+ finish();
+ break;
+ }
+ return true;
+ }
+
@Override
void handleTimerEvent(int state) {
+ if (mState != state) {
+ Slog.w(TAG, "Timeout in invalid state:[Expected:" + mState + ", Actual:" + state + "]");
+ return;
+ }
+
+ tv().displayOsd(MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION);
+ finish();
}
int getRecorderAddress() {