Merge "Check the validity of the port id of HdmiCecMessage only in TV." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 278e67f..0d71150 100644
--- a/Android.mk
+++ b/Android.mk
@@ -155,7 +155,7 @@
core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \
core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl \
- core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl \
+ core/java/android/hardware/hdmi/IHdmiRecordListener.aidl \
core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \
core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
core/java/android/hardware/input/IInputManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 4e0d311..943fef5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29093,7 +29093,7 @@
field public static final int LIMIT_EXCEEDED = 15; // 0xf
field public static final int LOCAL = 3; // 0x3
field public static final int LOST_SIGNAL = 14; // 0xe
- field public static final int MAXIMUM_VALID_VALUE = 42; // 0x2a
+ field public static final int MAXIMUM_VALID_VALUE = 44; // 0x2c
field public static final int MINIMUM_VALID_VALUE = 0; // 0x0
field public static final int MMI = 6; // 0x6
field public static final int NORMAL = 2; // 0x2
@@ -29101,6 +29101,8 @@
field public static final int NOT_VALID = -1; // 0xffffffff
field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+ field public static final int OUTGOING_CANCELED = 44; // 0x2c
+ field public static final int OUTGOING_FAILURE = 43; // 0x2b
field public static final int OUT_OF_NETWORK = 11; // 0xb
field public static final int OUT_OF_SERVICE = 18; // 0x12
field public static final int POWER_OFF = 17; // 0x11
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b1cbb13..42b8dbf 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -21,6 +21,8 @@
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
@@ -371,12 +373,14 @@
*/
private static BluetoothAdapter sAdapter;
+ private static BluetoothLeScanner sBluetoothLeScanner;
+ private static BluetoothLeAdvertiser sBluetoothLeAdvertiser;
+
private final IBluetoothManager mManagerService;
private IBluetooth mService;
- private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
- private final Handler mHandler; // Handler to post the advertise callback to run on main thread.
private final Object mLock = new Object();
+ private final Map<LeScanCallback, ScanCallback> mLeScanClients;
/**
* Get a handle to the default local Bluetooth adapter.
@@ -411,8 +415,7 @@
mService = managerService.registerAdapter(mManagerCallback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
mManagerService = managerService;
- mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
- mHandler = new Handler(Looper.getMainLooper());
+ mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
}
/**
@@ -451,19 +454,40 @@
}
/**
- * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
+ * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations, or
+ * null if Bluetooth LE Advertising is not support on this device.
+ * <p>
+ * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
+ * on this device before calling this method.
*/
public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
- // TODO: Return null if this feature is not supported by hardware.
- return new BluetoothLeAdvertiser(mManagerService);
+ if (getState() != STATE_ON) {
+ return null;
+ }
+ if (!isMultipleAdvertisementSupported()) {
+ return null;
+ }
+ synchronized(mLock) {
+ if (sBluetoothLeAdvertiser == null) {
+ sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
+ }
+ }
+ return sBluetoothLeAdvertiser;
}
/**
* Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
*/
public BluetoothLeScanner getBluetoothLeScanner() {
- // TODO: Return null if BLE scan is not supported by hardware.
- return new BluetoothLeScanner(mManagerService);
+ if (getState() != STATE_ON) {
+ return null;
+ }
+ synchronized(mLock) {
+ if (sBluetoothLeScanner == null) {
+ sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
+ }
+ }
+ return sBluetoothLeScanner;
}
/**
@@ -1625,13 +1649,17 @@
* instead.
*/
@Deprecated
- public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
+ public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);
-
if (callback == null) {
if (DBG) Log.e(TAG, "startLeScan: null callback");
return false;
}
+ BluetoothLeScanner scanner = getBluetoothLeScanner();
+ if (scanner == null) {
+ if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
+ return false;
+ }
synchronized(mLeScanClients) {
if (mLeScanClients.containsKey(callback)) {
@@ -1646,13 +1674,50 @@
return false;
}
- UUID uuid = UUID.randomUUID();
- GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
- iGatt.registerClient(new ParcelUuid(uuid), wrapper);
- if (wrapper.scanStarted()) {
- mLeScanClients.put(callback, wrapper);
- return true;
+ ScanCallback scanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
+ // Should not happen.
+ Log.e(TAG, "LE Scan has already started");
+ return;
+ }
+ ScanRecord scanRecord = result.getScanRecord();
+ if (scanRecord == null) {
+ return;
+ }
+ if (serviceUuids != null) {
+ List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
+ for (UUID uuid : serviceUuids) {
+ uuids.add(new ParcelUuid(uuid));
+ }
+ List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
+ if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
+ if (DBG) Log.d(TAG, "uuids does not match");
+ return;
+ }
+ }
+ callback.onLeScan(result.getDevice(), result.getRssi(),
+ scanRecord.getBytes());
+ }
+ };
+ ScanSettings settings = new ScanSettings.Builder()
+ .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+
+ List<ScanFilter> filters = new ArrayList<ScanFilter>();
+ if (serviceUuids != null && serviceUuids.length > 0) {
+ // Note scan filter does not support matching an UUID array so we put one
+ // UUID to hardware and match the whole array in callback.
+ ScanFilter filter = new ScanFilter.Builder().setServiceUuid(
+ new ParcelUuid(serviceUuids[0])).build();
+ filters.add(filter);
}
+ scanner.startScan(filters, settings, scanCallback);
+
+ mLeScanClients.put(callback, scanCallback);
+ return true;
+
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
@@ -1672,264 +1737,17 @@
@Deprecated
public void stopLeScan(LeScanCallback callback) {
if (DBG) Log.d(TAG, "stopLeScan()");
- GattCallbackWrapper wrapper;
- synchronized(mLeScanClients) {
- wrapper = mLeScanClients.remove(callback);
- if (wrapper == null) return;
+ BluetoothLeScanner scanner = getBluetoothLeScanner();
+ if (scanner == null) {
+ return;
}
- wrapper.stopLeScan();
- }
-
- /**
- * Bluetooth GATT interface callbacks
- */
- private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
- private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
- private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
-
- private final LeScanCallback mLeScanCb;
-
- // mLeHandle 0: not registered
- // -1: scan stopped
- // >0: registered and scan started
- private int mLeHandle;
- private final UUID[] mScanFilter;
- private WeakReference<BluetoothAdapter> mBluetoothAdapter;
-
- public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter,
- LeScanCallback leScanCb, UUID[] uuid) {
- mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
- mLeScanCb = leScanCb;
- mScanFilter = uuid;
- mLeHandle = 0;
- }
-
- public boolean scanStarted() {
- return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
- }
-
- private boolean waitForRegisteration(int maxWaitCount) {
- boolean started = false;
- synchronized(this) {
- if (mLeHandle == -1) return false;
- int count = 0;
- // wait for callback registration and LE scan to start
- while (mLeHandle == 0 && count < maxWaitCount) {
- try {
- wait(LE_CALLBACK_REG_TIMEOUT);
- } catch (InterruptedException e) {
- Log.e(TAG, "Callback reg wait interrupted: " + e);
- }
- count++;
- }
- started = (mLeHandle > 0);
+ synchronized (mLeScanClients) {
+ ScanCallback scanCallback = mLeScanClients.remove(callback);
+ if (scanCallback == null) {
+ if (DBG) Log.d(TAG, "scan not started yet");
+ return;
}
- return started;
- }
-
- public void stopLeScan() {
- synchronized(this) {
- if (mLeHandle <= 0) {
- Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
- return;
- }
- BluetoothAdapter adapter = mBluetoothAdapter.get();
- if (adapter != null) {
- try {
- IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
- iGatt.stopScan(mLeHandle, false);
- iGatt.unregisterClient(mLeHandle);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to stop scan and unregister" + e);
- }
- } else {
- Log.e(TAG, "stopLeScan, BluetoothAdapter is null");
- }
- mLeHandle = -1;
- notifyAll();
- }
- }
-
- /**
- * Application interface registered - app is ready to go
- */
- public void onClientRegistered(int status, int clientIf) {
- if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status +
- " clientIf=" + clientIf);
- synchronized(this) {
- if (mLeHandle == -1) {
- if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
- }
-
- if (status == BluetoothGatt.GATT_SUCCESS) {
- mLeHandle = clientIf;
- IBluetoothGatt iGatt = null;
- try {
- BluetoothAdapter adapter = mBluetoothAdapter.get();
- if (adapter != null) {
- iGatt = adapter.getBluetoothManager().getBluetoothGatt();
- if (mScanFilter == null) {
- iGatt.startScan(mLeHandle, false);
- } else {
- ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
- for(int i = 0; i != uuids.length; ++i) {
- uuids[i] = new ParcelUuid(mScanFilter[i]);
- }
- iGatt.startScanWithUuids(mLeHandle, false, uuids);
- }
- } else {
- Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
- mLeHandle = -1;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "fail to start le scan: " + e);
- mLeHandle = -1;
- }
- if (mLeHandle == -1) {
- // registration succeeded but start scan or advertise failed
- if (iGatt != null) {
- try {
- iGatt.unregisterClient(mLeHandle);
- } catch (RemoteException e) {
- Log.e(TAG, "fail to unregister callback: " + mLeHandle +
- " error: " + e);
- }
- }
- }
- } else {
- // registration failed
- mLeHandle = -1;
- }
- notifyAll();
- }
- }
-
- public void onClientConnectionState(int status, int clientIf,
- boolean connected, String address) {
- // no op
- }
-
- /**
- * Callback reporting an LE scan result.
- * @hide
- */
- public void onScanResult(String address, int rssi, byte[] advData) {
- if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
-
- // Check null in case the scan has been stopped
- synchronized(this) {
- if (mLeHandle <= 0) return;
- }
- try {
- BluetoothAdapter adapter = mBluetoothAdapter.get();
- if (adapter == null) {
- Log.d(TAG, "onScanResult, BluetoothAdapter null");
- return;
- }
- mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
- }
- }
-
- public void onGetService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid) {
- // no op
- }
-
- public void onGetIncludedService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int inclSrvcType, int inclSrvcInstId,
- ParcelUuid inclSrvcUuid) {
- // no op
- }
-
- public void onGetCharacteristic(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int charProps) {
- // no op
- }
-
- public void onGetDescriptor(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descUuid) {
- // no op
- }
-
- public void onSearchComplete(String address, int status) {
- // no op
- }
-
- public void onCharacteristicRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid, byte[] value) {
- // no op
- }
-
- public void onCharacteristicWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid) {
- // no op
- }
-
- public void onNotify(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- byte[] value) {
- // no op
- }
-
- public void onDescriptorRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descrUuid, byte[] value) {
- // no op
- }
-
- public void onDescriptorWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descrUuid) {
- // no op
- }
-
- public void onExecuteWrite(String address, int status) {
- // no op
- }
-
- public void onReadRemoteRssi(String address, int rssi, int status) {
- // no op
- }
-
- public void onAdvertiseStateChange(int advertiseState, int status) {
- }
-
- @Override
- public void onMultiAdvertiseCallback(int status) {
- // no op
- }
-
- @Override
- public void onConfigureMTU(String address, int mtu, int status) {
- // no op
- }
-
- @Override
- public void onConnectionCongested(String address, boolean congested) {
- // no op
- }
-
- @Override
- public void onBatchScanResults(List<ScanResult> results) {
- // no op
- }
-
- @Override
- public void onFoundOrLost(boolean onFound, String address,int rssi,
- byte[] advData) {
- // no op
+ scanner.stopScan(scanCallback);
}
}
}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 533be13..edf823e 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -33,10 +33,8 @@
interface IBluetoothGatt {
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
- void startScan(in int appIf, in boolean isServer);
- void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids);
- void startScanWithFilters(in int appIf, in boolean isServer,
- in ScanSettings settings, in List<ScanFilter> filters);
+ void startScan(in int appIf, in boolean isServer, in ScanSettings settings,
+ in List<ScanFilter> filters);
void stopScan(in int appIf, in boolean isServer);
void flushPendingBatchResults(in int appIf, in boolean isServer);
void startMultiAdvertising(in int appIf,
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 980f717..8e7d400 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -151,6 +151,7 @@
synchronized (mLeScanClients) {
BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
if (wrapper == null) {
+ if (DBG) Log.d(TAG, "could not find callback wrapper");
return;
}
wrapper.stopLeScan();
@@ -266,7 +267,7 @@
if (status == BluetoothGatt.GATT_SUCCESS) {
mClientIf = clientIf;
try {
- mBluetoothGatt.startScanWithFilters(mClientIf, false, mSettings, mFilters);
+ mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters);
} catch (RemoteException e) {
Log.e(TAG, "fail to start le scan: " + e);
mClientIf = -1;
diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
index acf92f1..d663714 100644
--- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
@@ -67,6 +67,7 @@
// are immutable value.
private final int mLogicalAddress;
private final int mPhysicalAddress;
+ private final int mPortId;
private final int mDeviceType;
private final int mVendorId;
private final String mDisplayName;
@@ -80,11 +81,12 @@
public HdmiCecDeviceInfo createFromParcel(Parcel source) {
int logicalAddress = source.readInt();
int physicalAddress = source.readInt();
+ int portId = source.readInt();
int deviceType = source.readInt();
int vendorId = source.readInt();
String displayName = source.readString();
- return new HdmiCecDeviceInfo(logicalAddress, physicalAddress, deviceType,
- vendorId, displayName);
+ return new HdmiCecDeviceInfo(logicalAddress, physicalAddress, portId,
+ deviceType, vendorId, displayName);
}
@Override
@@ -98,15 +100,17 @@
*
* @param logicalAddress logical address of HDMI-CEC device
* @param physicalAddress physical address of HDMI-CEC device
+ * @param portId HDMI port ID (1 for HDMI1)
* @param deviceType type of device
* @param vendorId vendor id of device. Used for vendor specific command.
* @param displayName name of device
* @hide
*/
- public HdmiCecDeviceInfo(int logicalAddress, int physicalAddress, int deviceType,
+ public HdmiCecDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
int vendorId, String displayName) {
mLogicalAddress = logicalAddress;
mPhysicalAddress = physicalAddress;
+ mPortId = portId;
mDeviceType = deviceType;
mDisplayName = displayName;
mVendorId = vendorId;
@@ -127,6 +131,13 @@
}
/**
+ * Return the port ID.
+ */
+ public int getPortId() {
+ return mPortId;
+ }
+
+ /**
* Return type of the device. For more details, refer constants between
* {@link DEVICE_TV} and {@link DEVICE_INACTIVE}.
*/
@@ -179,6 +190,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mLogicalAddress);
dest.writeInt(mPhysicalAddress);
+ dest.writeInt(mPortId);
dest.writeInt(mDeviceType);
dest.writeInt(mVendorId);
dest.writeString(mDisplayName);
@@ -189,6 +201,7 @@
StringBuffer s = new StringBuffer();
s.append("logical_address: ").append(mLogicalAddress).append(", ");
s.append("physical_address: ").append(mPhysicalAddress).append(", ");
+ s.append("port_id: ").append(mPortId).append(", ");
s.append("device_type: ").append(mDeviceType).append(", ");
s.append("vendor_id: ").append(mVendorId).append(", ");
s.append("display_name: ").append(mDisplayName);
@@ -204,6 +217,7 @@
HdmiCecDeviceInfo other = (HdmiCecDeviceInfo) obj;
return mLogicalAddress == other.mLogicalAddress
&& mPhysicalAddress == other.mPhysicalAddress
+ && mPortId == other.mPortId
&& mDeviceType == other.mDeviceType
&& mVendorId == other.mVendorId
&& mDisplayName.equals(other.mDisplayName);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 521a439..55db1df 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -69,69 +69,71 @@
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;
+ // --- One Touch Recording success result
/** Recording currently selected source. Indicates the status of a recording. */
- public static final int MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x101;
+ public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x01;
/** Recording Digital Service. Indicates the status of a recording. */
- public static final int MESSAGE_RECORDING_DIGITAL_SERVICE = 0x102;
+ public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 0x02;
/** Recording Analogue Service. Indicates the status of a recording. */
- public static final int MESSAGE_RECORDING_ANALOGUE_SERVICE = 0x103;
+ public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 0x03;
/** Recording External input. Indicates the status of a recording. */
- public static final int MESSAGE_RECORDING_EXTERNAL_INPUT = 0x104;
+ public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 0x04;
+
+ // --- One Touch Record failure result
/** No recording – unable to record Digital Service. No suitable tuner. */
- public static final int MESSAGE_NO_RECORDNIG_UNABLE_DIGITAL_SERVICE = 0x105;
+ public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 0x05;
/** No recording – unable to record Analogue Service. No suitable tuner. */
- public static final int MESSAGE_NO_RECORDNIG_UNABLE_ANALOGUE_SERVICE = 0x106;
+ public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 0x06;
/**
* 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;
+ public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 0x07;
/** No recording – invalid External plug number */
- public static final int MESSAGE_NO_RECORDNIG_INVALID_EXTERNAL_PLUG_NUMBER = 0x109;
+ public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 0x09;
/** No recording – invalid External Physical Address */
- public static final int MESSAGE_NO_RECORDNIG_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x10A;
+ public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x0A;
/** No recording – CA system not supported */
- public static final int MESSAGE_NO_RECORDNIG_UNSUPPORTED_CA = 0x10B;
+ public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 0x0B;
/** No Recording – No or Insufficient CA Entitlements” */
- public static final int MESSAGE_NO_RECORDNIG_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x10C;
+ public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x0C;
/** No recording – Not allowed to copy source. Source is “copy never”. */
- public static final int MESSAGE_NO_RECORDNIG_DISALLOW_TO_COPY = 0x10D;
+ public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 0x0D;
/** No recording – No further copies allowed */
- public static final int MESSAGE_NO_RECORDNIG_DISALLOW_TO_FUTHER_COPIES = 0x10E;
+ public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 0x0E;
/** No recording – No media */
- public static final int MESSAGE_NO_RECORDNIG_NO_MEDIA = 0x110;
+ public static final int ONE_TOUCH_RECORD_NO_MEDIA = 0x10;
/** No recording – playing */
- public static final int MESSAGE_NO_RECORDNIG_PLAYING = 0x111;
+ public static final int ONE_TOUCH_RECORD_PLAYING = 0x11;
/** No recording – already recording */
- public static final int MESSAGE_NO_RECORDNIG_ALREADY_RECORDING = 0x112;
+ public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 0x12;
/** No recording – media protected */
- public static final int MESSAGE_NO_RECORDNIG_MEDIA_PROTECTED = 0x113;
+ public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 0x13;
/** No recording – no source signal */
- public static final int MESSAGE_NO_RECORDNIG_NO_SOURCE_SIGNAL = 0x114;
+ public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 0x14;
/** No recording – media problem */
- public static final int MESSAGE_NO_RECORDNIG_MEDIA_PROBLEM = 0x115;
+ public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 0x15;
/** No recording – not enough space available */
- public static final int MESSAGE_NO_RECORDNIG_NOT_ENOUGH_SPACE = 0x116;
+ public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 0x16;
/** No recording – Parental Lock On */
- public static final int MESSAGE_NO_RECORDNIG_PARENT_LOCK_ON = 0x117;
+ public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 0x17;
/** Recording terminated normally */
- public static final int MESSAGE_RECORDING_TERMINATED_NORMALLY = 0x11A;
+ public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 0x1A;
/** Recording has already terminated */
- public static final int MESSAGE_RECORDING_ALREADY_TERMINATED = 0x11B;
+ public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 0x1B;
/** No recording – other reason */
- public static final int MESSAGE_NO_RECORDNIG_OTHER_REASON = 0x11F;
+ public static final int ONE_TOUCH_RECORD_OTHER_REASON = 0x1F;
// 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;
+ public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 0x30;
/** No recording. Please check recorder and connection. */
- public static final int MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION = 0x131;
+ public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 0x31;
/** Cannot record currently displayed source. */
- public static final int MESSAGE_NO_RECORDING_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x132;
+ public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x32;
+ /** CEC is disabled. */
+ public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 0x33;
+ // --- Types for timer recording
/** Timer recording type for digital service source. */
public static final int TIMER_RECORDING_TYPE_DIGITAL = 1;
/** Timer recording type for analogue service source. */
@@ -139,6 +141,14 @@
/** Timer recording type for external source. */
public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3;
+ // --- Extra result value for timer recording.
+ /** No timer recording - check recorder and connection. */
+ public static final int TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 0x01;
+ /** No timer recording - cannot record selected source. */
+ public static final int TIME_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 0x02;
+ /** CEC is disabled. */
+ public static final int TIME_RECORDING_RESULT_EXTRA_CEC_DISABLED = 0x33;
+
// 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/HdmiRecordListener.java b/core/java/android/hardware/hdmi/HdmiRecordListener.java
new file mode 100644
index 0000000..0b1166b
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiRecordListener.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.hardware.hdmi;
+
+import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiRecordSources.RecordSource;
+
+/**
+ * Listener for hdmi record feature including one touch record and timer recording.
+ * @hide
+ */
+@SystemApi
+public abstract class HdmiRecordListener {
+ protected HdmiRecordListener() {}
+
+ /**
+ * Called when TV received one touch record request from record device. The client of this
+ * should use {@link HdmiRecordSources} to return it.
+ *
+ * @param recorderAddress
+ * @return record source to be used for recording. Null if no device is available.
+ */
+ public abstract RecordSource getOneTouchRecordSource(int recorderAddress);
+
+ /**
+ * Called when one touch record is started or failed during initialization.
+ *
+ * @param result result code. For more details, please look at all constants starting with
+ * "ONE_TOUCH_RECORD_". Only
+ * {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE},
+ * {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE},
+ * {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE}, and
+ * {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT} mean normal
+ * start of recording; otherwise, describes failure.
+ */
+ public void onOneTouchRecordResult(int result) {
+ }
+
+ /**
+ * Called when timer recording is started or failed during initialization.
+ *
+ * @param result The most significant three bytes may contain result of <Timer Status>
+ * while the least significant byte may have error message like
+ * {@link HdmiControlManager#TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION}
+ * or
+ * {@link HdmiControlManager #TIME_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE}
+ * . If the least significant byte has non zero value the most significant three bytes
+ * may have 0 value.
+ */
+ // TODO: implement result parser.
+ public void onTimerRecordingResult(int result) {
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiRecordSources.java b/core/java/android/hardware/hdmi/HdmiRecordSources.java
index 296cae6..917d1d9 100644
--- a/core/java/android/hardware/hdmi/HdmiRecordSources.java
+++ b/core/java/android/hardware/hdmi/HdmiRecordSources.java
@@ -492,7 +492,7 @@
/** Indicates that a service is identified by a logical or virtual channel number. */
private static final int DIGITAL_SERVICE_IDENTIFIED_BY_CHANNEL = 1;
- private static final int EXTRA_DATA_SIZE = 7;
+ static final int EXTRA_DATA_SIZE = 7;
/**
* Type of identification. It should be one of DIGITAL_SERVICE_IDENTIFIED_BY_DIGITAL_ID and
@@ -609,7 +609,7 @@
*/
@SystemApi
public static final class AnalogueServiceSource extends RecordSource {
- private static final int EXTRA_DATA_SIZE = 4;
+ static final int EXTRA_DATA_SIZE = 4;
/** Indicates the Analogue broadcast type. */
private final int mBroadcastType;
@@ -668,7 +668,7 @@
*/
@SystemApi
public static final class ExternalPlugData extends RecordSource {
- private static final int EXTRA_DATA_SIZE = 1;
+ static final int EXTRA_DATA_SIZE = 1;
/** External Plug number on the Recording Device. */
private final int mPlugNumber;
@@ -713,7 +713,7 @@
*/
@SystemApi
public static final class ExternalPhysicalAddress extends RecordSource {
- private static final int EXTRA_DATA_SIZE = 2;
+ static final int EXTRA_DATA_SIZE = 2;
private final int mPhysicalAddress;
@@ -751,6 +751,7 @@
* Check the byte array of record source.
* @hide
*/
+ @SystemApi
public static boolean checkRecordSource(byte[] recordSource) {
int recordSourceType = recordSource[0];
int extraDataSize = recordSource.length - 1;
diff --git a/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
index 3e5e49b..01b4dd3 100644
--- a/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
+++ b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
@@ -16,6 +16,10 @@
package android.hardware.hdmi;
+import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE;
+import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL;
+import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
+
import android.annotation.SystemApi;
import android.hardware.hdmi.HdmiRecordSources.AnalogueServiceSource;
import android.hardware.hdmi.HdmiRecordSources.DigitalServiceSource;
@@ -420,4 +424,35 @@
return getDataSize(false);
}
}
+
+ /**
+ * Check the byte array of timer record source.
+ * @param sourcetype
+ * @param recordSource
+ * @hide
+ */
+ @SystemApi
+ public static boolean checkTimerRecordSource(int sourcetype, byte[] recordSource) {
+ int recordSourceSize = recordSource.length - TimerInfo.BASIC_INFO_SIZE;
+ switch (sourcetype) {
+ case TIMER_RECORDING_TYPE_DIGITAL:
+ return DigitalServiceSource.EXTRA_DATA_SIZE == recordSourceSize;
+ case TIMER_RECORDING_TYPE_ANALOGUE:
+ return AnalogueServiceSource.EXTRA_DATA_SIZE == recordSourceSize;
+ case TIMER_RECORDING_TYPE_EXTERNAL:
+ int specifier = recordSource[TimerInfo.BASIC_INFO_SIZE];
+ if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG) {
+ // One byte for specifier.
+ return ExternalPlugData.EXTRA_DATA_SIZE + 1 == recordSourceSize;
+ } else if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS) {
+ // One byte for specifier.
+ return ExternalPhysicalAddress.EXTRA_DATA_SIZE + 1 == recordSourceSize;
+ } else {
+ // Invalid specifier.
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 6080914..3436287 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -15,10 +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.hardware.hdmi.HdmiRecordSources.RecordSource;
+import android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource;
import android.os.RemoteException;
import android.util.Log;
@@ -155,27 +154,15 @@
}
/**
- * Callback interface to used to get notified when a record request from recorder device.
+ * Set record listener
+ *
+ * @param listener
*/
- public interface RecordRequestListener {
- /**
- * Called when tv receives request request from recorder device. When it's called,
- * it should return record source in byte array so that hdmi control service
- * can start recording with the given source info.
- *
- * @return {@link HdmiRecordSources} to be used to set recording info
- */
- RecordSource onRecordRequestReceived(int recorderAddress);
- }
-
- /**
- * Set {@link RecordRequestListener} to hdmi control service.
- */
- public void setOneTouchRecordRequestListener(RecordRequestListener listener) {
+ public void setRecordListener(HdmiRecordListener listener) {
try {
- mService.setOneTouchRecordRequestListener(getCallbackWrapper(listener));
+ mService.setHdmiRecordListener(getListenerWrapper(listener));
} catch (RemoteException e) {
- Log.e(TAG, "failed to set record request listener: ", e);
+ Log.e(TAG, "failed to set record listener.", e);
}
}
@@ -282,13 +269,12 @@
};
}
- private static IHdmiRecordRequestListener getCallbackWrapper(
- final RecordRequestListener listener) {
- return new IHdmiRecordRequestListener.Stub() {
+ private static IHdmiRecordListener getListenerWrapper(final HdmiRecordListener callback) {
+ return new IHdmiRecordListener.Stub() {
@Override
- public byte[] onRecordRequestReceived(int recorderAddress) throws RemoteException {
+ public byte[] getOneTouchRecordSource(int recorderAddress) {
HdmiRecordSources.RecordSource source =
- listener.onRecordRequestReceived(recorderAddress);
+ callback.getOneTouchRecordSource(recorderAddress);
if (source == null) {
return EmptyArray.BYTE;
}
@@ -296,6 +282,16 @@
source.toByteArray(true, data, 0);
return data;
}
+
+ @Override
+ public void onOneTouchRecordResult(int result) {
+ callback.onOneTouchRecordResult(result);
+ }
+
+ @Override
+ public void onTimerRecordingResult(int result) {
+ callback.onTimerRecordingResult(result);
+ }
};
}
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 95e0ee0..808e0c9 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -22,7 +22,7 @@
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
-import android.hardware.hdmi.IHdmiRecordRequestListener;
+import android.hardware.hdmi.IHdmiRecordListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
@@ -62,7 +62,7 @@
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId);
void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
- void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener);
+ void setHdmiRecordListener(IHdmiRecordListener callback);
void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
void stopOneTouchRecord(int recorderAddress);
void startTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource);
diff --git a/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl b/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl
new file mode 100644
index 0000000..fba4b05
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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 android.hardware.hdmi;
+
+/**
+ * @hide
+ */
+ interface IHdmiRecordListener {
+ /**
+ * Called when TV received one touch record request from record device.
+ *
+ * @param recorderAddress
+ * @return record source in byte array.
+ */
+ byte[] getOneTouchRecordSource(int recorderAddress);
+
+ /**
+ * Called when one touch record is started or failed during initialization.
+ *
+ * @param result result code for one touch record
+ */
+ void onOneTouchRecordResult(int result);
+ /**
+ * Called when timer recording is started or failed during initialization.
+ * @param result result code for timer recording
+ */
+ void onTimerRecordingResult(int result);
+ }
\ No newline at end of file
diff --git a/core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl b/core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl
deleted file mode 100644
index f8f9e5f..0000000
--- a/core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 android.hardware.hdmi;
-
-/**
- * Callback interface definition for HDMI client to fill record source info
- * when it gets record start request.
- *
- * @hide
- */
-interface IHdmiRecordRequestListener {
- byte[] onRecordRequestReceived(int recorderAddress);
-}
\ No newline at end of file
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 30831cd..52618cc 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -222,7 +222,6 @@
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
* @attr ref android.R.styleable#TextView_elegantTextHeight
- * @attr ref android.R.styleable#TextView_letterSpacing
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0e87893..fedea4e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3755,7 +3755,7 @@
<attr name="shadowRadius" format="float" />
<!-- Elegant text height, especially for less compacted complex script text. -->
<attr name="elegantTextHeight" format="boolean" />
- <!-- @hide Text letter-spacing. -->
+ <!-- Text letter-spacing. -->
<attr name="letterSpacing" format="float" />
</declare-styleable>
<declare-styleable name="TextClock">
@@ -4050,7 +4050,7 @@
<attr name="textAllCaps" />
<!-- Elegant text height, especially for less compacted complex script text. -->
<attr name="elegantTextHeight" />
- <!-- @hide Text letter-spacing. -->
+ <!-- Text letter-spacing. -->
<attr name="letterSpacing" />
</declare-styleable>
<declare-styleable name="TextViewAppearance">
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b1902dd..a6e85e9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2268,7 +2268,6 @@
<public type="attr" name="checkMarkTintMode" />
<public type="attr" name="popupTheme" />
<public type="attr" name="toolbarStyle" />
- <public type="attr" name="letterSpacing" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index a75b485..86e14e1 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -69,6 +69,7 @@
private final int mLogicalAddress;
private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
+ private int mPortId = Constants.INVALID_PORT_ID;
private int mVendorId = Constants.UNKNOWN_VENDOR_ID;
private String mDisplayName = "";
private int mDeviceType = HdmiCecDeviceInfo.DEVICE_INACTIVE;
@@ -78,8 +79,8 @@
}
private HdmiCecDeviceInfo toHdmiCecDeviceInfo() {
- return new HdmiCecDeviceInfo(mLogicalAddress, mPhysicalAddress, mDeviceType, mVendorId,
- mDisplayName);
+ return new HdmiCecDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType,
+ mVendorId, mDisplayName);
}
}
@@ -252,12 +253,17 @@
byte params[] = cmd.getParams();
current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
+ current.mPortId = getPortId(current.mPhysicalAddress);
current.mDeviceType = params[2] & 0xFF;
increaseProcessedDeviceCount();
checkAndProceedStage();
}
+ private int getPortId(int physicalAddress) {
+ return tv().getPortId(physicalAddress);
+ }
+
private void handleSetOsdName(HdmiCecMessage cmd) {
Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index bbecafa..0d457e3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,10 +16,18 @@
package com.android.server.hdmi;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_CEC_DISABLED;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN;
+import static android.hardware.hdmi.HdmiControlManager.TIME_RECORDING_RESULT_EXTRA_CEC_DISABLED;
+import static android.hardware.hdmi.HdmiControlManager.TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION;
+import static android.hardware.hdmi.HdmiControlManager.TIME_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE;
+
import android.content.Intent;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiRecordSources;
+import android.hardware.hdmi.HdmiTimerRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -47,7 +55,7 @@
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceTv";
- // Whether ARC is available or not. "true" means that ARC is estabilished between TV and
+ // Whether ARC is available or not. "true" means that ARC is established between TV and
// AVR as audio receiver.
@ServiceThreadOnly
private boolean mArcEstablished = false;
@@ -190,6 +198,10 @@
}
}
+ int getPortId(int physicalAddress) {
+ return mService.pathToPortId(physicalAddress);
+ }
+
/**
* Returns the previous port id kept to handle input switching on <Inactive Source>.
*/
@@ -512,8 +524,8 @@
}
addCecDevice(new HdmiCecDeviceInfo(deviceInfo.getLogicalAddress(),
- deviceInfo.getPhysicalAddress(), deviceInfo.getDeviceType(),
- deviceInfo.getVendorId(), osdName));
+ deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
+ deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
return true;
}
@@ -819,7 +831,8 @@
// Assumes only one OneTouchRecordAction.
OneTouchRecordAction action = actions.get(0);
if (action.getRecorderAddress() != message.getSource()) {
- displayOsd(HdmiControlManager.MESSAGE_NO_RECORDING_PREVIOUS_RECORDING_IN_PROGRESS);
+ announceOneTouchRecordResult(
+ HdmiControlManager.ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS);
}
return super.handleRecordTvScreen(message);
}
@@ -830,6 +843,14 @@
return true;
}
+ void announceOneTouchRecordResult(int result) {
+ mService.invokeOneTouchRecordResult(result);
+ }
+
+ void announceTimerRecordingResult(int result) {
+ mService.invokeTimerRecordingResult(result);
+ }
+
private boolean isMessageForSystemAudio(HdmiCecMessage message) {
if (message.getSource() != Constants.ADDR_AUDIO_SYSTEM
|| message.getDestination() != Constants.ADDR_TV
@@ -1241,16 +1262,19 @@
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
+ announceOneTouchRecordResult(ONE_TOUCH_RECORD_CEC_DISABLED);
return;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
return;
}
if (!checkRecordSource(recordSource)) {
Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
+ announceOneTouchRecordResult(ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN);
return;
}
@@ -1264,11 +1288,13 @@
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not stop one touch record. CEC control is disabled.");
+ announceOneTouchRecordResult(ONE_TOUCH_RECORD_CEC_DISABLED);
return;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
return;
}
@@ -1292,8 +1318,35 @@
@ServiceThreadOnly
void startTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
assertRunOnServiceThread();
+ if (!mService.isControlEnabled()) {
+ Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
+ announceTimerRecordingResult(TIME_RECORDING_RESULT_EXTRA_CEC_DISABLED);
+ return;
+ }
- // TODO: implement this.
+ if (!checkRecorder(recorderAddress)) {
+ Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ announceTimerRecordingResult(
+ TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
+ return;
+ }
+
+ if (!checkTimerRecordingSource(sourceType, recordSource)) {
+ Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
+ announceTimerRecordingResult(
+ TIME_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE);
+ return;
+ }
+
+ addAndStartAction(
+ new TimerRecordingAction(this, recorderAddress, sourceType, recordSource));
+ Slog.i(TAG, "Start [Timer Recording]-Target:" + recorderAddress + ", SourceType:"
+ + sourceType + ", RecordSource:" + Arrays.toString(recordSource));
+ }
+
+ private boolean checkTimerRecordingSource(int sourceType, byte[] recordSource) {
+ return (recordSource != null)
+ && HdmiTimerRecordSources.checkTimerRecordSource(sourceType, recordSource);
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 79f1964..0855bfa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -478,6 +478,78 @@
return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF);
}
+ /**
+ * Build <Set Digital Timer> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of timing information and digital service information to be recorded
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSetDigitalTimer(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
+ }
+
+ /**
+ * Build <Set Analogue Timer> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of timing information and analog service information to be recorded
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSetAnalogueTimer(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
+ }
+
+ /**
+ * Build <Set External Timer> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of timing information and external source information to be recorded
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSetExternalTimer(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
+ }
+
+ /**
+ * Build <Clear Digital Timer> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of timing information and digital service information to be cleared
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildClearDigitalTimer(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
+ }
+
+ /**
+ * Build <Clear Analog Timer> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of timing information and analog service information to be cleared
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildClearAnalogueTimer(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
+ }
+
+ /**
+ * Build <Clear Digital Timer> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of timing information and external source information to be cleared
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildClearExternalTimer(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
+ }
+
/***** 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 eafcd7c..3532213 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -32,7 +32,7 @@
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
-import android.hardware.hdmi.IHdmiRecordRequestListener;
+import android.hardware.hdmi.IHdmiRecordListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.media.AudioManager;
@@ -45,6 +45,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings.Global;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -160,10 +161,10 @@
private InputChangeListenerRecord mInputChangeListenerRecord;
@GuardedBy("mLock")
- private IHdmiRecordRequestListener mRecordRequestListener;
+ private IHdmiRecordListener mRecordListener;
@GuardedBy("mLock")
- private HdmiRecordRequestListenerRecord mRecordRequestListenerRecord;
+ private HdmiRecordListenerRecord mRecordListenerRecord;
// 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.
@@ -197,6 +198,12 @@
// from being modified.
private List<HdmiPortInfo> mPortInfo;
+ // Map from path(physical address) to port ID.
+ private SparseIntArray mPortIdMap = new SparseIntArray();
+
+ // Map from port ID to HdmiPortInfo.
+ private SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>();
+
private HdmiCecMessageValidator mMessageValidator;
private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
@@ -238,7 +245,7 @@
if (mMhlController == null) {
Slog.i(TAG, "Device does not support MHL-control.");
}
- mPortInfo = initPortInfo();
+ initPortInfo();
mMessageValidator = new HdmiCecMessageValidator(this);
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
@@ -318,7 +325,7 @@
// Initialize HDMI port information. Combine the information from CEC and MHL HAL and
// keep them in one place.
@ServiceThreadOnly
- private List<HdmiPortInfo> initPortInfo() {
+ private void initPortInfo() {
assertRunOnServiceThread();
HdmiPortInfo[] cecPortInfo = null;
@@ -328,7 +335,12 @@
cecPortInfo = mCecController.getPortInfos();
}
if (cecPortInfo == null) {
- return Collections.emptyList();
+ return;
+ }
+
+ for (HdmiPortInfo info : cecPortInfo) {
+ mPortIdMap.put(info.getAddress(), info.getId());
+ mPortInfoMap.put(info.getId(), info);
}
HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
@@ -337,27 +349,24 @@
// mhlPortInfo = mMhlController.getPortInfos();
}
- // Use the id (port number) to find the matched info between CEC and MHL to combine them
- // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
- ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
- for (int i = 0; i < cecPortInfo.length; ++i) {
- HdmiPortInfo cec = cecPortInfo[i];
- int id = cec.getId();
- boolean mhlInfoFound = false;
- for (HdmiPortInfo mhl : mhlPortInfo) {
- if (id == mhl.getId()) {
- result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
- cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
- mhlInfoFound = true;
- break;
- }
- }
- if (!mhlInfoFound) {
- result.add(cec);
+ ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
+ for (HdmiPortInfo info : mhlPortInfo) {
+ if (info.isMhlSupported()) {
+ mhlSupportedPorts.add(info.getId());
}
}
- return Collections.unmodifiableList(result);
+ // Build HDMI port info list with CEC port info plus MHL supported flag.
+ ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
+ for (HdmiPortInfo info : cecPortInfo) {
+ if (mhlSupportedPorts.contains(info.getId())) {
+ result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
+ info.isCecSupported(), true, info.isArcSupported()));
+ } else {
+ result.add(info);
+ }
+ }
+ mPortInfo = Collections.unmodifiableList(result);
}
/**
@@ -366,22 +375,19 @@
* @param portId HDMI port id
* @return {@link HdmiPortInfo} for the given port
*/
+ @ServiceThreadOnly
HdmiPortInfo getPortInfo(int portId) {
- // mPortInfo is an unmodifiable list and the only reference to its inner list.
- // No lock is necessary.
- for (HdmiPortInfo info : mPortInfo) {
- if (portId == info.getId()) {
- return info;
- }
- }
- return null;
+ assertRunOnServiceThread();
+ return mPortInfoMap.get(portId, null);
}
/**
* Returns the routing path (physical address) of the HDMI port for the given
* port id.
*/
+ @ServiceThreadOnly
int portIdToPath(int portId) {
+ assertRunOnServiceThread();
HdmiPortInfo portInfo = getPortInfo(portId);
if (portInfo == null) {
Slog.e(TAG, "Cannot find the port info: " + portId);
@@ -396,23 +402,17 @@
* the port id to be returned is the ID associated with the port address
* 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
*/
+ @ServiceThreadOnly
int pathToPortId(int path) {
+ assertRunOnServiceThread();
int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
- for (HdmiPortInfo info : mPortInfo) {
- if (portAddress == info.getAddress()) {
- return info.getId();
- }
- }
- return Constants.INVALID_PORT_ID;
+ return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
}
+ @ServiceThreadOnly
boolean isValidPortId(int portId) {
- for (HdmiPortInfo info : mPortInfo) {
- if (portId == info.getId()) {
- return true;
- }
- }
- return false;
+ assertRunOnServiceThread();
+ return getPortInfo(portId) != null;
}
/**
@@ -468,12 +468,12 @@
/**
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
+ @ServiceThreadOnly
boolean isConnectedToArcPort(int physicalAddress) {
- for (HdmiPortInfo portInfo : mPortInfo) {
- if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
- && portInfo.isArcSupported()) {
- return true;
- }
+ assertRunOnServiceThread();
+ int portId = mPortIdMap.get(physicalAddress);
+ if (portId != Constants.INVALID_PORT_ID) {
+ return mPortInfoMap.get(portId).isArcSupported();
}
return false;
}
@@ -622,7 +622,8 @@
// TODO: find better name instead of model name.
String displayName = Build.MODEL;
return new HdmiCecDeviceInfo(logicalAddress,
- getPhysicalAddress(), deviceType, getVendorId(), displayName);
+ getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
+ getVendorId(), displayName);
}
// Record class that monitors the event of the caller of being killed. Used to clean up
@@ -692,11 +693,11 @@
}
}
- private class HdmiRecordRequestListenerRecord implements IBinder.DeathRecipient {
+ private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
@Override
public void binderDied() {
synchronized (mLock) {
- mRecordRequestListener = null;
+ mRecordListener = null;
}
}
}
@@ -1023,11 +1024,11 @@
}
}
});
- }
+ }
@Override
- public void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
- HdmiControlService.this.setOneTouchRecordRequestListener(listener);
+ public void setHdmiRecordListener(IHdmiRecordListener listener) {
+ HdmiControlService.this.setHdmiRecordListener(listener);
}
@Override
@@ -1230,32 +1231,55 @@
}
}
- private void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
+ private void setHdmiRecordListener(IHdmiRecordListener listener) {
synchronized (mLock) {
- mRecordRequestListenerRecord = new HdmiRecordRequestListenerRecord();
+ mRecordListenerRecord = new HdmiRecordListenerRecord();
try {
- listener.asBinder().linkToDeath(mRecordRequestListenerRecord, 0);
+ listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
} catch (RemoteException e) {
- Slog.w(TAG, "Listener already died", e);
- return;
+ Slog.w(TAG, "Listener already died.", e);
}
- mRecordRequestListener = listener;
+ mRecordListener = listener;
}
}
byte[] invokeRecordRequestListener(int recorderAddress) {
synchronized (mLock) {
- try {
- if (mRecordRequestListener != null) {
- return mRecordRequestListener.onRecordRequestReceived(recorderAddress);
+ if (mRecordListener != null) {
+ try {
+ return mRecordListener.getOneTouchRecordSource(recorderAddress);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to start record.", e);
}
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to start record.", e);
}
return EmptyArray.BYTE;
}
}
+ void invokeOneTouchRecordResult(int result) {
+ synchronized (mLock) {
+ if (mRecordListener != null) {
+ try {
+ mRecordListener.onOneTouchRecordResult(result);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
+ }
+ }
+ }
+ }
+
+ void invokeTimerRecordingResult(int result) {
+ synchronized (mLock) {
+ if (mRecordListener != null) {
+ try {
+ mRecordListener.onTimerRecordingResult(result);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
+ }
+ }
+ }
+ }
+
private void invokeCallback(IHdmiControlCallback callback, int result) {
try {
callback.onComplete(result);
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 7d7a95b..4ef7c96 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -153,6 +153,7 @@
}
tv().addCecDevice(new HdmiCecDeviceInfo(
mDeviceLogicalAddress, mDevicePhysicalAddress,
+ tv().getPortId(mDevicePhysicalAddress),
HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress),
mVendorId, mDisplayName));
diff --git a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
index 9ecbc5e..befc640 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
@@ -16,26 +16,24 @@
package com.android.server.hdmi;
-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 static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE;
+import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT;
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.
+ * Feature action that performs one touch record.
*/
public class OneTouchRecordAction extends FeatureAction {
private static final String TAG = "OneTouchRecordAction";
- // Timer out for waiting <Record Status>
- private static final int RECORD_STATUS_TIMEOUT = 120000;
+ // Timer out for waiting <Record Status> 120s
+ private static final int RECORD_STATUS_TIMEOUT_MS = 120000;
// State that waits for <Record Status> once sending <Record On>
private static final int STATE_WAITING_FOR_RECORD_STATUS = 1;
@@ -65,13 +63,14 @@
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);
+ tv().announceOneTouchRecordResult(
+ ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
finish();
return;
}
mState = STATE_WAITING_FOR_RECORD_STATUS;
- addTimer(mState, RECORD_STATUS_TIMEOUT);
+ addTimer(mState, RECORD_STATUS_TIMEOUT_MS);
}
});
}
@@ -97,18 +96,16 @@
}
int recordStatus = cmd.getParams()[0];
+ tv().announceOneTouchRecordResult(recordStatus);
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:
+ switch (recordStatus) {
+ case ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE:
+ case ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE:
+ case ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE:
+ case ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT:
mState = STATE_RECORDING_IN_PROGRESS;
mActionTimer.clearTimerMessage();
break;
@@ -126,7 +123,7 @@
return;
}
- tv().displayOsd(MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION);
+ tv().announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
finish();
}
diff --git a/services/core/java/com/android/server/hdmi/TimerRecordingAction.java b/services/core/java/com/android/server/hdmi/TimerRecordingAction.java
new file mode 100644
index 0000000..1dc26f1
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/TimerRecordingAction.java
@@ -0,0 +1,169 @@
+/*
+ * 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 static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE;
+import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL;
+import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
+import static android.hardware.hdmi.HdmiControlManager.TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION;
+
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+import java.util.Arrays;
+
+/**
+ * Feature action that performs timer recording.
+ */
+public class TimerRecordingAction extends FeatureAction {
+ private static final String TAG = "TimerRecordingAction";
+
+ // Timer out for waiting <Timer Status> 120s.
+ private static final int TIMER_STATUS_TIMEOUT_MS = 120000;
+
+ // State that waits for <Timer Status> once sending <Set XXX Timer>
+ private static final int STATE_WAITING_FOR_TIMER_STATUS = 1;
+
+ private final int mRecorderAddress;
+ private final int mSourceType;
+ private final byte[] mRecordSource;
+
+ TimerRecordingAction(HdmiCecLocalDevice source, int recorderAddress, int sourceType,
+ byte[] recordSource) {
+ super(source);
+ mRecorderAddress = recorderAddress;
+ mSourceType = sourceType;
+ mRecordSource = recordSource;
+ }
+
+ @Override
+ boolean start() {
+ sendTimerMessage();
+ return true;
+ }
+
+ private void sendTimerMessage() {
+ HdmiCecMessage message = null;
+ switch (mSourceType) {
+ case TIMER_RECORDING_TYPE_DIGITAL:
+ message = HdmiCecMessageBuilder.buildSetDigitalTimer(getSourceAddress(),
+ mRecorderAddress, mRecordSource);
+ break;
+ case TIMER_RECORDING_TYPE_ANALOGUE:
+ message = HdmiCecMessageBuilder.buildSetAnalogueTimer(getSourceAddress(),
+ mRecorderAddress, mRecordSource);
+ break;
+ case TIMER_RECORDING_TYPE_EXTERNAL:
+ message = HdmiCecMessageBuilder.buildSetExternalTimer(getSourceAddress(),
+ mRecorderAddress, mRecordSource);
+ break;
+ default:
+ tv().announceTimerRecordingResult(
+ TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
+ finish();
+ return;
+ }
+ sendCommand(message, new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != Constants.SEND_RESULT_SUCCESS) {
+ mState = STATE_WAITING_FOR_TIMER_STATUS;
+ addTimer(mState, TIMER_STATUS_TIMEOUT_MS);
+ finish();
+ return;
+ }
+ }
+ });
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_TIMER_STATUS) {
+ return false;
+ }
+
+ if (cmd.getSource() != mRecorderAddress) {
+ return false;
+ }
+
+ switch (cmd.getOpcode()) {
+ case Constants.MESSAGE_TIMER_STATUS:
+ return handleTimerStatus(cmd);
+ case Constants.MESSAGE_FEATURE_ABORT:
+ return handleFeatureAbort(cmd);
+ }
+ return false;
+ }
+
+ private boolean handleTimerStatus(HdmiCecMessage cmd) {
+ byte[] timerStatusData = cmd.getParams();
+ // [Timer Status Data] should be one or three bytes.
+ if (timerStatusData.length == 1 || timerStatusData.length == 3) {
+ tv().announceTimerRecordingResult(bytesToInt(timerStatusData));
+ Slog.i(TAG, "Received [Timer Status Data]:" + Arrays.toString(timerStatusData));
+ } else {
+ Slog.w(TAG, "Invalid [Timer Status Data]:" + Arrays.toString(timerStatusData));
+ }
+
+ // Unlike one touch record, finish timer record when <Timer Status> is received.
+ finish();
+ return true;
+ }
+
+ private boolean handleFeatureAbort(HdmiCecMessage cmd) {
+ byte[] params = cmd.getParams();
+ int messageType = params[0];
+ switch (messageType) {
+ case Constants.MESSAGE_SET_DIGITAL_TIMER: // fall through
+ case Constants.MESSAGE_SET_ANALOG_TIMER: // fall through
+ case Constants.MESSAGE_SET_EXTERNAL_TIMER: // fall through
+ break;
+ default:
+ return false;
+ }
+ int reason = params[1];
+ Slog.i(TAG, "[Feature Abort] for " + messageType + " reason:" + reason);
+ tv().announceTimerRecordingResult(TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
+ finish();
+ return true;
+ }
+
+ // Convert byte array to int.
+ private static int bytesToInt(byte[] data) {
+ if (data.length > 4) {
+ throw new IllegalArgumentException("Invalid data size:" + Arrays.toString(data));
+ }
+ int result = 0;
+ for (int i = 0; i < data.length; ++i) {
+ int shift = (3 - i) * 8;
+ result |= ((data[i] & 0xFF) << shift);
+ }
+ return result;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ Slog.w(TAG, "Timeout in invalid state:[Expected:" + mState + ", Actual:" + state + "]");
+ return;
+ }
+
+ tv().announceTimerRecordingResult(TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
+ finish();
+ }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5808e2f..d0fdc8b 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -693,45 +693,6 @@
updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
}
- private void unregisterClientInternalLocked(IBinder clientToken, String inputId,
- int userId) {
- UserState userState = getUserStateLocked(userId);
- ClientState clientState = userState.clientStateMap.get(clientToken);
- if (clientState != null) {
- clientState.mInputIds.remove(inputId);
- if (clientState.isEmpty()) {
- userState.clientStateMap.remove(clientToken);
- }
- }
-
- TvInputInfo info = userState.inputMap.get(inputId).mInfo;
- if (info == null) {
- return;
- }
- ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
- if (serviceState == null) {
- return;
- }
-
- // Remove this client from the client list and unregister the callback.
- serviceState.mClientTokens.remove(clientToken);
- if (!serviceState.mClientTokens.isEmpty()) {
- // We have other clients who want to keep the callback. Do this later.
- return;
- }
- if (serviceState.mService == null || serviceState.mCallback == null) {
- return;
- }
- try {
- serviceState.mService.unregisterCallback(serviceState.mCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- } finally {
- serviceState.mCallback = null;
- updateServiceConnectionLocked(info.getComponent(), userId);
- }
- }
-
private void notifyInputAddedLocked(UserState userState, String inputId) {
if (DEBUG) {
Slog.d(TAG, "notifyInputAdded: inputId = " + inputId);
@@ -1415,13 +1376,6 @@
pw.increaseIndent();
- pw.println("mInputIds:");
- pw.increaseIndent();
- for (String inputId : client.mInputIds) {
- pw.println(inputId);
- }
- pw.decreaseIndent();
-
pw.println("mSessionTokens:");
pw.increaseIndent();
for (IBinder token : client.mSessionTokens) {
@@ -1545,7 +1499,6 @@
}
private final class ClientState implements IBinder.DeathRecipient {
- private final List<String> mInputIds = new ArrayList<String>();
private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
private IBinder mClientToken;
@@ -1557,7 +1510,7 @@
}
public boolean isEmpty() {
- return mInputIds.isEmpty() && mSessionTokens.isEmpty();
+ return mSessionTokens.isEmpty();
}
@Override
@@ -1565,17 +1518,13 @@
synchronized (mLock) {
UserState userState = getUserStateLocked(mUserId);
// DO NOT remove the client state of clientStateMap in this method. It will be
- // removed in releaseSessionLocked() or unregisterClientInternalLocked().
+ // removed in releaseSessionLocked().
ClientState clientState = userState.clientStateMap.get(mClientToken);
if (clientState != null) {
while (clientState.mSessionTokens.size() > 0) {
releaseSessionLocked(
clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
}
- while (clientState.mInputIds.size() > 0) {
- unregisterClientInternalLocked(
- mClientToken, clientState.mInputIds.get(0), mUserId);
- }
}
mClientToken = null;
}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index d2044be..aa6c47c 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -149,10 +149,21 @@
*/
public static final int EXITED_ECM = 42;
+ /**
+ * The outgoing call failed with an unknown cause.
+ */
+ public static final int OUTGOING_FAILURE = 43;
+
+ /**
+ * The outgoing call was canceled by the {@link android.telecomm.ConnectionService}.
+ */
+ public static final int OUTGOING_CANCELED = 44;
+
/** Smallest valid value for call disconnect codes. */
public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
+
/** Largest valid value for call disconnect codes. */
- public static final int MAXIMUM_VALID_VALUE = EXITED_ECM;
+ public static final int MAXIMUM_VALID_VALUE = OUTGOING_CANCELED;
/** Private constructor to avoid class instantiation. */
private DisconnectCause() {
@@ -246,6 +257,10 @@
return "EXITED_ECM";
case ERROR_UNSPECIFIED:
return "ERROR_UNSPECIFIED";
+ case OUTGOING_FAILURE:
+ return "OUTGOING_FAILURE";
+ case OUTGOING_CANCELED:
+ return "OUTGOING_CANCELED";
default:
return "INVALID";
}