1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.hdmi; 18 19 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_ARC_PENDING; 20 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_CONNECTED; 21 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_PENDING; 22 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_IDLE; 23 24 import android.stats.hdmi.HdmiStatsEnums; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.util.FrameworkStatsLog; 28 29 /** 30 * Provides methods for writing HDMI-CEC statsd atoms. 31 */ 32 @VisibleForTesting 33 public class HdmiCecAtomWriter { 34 35 @VisibleForTesting 36 protected static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100; 37 private static final int ERROR_CODE_UNKNOWN = -1; 38 39 /** 40 * Writes a HdmiCecMessageReported atom representing an HDMI CEC message. 41 * Should only be directly used for sent messages; for received messages, 42 * use the overloaded version with the errorCode argument omitted. 43 * 44 * @param message The HDMI CEC message 45 * @param direction Whether the message is incoming, outgoing, or neither 46 * @param errorCode The error code from the final attempt to send the message 47 * @param callingUid The calling uid of the app that triggered this message 48 */ messageReported( HdmiCecMessage message, int direction, int callingUid, int errorCode)49 public void messageReported( 50 HdmiCecMessage message, int direction, int callingUid, int errorCode) { 51 MessageReportedGenericArgs genericArgs = createMessageReportedGenericArgs( 52 message, direction, errorCode, callingUid); 53 MessageReportedSpecialArgs specialArgs = createMessageReportedSpecialArgs(message); 54 messageReportedBase(genericArgs, specialArgs); 55 } 56 57 /** 58 * Version of messageReported for received messages, where no error code is present. 59 * 60 * @param message The HDMI CEC message 61 * @param direction Whether the message is incoming, outgoing, or neither 62 * @param callingUid The calling uid of the app that triggered this message 63 */ messageReported(HdmiCecMessage message, int direction, int callingUid)64 public void messageReported(HdmiCecMessage message, int direction, int callingUid) { 65 messageReported(message, direction, callingUid, ERROR_CODE_UNKNOWN); 66 } 67 68 /** 69 * Constructs the generic arguments for logging a HDMI CEC message. 70 * 71 * @param message The HDMI CEC message 72 * @param direction Whether the message is incoming, outgoing, or neither 73 * @param errorCode The error code of the message if it's outgoing; 74 * otherwise, ERROR_CODE_UNKNOWN 75 */ createMessageReportedGenericArgs( HdmiCecMessage message, int direction, int errorCode, int callingUid)76 private MessageReportedGenericArgs createMessageReportedGenericArgs( 77 HdmiCecMessage message, int direction, int errorCode, int callingUid) { 78 int sendMessageResult = errorCode == ERROR_CODE_UNKNOWN 79 ? HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN 80 : errorCode + 10; 81 return new MessageReportedGenericArgs(callingUid, direction, message.getSource(), 82 message.getDestination(), message.getOpcode(), sendMessageResult); 83 } 84 85 /** 86 * Constructs the special arguments for logging an HDMI CEC message. 87 * 88 * @param message The HDMI CEC message to log 89 * @return An object containing the special arguments for the message 90 */ createMessageReportedSpecialArgs(HdmiCecMessage message)91 private MessageReportedSpecialArgs createMessageReportedSpecialArgs(HdmiCecMessage message) { 92 // Special arguments depend on message opcode 93 switch (message.getOpcode()) { 94 case Constants.MESSAGE_USER_CONTROL_PRESSED: 95 return createUserControlPressedSpecialArgs(message); 96 case Constants.MESSAGE_FEATURE_ABORT: 97 return createFeatureAbortSpecialArgs(message); 98 default: 99 return new MessageReportedSpecialArgs(); 100 } 101 } 102 103 /** 104 * Constructs the special arguments for a <User Control Pressed> message. 105 * 106 * @param message The HDMI CEC message to log 107 */ createUserControlPressedSpecialArgs( HdmiCecMessage message)108 private MessageReportedSpecialArgs createUserControlPressedSpecialArgs( 109 HdmiCecMessage message) { 110 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 111 112 if (message.getParams().length > 0) { 113 int keycode = message.getParams()[0]; 114 if (keycode >= 0x1E && keycode <= 0x29) { 115 specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER; 116 } else { 117 specialArgs.mUserControlPressedCommand = keycode + 0x100; 118 } 119 } 120 121 return specialArgs; 122 } 123 124 /** 125 * Constructs the special arguments for a <Feature Abort> message. 126 * 127 * @param message The HDMI CEC message to log 128 */ createFeatureAbortSpecialArgs(HdmiCecMessage message)129 private MessageReportedSpecialArgs createFeatureAbortSpecialArgs(HdmiCecMessage message) { 130 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 131 132 if (message.getParams().length > 0) { 133 specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte 134 if (message.getParams().length > 1) { 135 specialArgs.mFeatureAbortReason = message.getParams()[1] + 10; 136 } 137 } 138 139 return specialArgs; 140 } 141 142 /** 143 * Writes a HdmiCecMessageReported atom. 144 * 145 * @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms 146 * @param specialArgs Special arguments; depends on the opcode of the message 147 */ messageReportedBase(MessageReportedGenericArgs genericArgs, MessageReportedSpecialArgs specialArgs)148 private void messageReportedBase(MessageReportedGenericArgs genericArgs, 149 MessageReportedSpecialArgs specialArgs) { 150 writeHdmiCecMessageReportedAtom( 151 genericArgs.mUid, 152 genericArgs.mDirection, 153 genericArgs.mInitiatorLogicalAddress, 154 genericArgs.mDestinationLogicalAddress, 155 genericArgs.mOpcode, 156 genericArgs.mSendMessageResult, 157 specialArgs.mUserControlPressedCommand, 158 specialArgs.mFeatureAbortOpcode, 159 specialArgs.mFeatureAbortReason); 160 } 161 162 /** 163 * Writes a HdmiCecMessageReported atom representing an incoming or outgoing HDMI-CEC message. 164 */ 165 @VisibleForTesting writeHdmiCecMessageReportedAtom(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, int featureAbortReason)166 protected void writeHdmiCecMessageReportedAtom(int uid, int direction, 167 int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, 168 int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, 169 int featureAbortReason) { 170 FrameworkStatsLog.write( 171 FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED, 172 uid, 173 direction, 174 initiatorLogicalAddress, 175 destinationLogicalAddress, 176 opcode, 177 sendMessageResult, 178 userControlPressedCommand, 179 featureAbortOpcode, 180 featureAbortReason); 181 } 182 183 /** 184 * Writes a HdmiCecActiveSourceChanged atom representing a change in the active source. 185 * 186 * @param logicalAddress The Logical Address of the new active source 187 * @param physicalAddress The Physical Address of the new active source 188 * @param relationshipToActiveSource The relationship between this device and the active source 189 */ activeSourceChanged(int logicalAddress, int physicalAddress, @Constants.PathRelationship int relationshipToActiveSource)190 public void activeSourceChanged(int logicalAddress, int physicalAddress, 191 @Constants.PathRelationship int relationshipToActiveSource) { 192 FrameworkStatsLog.write( 193 FrameworkStatsLog.HDMI_CEC_ACTIVE_SOURCE_CHANGED, 194 logicalAddress, 195 physicalAddress, 196 relationshipToActiveSource 197 ); 198 } 199 200 /** 201 * Writes a HdmiEarcStatusReported atom representing a eARC status change. 202 * @param isSupported Whether the hardware supports eARC. 203 * @param isEnabled Whether eARC is enabled. 204 * @param oldConnectionState If enumLogReason == HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED, 205 * the state just before the change. Otherwise, the current state. 206 * @param newConnectionState If enumLogReason == HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED, 207 * the state just after the change. Otherwise, the current state. 208 * @param enumLogReason The event that triggered the log. 209 */ earcStatusChanged(boolean isSupported, boolean isEnabled, int oldConnectionState, int newConnectionState, int enumLogReason)210 public void earcStatusChanged(boolean isSupported, boolean isEnabled, int oldConnectionState, 211 int newConnectionState, int enumLogReason) { 212 int enumOldConnectionState = earcStateToEnum(oldConnectionState); 213 int enumNewConnectionState = earcStateToEnum(newConnectionState); 214 215 FrameworkStatsLog.write( 216 FrameworkStatsLog.HDMI_EARC_STATUS_REPORTED, 217 isSupported, 218 isEnabled, 219 enumOldConnectionState, 220 enumNewConnectionState, 221 enumLogReason 222 ); 223 } 224 225 /** 226 * Writes a HdmiSoundbarModeStatusReported atom representing a Dynamic soundbar mode status 227 * change. 228 * @param isSupported Whether the hardware supports ARC. 229 * @param isEnabled Whether DSM is enabled. 230 * @param enumLogReason The event that triggered the log. 231 */ dsmStatusChanged(boolean isSupported, boolean isEnabled, int enumLogReason)232 public void dsmStatusChanged(boolean isSupported, boolean isEnabled, int enumLogReason) { 233 FrameworkStatsLog.write( 234 FrameworkStatsLog.HDMI_SOUNDBAR_MODE_STATUS_REPORTED, 235 isSupported, 236 isEnabled, 237 enumLogReason); 238 } 239 earcStateToEnum(int earcState)240 private int earcStateToEnum(int earcState) { 241 switch (earcState) { 242 case HDMI_EARC_STATUS_IDLE: 243 return HdmiStatsEnums.HDMI_EARC_STATUS_IDLE; 244 case HDMI_EARC_STATUS_EARC_PENDING: 245 return HdmiStatsEnums.HDMI_EARC_STATUS_EARC_PENDING; 246 case HDMI_EARC_STATUS_ARC_PENDING: 247 return HdmiStatsEnums.HDMI_EARC_STATUS_ARC_PENDING; 248 case HDMI_EARC_STATUS_EARC_CONNECTED: 249 return HdmiStatsEnums.HDMI_EARC_STATUS_EARC_CONNECTED; 250 default: 251 return HdmiStatsEnums.HDMI_EARC_STATUS_UNKNOWN; 252 } 253 } 254 255 /** 256 * Contains the required arguments for creating any HdmiCecMessageReported atom 257 */ 258 private class MessageReportedGenericArgs { 259 final int mUid; 260 final int mDirection; 261 final int mInitiatorLogicalAddress; 262 final int mDestinationLogicalAddress; 263 final int mOpcode; 264 final int mSendMessageResult; 265 MessageReportedGenericArgs(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult)266 MessageReportedGenericArgs(int uid, int direction, int initiatorLogicalAddress, 267 int destinationLogicalAddress, int opcode, int sendMessageResult) { 268 this.mUid = uid; 269 this.mDirection = direction; 270 this.mInitiatorLogicalAddress = initiatorLogicalAddress; 271 this.mDestinationLogicalAddress = destinationLogicalAddress; 272 this.mOpcode = opcode; 273 this.mSendMessageResult = sendMessageResult; 274 } 275 } 276 277 /** 278 * Contains the opcode-dependent arguments for creating a HdmiCecMessageReported atom. Each 279 * field is initialized to a null-like value by default. Therefore, a freshly constructed 280 * instance of this object represents a HDMI CEC message whose type does not require any 281 * additional arguments. 282 */ 283 private class MessageReportedSpecialArgs { 284 int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN; 285 int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN; 286 int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN; 287 } 288 } 289