Loading services/core/java/com/android/server/hdmi/HdmiCecController.java +116 −20 Original line number Diff line number Diff line Loading @@ -20,9 +20,12 @@ import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; import android.util.Slog; import android.util.SparseArray; import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; import libcore.util.EmptyArray; import java.util.ArrayList; Loading @@ -34,6 +37,9 @@ import java.util.List; * and pass it to CEC HAL so that it sends message to other device. For incoming * message it translates the message and delegates it to proper module. * * <p>It should be careful to access member variables on IO thread because * it can be accessed from system thread as well. * * <p>It can be created only by {@link HdmiCecController#create} * * <p>Declared as package-private, accessed by {@link HdmiControlService} only. Loading @@ -55,6 +61,8 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3; // Handler instance to process synchronous I/O (mainly send) message. private Handler mIoHandler; Loading @@ -64,14 +72,13 @@ final class HdmiCecController { // Stores the pointer to the native implementation of the service that // interacts with HAL. private long mNativePtr; private volatile long mNativePtr; private HdmiControlService mService; // Map-like container of all cec devices. A logical address of device is // used as key of container. private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<HdmiCecDeviceInfo>(); private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>(); // Stores the local CEC devices in the system. private final ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); Loading Loading @@ -115,6 +122,7 @@ final class HdmiCecController { * @param deviceTypes array of device types */ void initializeLocalDevices(int[] deviceTypes) { assertRunOnServiceThread(); for (int type : deviceTypes) { HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type); if (device == null) { Loading Loading @@ -158,6 +166,8 @@ final class HdmiCecController { */ void allocateLogicalAddress(final int deviceType, final int preferredAddress, final AllocateLogicalAddressCallback callback) { assertRunOnServiceThread(); runOnIoThread(new Runnable() { @Override public void run() { Loading @@ -168,6 +178,7 @@ final class HdmiCecController { private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress, final AllocateLogicalAddressCallback callback) { assertRunOnIoThread(); int startAddress = preferredAddress; // If preferred address is "unregistered", start address will be the smallest // address matched with the given device type. Loading @@ -186,14 +197,7 @@ final class HdmiCecController { int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; if (curAddress != HdmiCec.ADDR_UNREGISTERED && deviceType == HdmiCec.getTypeFromAddress(i)) { // <Polling Message> is a message which has empty body and // uses same address for both source and destination address. // If sending <Polling Message> failed (NAK), it becomes // new logical address for the device because no device uses // it as logical address of the device. int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress, EMPTY_BODY); if (error != HdmiControlService.SEND_RESULT_SUCCESS) { if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) { logicalAddress = curAddress; break; } Loading Loading @@ -229,6 +233,7 @@ final class HdmiCecController { * that has the same logical address as new one has. */ HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { assertRunOnServiceThread(); HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress()); if (oldDeviceInfo != null) { removeDeviceInfo(deviceInfo.getLogicalAddress()); Loading @@ -247,6 +252,7 @@ final class HdmiCecController { * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null} */ HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress); if (deviceInfo != null) { mDeviceInfos.remove(logicalAddress); Loading @@ -255,13 +261,15 @@ final class HdmiCecController { } /** * Return a list of all {@HdmiCecDeviceInfo}. * Return a list of all {@link HdmiCecDeviceInfo}. * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ // TODO: put local devices to this list. List<HdmiCecDeviceInfo> getDeviceInfoList() { List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>( mDeviceInfos.size()); assertRunOnServiceThread(); List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<>(mDeviceInfos.size()); for (int i = 0; i < mDeviceInfos.size(); ++i) { deviceInfoList.add(mDeviceInfos.valueAt(i)); } Loading @@ -278,6 +286,7 @@ final class HdmiCecController { * Returns null if no logical address matched */ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); return mDeviceInfos.get(logicalAddress); } Loading @@ -292,6 +301,7 @@ final class HdmiCecController { * @return 0 on success. Otherwise, returns negative value */ int addLogicalAddress(int newLogicalAddress) { assertRunOnServiceThread(); if (HdmiCec.isValidAddress(newLogicalAddress)) { return nativeAddLogicalAddress(mNativePtr, newLogicalAddress); } else { Loading @@ -305,6 +315,7 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ void clearLogicalAddress() { assertRunOnServiceThread(); // TODO: consider to backup logical address so that new logical address // allocation can use it as preferred address. for (HdmiCecLocalDevice device : mLocalDevices) { Loading @@ -322,6 +333,7 @@ final class HdmiCecController { * is between 0x0000 and 0xFFFF. If failed it returns -1 */ int getPhysicalAddress() { assertRunOnServiceThread(); return nativeGetPhysicalAddress(mNativePtr); } Loading @@ -331,6 +343,7 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVersion() { assertRunOnServiceThread(); return nativeGetVersion(mNativePtr); } Loading @@ -340,9 +353,96 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVendorId() { assertRunOnServiceThread(); return nativeGetVendorId(mNativePtr); } /** * Poll all remote devices. It sends <Polling Message> to all remote * devices. * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. * * @param callback an interface used to get a list of all remote devices' address * @param retryCount the number of retry used to send polling message to remote devices */ void pollDevices(DevicePollingCallback callback, int retryCount) { assertRunOnServiceThread(); // Extract polling candidates. No need to poll against local devices. ArrayList<Integer> pollingCandidates = new ArrayList<>(); for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) { if (!isAllocatedLocalDeviceAddress(i)) { pollingCandidates.add(i); } } runDevicePolling(pollingCandidates, retryCount, callback); } private boolean isAllocatedLocalDeviceAddress(int address) { for (HdmiCecLocalDevice device : mLocalDevices) { if (device.isAddressOf(address)) { return true; } } return false; } private void runDevicePolling(final List<Integer> candidates, final int retryCount, final DevicePollingCallback callback) { assertRunOnServiceThread(); runOnIoThread(new Runnable() { @Override public void run() { final ArrayList<Integer> allocated = new ArrayList<>(); for (Integer address : candidates) { if (sendPollMessage(address, retryCount)) { allocated.add(address); } } if (callback != null) { runOnServiceThread(new Runnable() { @Override public void run() { callback.onPollingFinished(allocated); } }); } } }); } private boolean sendPollMessage(int address, int retryCount) { assertRunOnIoThread(); for (int i = 0; i < retryCount; ++i) { // <Polling Message> is a message which has empty body and // uses same address for both source and destination address. // If sending <Polling Message> failed (NAK), it becomes // new logical address for the device because no device uses // it as logical address of the device. if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY) == HdmiControlService.SEND_RESULT_SUCCESS) { return true; } } return false; } private void assertRunOnIoThread() { if (Looper.myLooper() != mIoHandler.getLooper()) { throw new IllegalStateException("Should run on io thread."); } } private void assertRunOnServiceThread() { if (Looper.myLooper() != mControlHandler.getLooper()) { throw new IllegalStateException("Should run on service thread."); } } // Run a Runnable on IO thread. // It should be careful to access member variables on IO thread because // it can be accessed from system thread as well. private void runOnIoThread(Runnable runnable) { mIoHandler.post(runnable); } Loading @@ -356,15 +456,11 @@ final class HdmiCecController { if (address == HdmiCec.ADDR_BROADCAST) { return true; } for (HdmiCecLocalDevice device : mLocalDevices) { if (device.isAddressOf(address)) { return true; } } return false; return isAllocatedLocalDeviceAddress(address); } private void onReceiveCommand(HdmiCecMessage message) { assertRunOnServiceThread(); if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) { return; Loading services/core/java/com/android/server/hdmi/HdmiControlService.java +24 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.server.SystemService; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; /** Loading Loading @@ -68,6 +69,18 @@ public final class HdmiControlService extends SystemService { void onSendCompleted(int error); } /** * Interface to get a list of available logical devices. */ interface DevicePollingCallback { /** * Called when device polling is finished. * * @param ackedAddress a list of logical addresses of available devices */ void onPollingFinished(List<Integer> ackedAddress); } // A thread to handle synchronous IO of CEC and MHL control service. // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms) // and sparse call it shares a thread to handle IO operations. Loading Loading @@ -281,6 +294,17 @@ public final class HdmiControlService extends SystemService { // TODO: Start "RequestArcInitiationAction" if ARC port. } /** * Poll all remote devices. It sends <Polling Message> to all remote * devices. * * @param callback an interface used to get a list of all remote devices' address * @param retryCount the number of retry used to send polling message to remote devices */ void pollDevices(DevicePollingCallback callback, int retryCount) { mCecController.pollDevices(callback, retryCount); } private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. Loading Loading
services/core/java/com/android/server/hdmi/HdmiCecController.java +116 −20 Original line number Diff line number Diff line Loading @@ -20,9 +20,12 @@ import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; import android.util.Slog; import android.util.SparseArray; import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; import libcore.util.EmptyArray; import java.util.ArrayList; Loading @@ -34,6 +37,9 @@ import java.util.List; * and pass it to CEC HAL so that it sends message to other device. For incoming * message it translates the message and delegates it to proper module. * * <p>It should be careful to access member variables on IO thread because * it can be accessed from system thread as well. * * <p>It can be created only by {@link HdmiCecController#create} * * <p>Declared as package-private, accessed by {@link HdmiControlService} only. Loading @@ -55,6 +61,8 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3; // Handler instance to process synchronous I/O (mainly send) message. private Handler mIoHandler; Loading @@ -64,14 +72,13 @@ final class HdmiCecController { // Stores the pointer to the native implementation of the service that // interacts with HAL. private long mNativePtr; private volatile long mNativePtr; private HdmiControlService mService; // Map-like container of all cec devices. A logical address of device is // used as key of container. private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<HdmiCecDeviceInfo>(); private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>(); // Stores the local CEC devices in the system. private final ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); Loading Loading @@ -115,6 +122,7 @@ final class HdmiCecController { * @param deviceTypes array of device types */ void initializeLocalDevices(int[] deviceTypes) { assertRunOnServiceThread(); for (int type : deviceTypes) { HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type); if (device == null) { Loading Loading @@ -158,6 +166,8 @@ final class HdmiCecController { */ void allocateLogicalAddress(final int deviceType, final int preferredAddress, final AllocateLogicalAddressCallback callback) { assertRunOnServiceThread(); runOnIoThread(new Runnable() { @Override public void run() { Loading @@ -168,6 +178,7 @@ final class HdmiCecController { private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress, final AllocateLogicalAddressCallback callback) { assertRunOnIoThread(); int startAddress = preferredAddress; // If preferred address is "unregistered", start address will be the smallest // address matched with the given device type. Loading @@ -186,14 +197,7 @@ final class HdmiCecController { int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; if (curAddress != HdmiCec.ADDR_UNREGISTERED && deviceType == HdmiCec.getTypeFromAddress(i)) { // <Polling Message> is a message which has empty body and // uses same address for both source and destination address. // If sending <Polling Message> failed (NAK), it becomes // new logical address for the device because no device uses // it as logical address of the device. int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress, EMPTY_BODY); if (error != HdmiControlService.SEND_RESULT_SUCCESS) { if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) { logicalAddress = curAddress; break; } Loading Loading @@ -229,6 +233,7 @@ final class HdmiCecController { * that has the same logical address as new one has. */ HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { assertRunOnServiceThread(); HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress()); if (oldDeviceInfo != null) { removeDeviceInfo(deviceInfo.getLogicalAddress()); Loading @@ -247,6 +252,7 @@ final class HdmiCecController { * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null} */ HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress); if (deviceInfo != null) { mDeviceInfos.remove(logicalAddress); Loading @@ -255,13 +261,15 @@ final class HdmiCecController { } /** * Return a list of all {@HdmiCecDeviceInfo}. * Return a list of all {@link HdmiCecDeviceInfo}. * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ // TODO: put local devices to this list. List<HdmiCecDeviceInfo> getDeviceInfoList() { List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>( mDeviceInfos.size()); assertRunOnServiceThread(); List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<>(mDeviceInfos.size()); for (int i = 0; i < mDeviceInfos.size(); ++i) { deviceInfoList.add(mDeviceInfos.valueAt(i)); } Loading @@ -278,6 +286,7 @@ final class HdmiCecController { * Returns null if no logical address matched */ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); return mDeviceInfos.get(logicalAddress); } Loading @@ -292,6 +301,7 @@ final class HdmiCecController { * @return 0 on success. Otherwise, returns negative value */ int addLogicalAddress(int newLogicalAddress) { assertRunOnServiceThread(); if (HdmiCec.isValidAddress(newLogicalAddress)) { return nativeAddLogicalAddress(mNativePtr, newLogicalAddress); } else { Loading @@ -305,6 +315,7 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ void clearLogicalAddress() { assertRunOnServiceThread(); // TODO: consider to backup logical address so that new logical address // allocation can use it as preferred address. for (HdmiCecLocalDevice device : mLocalDevices) { Loading @@ -322,6 +333,7 @@ final class HdmiCecController { * is between 0x0000 and 0xFFFF. If failed it returns -1 */ int getPhysicalAddress() { assertRunOnServiceThread(); return nativeGetPhysicalAddress(mNativePtr); } Loading @@ -331,6 +343,7 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVersion() { assertRunOnServiceThread(); return nativeGetVersion(mNativePtr); } Loading @@ -340,9 +353,96 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVendorId() { assertRunOnServiceThread(); return nativeGetVendorId(mNativePtr); } /** * Poll all remote devices. It sends <Polling Message> to all remote * devices. * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. * * @param callback an interface used to get a list of all remote devices' address * @param retryCount the number of retry used to send polling message to remote devices */ void pollDevices(DevicePollingCallback callback, int retryCount) { assertRunOnServiceThread(); // Extract polling candidates. No need to poll against local devices. ArrayList<Integer> pollingCandidates = new ArrayList<>(); for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) { if (!isAllocatedLocalDeviceAddress(i)) { pollingCandidates.add(i); } } runDevicePolling(pollingCandidates, retryCount, callback); } private boolean isAllocatedLocalDeviceAddress(int address) { for (HdmiCecLocalDevice device : mLocalDevices) { if (device.isAddressOf(address)) { return true; } } return false; } private void runDevicePolling(final List<Integer> candidates, final int retryCount, final DevicePollingCallback callback) { assertRunOnServiceThread(); runOnIoThread(new Runnable() { @Override public void run() { final ArrayList<Integer> allocated = new ArrayList<>(); for (Integer address : candidates) { if (sendPollMessage(address, retryCount)) { allocated.add(address); } } if (callback != null) { runOnServiceThread(new Runnable() { @Override public void run() { callback.onPollingFinished(allocated); } }); } } }); } private boolean sendPollMessage(int address, int retryCount) { assertRunOnIoThread(); for (int i = 0; i < retryCount; ++i) { // <Polling Message> is a message which has empty body and // uses same address for both source and destination address. // If sending <Polling Message> failed (NAK), it becomes // new logical address for the device because no device uses // it as logical address of the device. if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY) == HdmiControlService.SEND_RESULT_SUCCESS) { return true; } } return false; } private void assertRunOnIoThread() { if (Looper.myLooper() != mIoHandler.getLooper()) { throw new IllegalStateException("Should run on io thread."); } } private void assertRunOnServiceThread() { if (Looper.myLooper() != mControlHandler.getLooper()) { throw new IllegalStateException("Should run on service thread."); } } // Run a Runnable on IO thread. // It should be careful to access member variables on IO thread because // it can be accessed from system thread as well. private void runOnIoThread(Runnable runnable) { mIoHandler.post(runnable); } Loading @@ -356,15 +456,11 @@ final class HdmiCecController { if (address == HdmiCec.ADDR_BROADCAST) { return true; } for (HdmiCecLocalDevice device : mLocalDevices) { if (device.isAddressOf(address)) { return true; } } return false; return isAllocatedLocalDeviceAddress(address); } private void onReceiveCommand(HdmiCecMessage message) { assertRunOnServiceThread(); if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) { return; Loading
services/core/java/com/android/server/hdmi/HdmiControlService.java +24 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.server.SystemService; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; /** Loading Loading @@ -68,6 +69,18 @@ public final class HdmiControlService extends SystemService { void onSendCompleted(int error); } /** * Interface to get a list of available logical devices. */ interface DevicePollingCallback { /** * Called when device polling is finished. * * @param ackedAddress a list of logical addresses of available devices */ void onPollingFinished(List<Integer> ackedAddress); } // A thread to handle synchronous IO of CEC and MHL control service. // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms) // and sparse call it shares a thread to handle IO operations. Loading Loading @@ -281,6 +294,17 @@ public final class HdmiControlService extends SystemService { // TODO: Start "RequestArcInitiationAction" if ARC port. } /** * Poll all remote devices. It sends <Polling Message> to all remote * devices. * * @param callback an interface used to get a list of all remote devices' address * @param retryCount the number of retry used to send polling message to remote devices */ void pollDevices(DevicePollingCallback callback, int retryCount) { mCecController.pollDevices(callback, retryCount); } private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. Loading