Implement OneTouchRecordAction and stop one touch record.
In addition to it, add api for clearTimerRecording as well. Separately, I will replace logic for notifying message with callback interface. Bug: 16160962 Change-Id: I2368f7c697eb44ed4542c0ec4412c63a6ae41a5c
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
* </pre>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
@@ -211,13 +227,47 @@ public final class HdmiTvClient extends HdmiClient {
|
||||
* TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
|
||||
* tvClient.startTimerRecording(recorderAddress, source);
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 <Record TV Screen> is replying <Feature Abort> 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();
|
||||
|
||||
@@ -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<OneTouchRecordAction> 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.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. ******/
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <Record Status>
|
||||
private static final int RECORD_STATUS_TIMEOUT = 120000;
|
||||
|
||||
// State that waits for <Record Status> once sending <Record On>
|
||||
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 <Record On>, 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 <Record Off>
|
||||
// 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() {
|
||||
|
||||
Reference in New Issue
Block a user