diff options
author | Jungshik Jang <jayjang@google.com> | 2014-05-23 16:48:31 +0900 |
---|---|---|
committer | Jungshik Jang <jayjang@google.com> | 2014-06-02 10:48:57 +0900 |
commit | 9d499bfe4a52068fd0c25b3cce34bd5e445e0f96 (patch) | |
tree | f7728d47eda5fb81dc87190bc1ec485342fa95f5 /services | |
parent | 61ced38d61926bc28638d805436086db22b642c3 (diff) | |
download | frameworks_base-9d499bfe4a52068fd0c25b3cce34bd5e445e0f96.zip frameworks_base-9d499bfe4a52068fd0c25b3cce34bd5e445e0f96.tar.gz frameworks_base-9d499bfe4a52068fd0c25b3cce34bd5e445e0f96.tar.bz2 |
DO NOT MERGE: Implement <Polling Message>.
When Device Discovery is launched or Hot-plug detection is
launched, the first step of it is to send <Polling Message>
to all remote devices. According to type of feature,
it may have different retry count for sending <Polling Message>.
As <Polling Message> to all devices should be serialized operation
it runs on io thread as single operation.
Along with this, added assertRunOnIoThread and
assertRunOnServiceThread used to make sure that all methods are
called in proper thread.
Change-Id: I2d2df0216867c188e99ba24b216ec73f3396eeae
Diffstat (limited to 'services')
-rw-r--r-- | services/core/java/com/android/server/hdmi/HdmiCecController.java | 136 | ||||
-rw-r--r-- | services/core/java/com/android/server/hdmi/HdmiControlService.java | 24 |
2 files changed, 140 insertions, 20 deletions
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 7cad906..36e0ed7 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -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; @@ -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. @@ -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; @@ -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<>(); @@ -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) { @@ -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() { @@ -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. @@ -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; } @@ -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()); @@ -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); @@ -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)); } @@ -278,6 +286,7 @@ final class HdmiCecController { * Returns null if no logical address matched */ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { + assertRunOnServiceThread(); return mDeviceInfos.get(logicalAddress); } @@ -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 { @@ -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) { @@ -322,6 +333,7 @@ final class HdmiCecController { * is between 0x0000 and 0xFFFF. If failed it returns -1 */ int getPhysicalAddress() { + assertRunOnServiceThread(); return nativeGetPhysicalAddress(mNativePtr); } @@ -331,6 +343,7 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVersion() { + assertRunOnServiceThread(); return nativeGetVersion(mNativePtr); } @@ -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); } @@ -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; diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2cd3eab..74961fd 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -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; /** @@ -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. @@ -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. |