diff options
author | Jungshik Jang <jayjang@google.com> | 2014-08-25 15:37:20 +0900 |
---|---|---|
committer | Jungshik Jang <jayjang@google.com> | 2014-08-26 14:13:57 +0900 |
commit | 339227da7cf025ce4ae0c85ddc52643d63972321 (patch) | |
tree | 2e549ca941b95a21fdd4ddcb0a97043808963d42 /services/core/java/com/android/server/hdmi | |
parent | 93b18bda1bf3a2d4c48b9f648ed00083c2cabfca (diff) | |
download | frameworks_base-339227da7cf025ce4ae0c85ddc52643d63972321.zip frameworks_base-339227da7cf025ce4ae0c85ddc52643d63972321.tar.gz frameworks_base-339227da7cf025ce4ae0c85ddc52643d63972321.tar.bz2 |
Fix several bugs in HdmiControlService.
1. Fix HdmniLogger null pointer exception
2. Should check arc enabled port for arc requests
3. Disallow ARC action coming from indirect AVR device.
4. Check original opcode of feature action
5. Add bitmasking to all parameters of cec message.
Bug: 17243701, Bug: 17238394, Bug: 17241401
Change-Id: Iff0da78b0de9a29fb00e683c261528e0baea66af
Diffstat (limited to 'services/core/java/com/android/server/hdmi')
9 files changed, 125 insertions, 70 deletions
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 935714c..10a47d8 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -22,6 +22,7 @@ import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_FAIL_T 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.OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE; @@ -29,7 +30,6 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; -import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiRecordSources; @@ -39,7 +39,6 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.os.RemoteException; import android.os.SystemProperties; -import android.os.UserHandle; import android.provider.Settings.Global; import android.util.ArraySet; import android.util.Slog; @@ -57,7 +56,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Locale; /** * Represent a logical device of type TV residing in Android system. @@ -70,8 +68,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly private boolean mArcEstablished = false; - // Whether ARC feature is enabled or not. - private boolean mArcFeatureEnabled = false; + // Whether ARC feature is enabled or not. The default value is true. + // TODO: once adding system setting for it, read the value to it. + private boolean mArcFeatureEnabled = true; // Whether System audio mode is activated or not. // This becomes true only when all system audio sequences are finished. @@ -642,19 +641,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // If there is AVR, initiate System Audio Auto initiation action, // which turns on and off system audio according to last system // audio setting. - if (mSystemAudioActivated && getAvrDeviceInfo() != null) { - addAndStartAction(new SystemAudioAutoInitiationAction( - HdmiCecLocalDeviceTv.this, - getAvrDeviceInfo().getLogicalAddress())); - if (mArcEstablished) { - startArcAction(true); - } + HdmiDeviceInfo avr = getAvrDeviceInfo(); + if (avr != null) { + onNewAvrAdded(avr); } } }); addAndStartAction(action); } + @ServiceThreadOnly + void onNewAvrAdded(HdmiDeviceInfo avr) { + assertRunOnServiceThread(); + if (getSystemAudioModeSetting()) { + addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); + } + if (isArcFeatureEnabled()) { + startArcAction(true); + } + } + // Clear all device info. @ServiceThreadOnly private void clearDeviceInfoList() { @@ -757,6 +763,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); if (mArcFeatureEnabled != enabled) { + mArcFeatureEnabled = enabled; if (enabled) { if (!mArcEstablished) { startArcAction(true); @@ -766,7 +773,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { startArcAction(false); } } - mArcFeatureEnabled = enabled; } } @@ -777,13 +783,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } @ServiceThreadOnly - private void startArcAction(boolean enabled) { + void startArcAction(boolean enabled) { assertRunOnServiceThread(); HdmiDeviceInfo info = getAvrDeviceInfo(); if (info == null) { + Slog.w(TAG, "Failed to start arc action; No AVR device."); return; } - if (!isConnectedToArcPort(info.getPhysicalAddress())) { + if (!canStartArcUpdateAction(info.getLogicalAddress(), enabled)) { + Slog.w(TAG, "Failed to start arc action; ARC configuration check failed."); + if (enabled && !isConnectedToArcPort(info.getPhysicalAddress())) { + displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); + } return; } @@ -801,6 +812,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } + private boolean isDirectConnectAddress(int physicalAddress) { + return (physicalAddress & Constants.ROUTING_PATH_TOP_MASK) == physicalAddress; + } + void setAudioStatus(boolean mute, int volume) { synchronized (mLock) { mSystemAudioMute = mute; @@ -857,6 +872,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleInitiateArc(HdmiCecMessage message) { assertRunOnServiceThread(); + + if (!canStartArcUpdateAction(message.getSource(), true)) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); + if (!isConnectedToArcPort(message.getSource())) { + displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); + } + return true; + } + // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. removeAction(RequestArcInitiationAction.class); @@ -866,10 +890,29 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return true; } + private boolean canStartArcUpdateAction(int avrAddress, boolean shouldCheckArcFeatureEnabled) { + HdmiDeviceInfo avr = getAvrDeviceInfo(); + if (avr != null + && (avrAddress == avr.getLogicalAddress()) + && isConnectedToArcPort(avr.getPhysicalAddress()) + && isDirectConnectAddress(avr.getPhysicalAddress())) { + if (shouldCheckArcFeatureEnabled) { + return isArcFeatureEnabled(); + } else { + return true; + } + } else { + return false; + } + } + @Override @ServiceThreadOnly protected boolean handleTerminateArc(HdmiCecMessage message) { assertRunOnServiceThread(); + // In cast of termination, do not check ARC configuration in that AVR device + // might be removed already. + // In case where <Terminate Arc> is started by <Request ARC Termination> // need to clean up RequestArcInitiationAction. removeAction(RequestArcTerminationAction.class); @@ -1000,7 +1043,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * Return a list of all {@link HdmiDeviceInfo}. * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. - * This is not thread-safe. For thread safety, call {@link #getSafeExternalInputs} which + * This is not thread-safe. For thread safety, call {@link #getSafeExternalInputsLocked} which * does not include local device. */ @ServiceThreadOnly @@ -1116,7 +1159,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * * This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}. * - * @param address logical address of the device to be retrieved + * @param logicalAddress logical address of the device to be retrieved * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}. * Returns null if no logical address matched */ @@ -1388,11 +1431,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return mService.isPowerStandbyOrTransient(); } + @ServiceThreadOnly void displayOsd(int messageId) { - Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); - intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); - mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL, - HdmiControlService.PERMISSION); + assertRunOnServiceThread(); + mService.displayOsd(messageId); } // Seq #54 and #55 diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index b53cd45..9a51e3c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -54,8 +54,8 @@ public class HdmiCecMessageBuilder { static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode, int reason) { byte[] params = new byte[] { - (byte) originalOpcode, - (byte) reason, + (byte) (originalOpcode & 0xFF), + (byte) (reason & 0xFF), }; return buildCommand(src, dest, Constants.MESSAGE_FEATURE_ABORT, params); } @@ -110,9 +110,9 @@ public class HdmiCecMessageBuilder { // Hdmi CEC uses lower-cased ISO 639-2 (3 letters code). String normalized = language.toLowerCase(); byte[] params = new byte[] { - (byte) normalized.charAt(0), - (byte) normalized.charAt(1), - (byte) normalized.charAt(2), + (byte) (normalized.charAt(0) & 0xFF), + (byte) (normalized.charAt(1) & 0xFF), + (byte) (normalized.charAt(2) & 0xFF), }; // <Set Menu Language> is broadcast message. return buildCommand(src, Constants.ADDR_BROADCAST, @@ -155,7 +155,7 @@ public class HdmiCecMessageBuilder { (byte) ((address >> 8) & 0xFF), (byte) (address & 0xFF), // One byte device type - (byte) deviceType + (byte) (deviceType & 0xFF) }; // <Report Physical Address> is broadcast message. return buildCommand(src, Constants.ADDR_BROADCAST, @@ -194,7 +194,7 @@ public class HdmiCecMessageBuilder { */ static HdmiCecMessage buildCecVersion(int src, int dest, int version) { byte[] params = new byte[] { - (byte) version + (byte) (version & 0xFF) }; return buildCommand(src, dest, Constants.MESSAGE_CEC_VERSION, params); } @@ -332,7 +332,7 @@ public class HdmiCecMessageBuilder { */ static HdmiCecMessage buildReportPowerStatus(int src, int dest, int powerStatus) { byte[] param = new byte[] { - (byte) (powerStatus) + (byte) (powerStatus & 0xFF) }; return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param); } @@ -347,7 +347,7 @@ public class HdmiCecMessageBuilder { */ static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) { byte[] param = new byte[] { - (byte) (menuStatus) + (byte) (menuStatus & 0xFF) }; return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param); } @@ -391,7 +391,7 @@ public class HdmiCecMessageBuilder { * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) { - return buildUserControlPressed(src, dest, new byte[] { (byte) uiCommand }); + return buildUserControlPressed(src, dest, new byte[] { (byte) (uiCommand & 0xFF) }); } /** @@ -594,7 +594,7 @@ public class HdmiCecMessageBuilder { private static byte[] physicalAddressToParam(int physicalAddress) { return new byte[] { - (byte) (physicalAddress >> 8), + (byte) ((physicalAddress >> 8) & 0xFF), (byte) (physicalAddress & 0xFF) }; } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 3021285..fcccfc0 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -606,7 +606,7 @@ public final class HdmiControlService extends SystemService { * Whether a device of the specified physical address is connected to ARC enabled port. */ boolean isConnectedToArcPort(int physicalAddress) { - int portId = mPortIdMap.get(physicalAddress); + int portId = pathToPortId(physicalAddress); if (portId != Constants.INVALID_PORT_ID) { return mPortInfoMap.get(portId).isArcSupported(); } @@ -1980,4 +1980,13 @@ public final class HdmiControlService extends SystemService { return mMhlInputChangeEnabled; } } + + @ServiceThreadOnly + void displayOsd(int messageId) { + assertRunOnServiceThread(); + Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); + intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL, + HdmiControlService.PERMISSION); + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java index 93ccfe1..cb72bc1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiLogger.java +++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java @@ -16,6 +16,7 @@ package com.android.server.hdmi; +import android.annotation.Nullable; import android.os.SystemClock; import android.util.Pair; import android.util.Slog; @@ -47,7 +48,7 @@ final class HdmiLogger { long curTime = SystemClock.uptimeMillis(); Pair<Long, Integer> timing = mWarningTimingCache.get(logMessage); if (shouldLogNow(timing, curTime)) { - Slog.w(mTag, buildMessage(logMessage, timing, curTime)); + Slog.w(mTag, buildMessage(logMessage, timing)); mWarningTimingCache.put(logMessage, new Pair<>(curTime, 1)); } else { increaseLogCount(mWarningTimingCache, logMessage); @@ -58,16 +59,16 @@ final class HdmiLogger { long curTime = SystemClock.uptimeMillis(); Pair<Long, Integer> timing = mErrorTimingCache.get(logMessage); if (shouldLogNow(timing, curTime)) { - Slog.e(mTag, buildMessage(logMessage, timing, curTime)); + Slog.e(mTag, buildMessage(logMessage, timing)); mErrorTimingCache.put(logMessage, new Pair<>(curTime, 1)); } else { increaseLogCount(mErrorTimingCache, logMessage); } } - private String buildMessage(String message, Pair<Long, Integer> timing, long curTime) { + private String buildMessage(String message, @Nullable Pair<Long, Integer> timing) { return new StringBuilder() - .append("[").append(timing == null ? curTime : timing.second).append("]:") + .append("[").append(timing == null ? 1 : timing.second).append("]:") .append(message).toString(); } @@ -78,7 +79,7 @@ final class HdmiLogger { } } - private boolean shouldLogNow(Pair<Long, Integer> timing, long curTime) { + private boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) { return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS; } } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index a5fdbea..2074085 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -103,8 +103,8 @@ final class NewDeviceAction extends HdmiCecFeatureAction { requestVendorId(); return true; } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { - int requestOpcode = params[1] & 0xFF; - if (requestOpcode == Constants.MESSAGE_SET_OSD_NAME) { + int requestOpcode = params[0] & 0xFF; + if (requestOpcode == Constants.MESSAGE_GIVE_OSD_NAME) { requestVendorId(); return true; } @@ -116,8 +116,8 @@ final class NewDeviceAction extends HdmiCecFeatureAction { finish(); return true; } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { - int requestOpcode = params[1] & 0xFF; - if (requestOpcode == Constants.MESSAGE_DEVICE_VENDOR_ID) { + int requestOpcode = params[0] & 0xFF; + if (requestOpcode == Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID) { addDeviceInfo(); finish(); return true; @@ -152,30 +152,19 @@ final class NewDeviceAction extends HdmiCecFeatureAction { if (mDisplayName == null) { mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress); } - tv().addCecDevice(new HdmiDeviceInfo( + HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo( mDeviceLogicalAddress, mDevicePhysicalAddress, tv().getPortId(mDevicePhysicalAddress), HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress), - mVendorId, mDisplayName)); + mVendorId, mDisplayName); + tv().addCecDevice(deviceInfo); if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { - if (tv().getSystemAudioModeSetting()) { - addAndStartAction(new SystemAudioAutoInitiationAction(localDevice(), - mDeviceLogicalAddress)); - } - - if (shouldTryArcInitiation()) { - addAndStartAction(new RequestArcInitiationAction(localDevice(), - mDeviceLogicalAddress)); - } + tv().onNewAvrAdded(deviceInfo); } } - private boolean shouldTryArcInitiation() { - return tv().isConnectedToArcPort(mDevicePhysicalAddress) && tv().isArcFeatureEnabled(); - } - @Override public void handleTimerEvent(int state) { if (mState == STATE_NONE || mState != state) { diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java index 9c530a3..17c2d6c 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java @@ -17,7 +17,6 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; - import android.util.Slog; /** @@ -57,11 +56,17 @@ abstract class RequestArcAction extends HdmiCecFeatureAction { switch (opcode) { // Handles only <Feature Abort> here and, both <Initiate ARC> and <Terminate ARC> // are handled in HdmiControlService itself because both can be - // received wihtout <Request ARC Initiation> or <Request ARC Termination>. + // received without <Request ARC Initiation> or <Request ARC Termination>. case Constants.MESSAGE_FEATURE_ABORT: - disableArcTransmission(); - finish(); - return true; + int originalOpcode = cmd.getParams()[0] & 0xFF; + if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION + || originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) { + disableArcTransmission(); + finish(); + return true; + } else { + return false; + } default: Slog.w(TAG, "Unsupported opcode:" + cmd.toString()); } diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java index 9f7f98a..c1c6b91 100644 --- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java +++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java @@ -110,10 +110,14 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction { int opcode = cmd.getOpcode(); if (opcode == Constants.MESSAGE_FEATURE_ABORT) { - setArcStatus(false); + int originalOpcode = cmd.getParams()[0] & 0xFF; + if (originalOpcode == Constants.MESSAGE_REPORT_ARC_INITIATED) { + setArcStatus(false); + finish(); + return true; + } } - finish(); - return true; + return false; } @Override diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java index 7e45a99..ac2c7b9 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -127,7 +127,8 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction { switch (mState) { case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE: if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT - && cmd.getParams()[0] == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) { + && (cmd.getParams()[0] & 0xFF) + == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) { setSystemAudioMode(false); finishWithCallback(HdmiControlManager.RESULT_EXCEPTION); return true; diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java index 01c6f3e..ddc267a 100644 --- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java +++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java @@ -156,10 +156,14 @@ final class VolumeControlAction extends HdmiCecFeatureAction { handleReportAudioStatus(cmd); return true; case Constants.MESSAGE_FEATURE_ABORT: - // TODO: handle feature abort. - finish(); - return true; - default: + int originalOpcode = cmd.getParams()[0] & 0xFF; + if (originalOpcode == Constants.MESSAGE_USER_CONTROL_PRESSED + || originalOpcode == Constants.MESSAGE_USER_CONTROL_RELEASED) { + // TODO: handle feature abort. + finish(); + return true; + } + default: // fall through return false; } } |