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 android.stats.hdmi.HdmiStatsEnums; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.internal.util.FrameworkStatsLog; 23 24 /** 25 * Provides methods for writing HDMI-CEC statsd atoms. 26 */ 27 @VisibleForTesting 28 public class HdmiCecAtomWriter { 29 30 @VisibleForTesting 31 protected static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100; 32 private static final int ERROR_CODE_UNKNOWN = -1; 33 34 /** 35 * Writes a HdmiCecMessageReported atom representing an HDMI CEC message. 36 * Should only be directly used for sent messages; for received messages, 37 * use the overloaded version with the errorCode argument omitted. 38 * 39 * @param message The HDMI CEC message 40 * @param direction Whether the message is incoming, outgoing, or neither 41 * @param errorCode The error code from the final attempt to send the message 42 * @param callingUid The calling uid of the app that triggered this message 43 */ messageReported( HdmiCecMessage message, int direction, int callingUid, int errorCode)44 public void messageReported( 45 HdmiCecMessage message, int direction, int callingUid, int errorCode) { 46 MessageReportedGenericArgs genericArgs = createMessageReportedGenericArgs( 47 message, direction, errorCode, callingUid); 48 MessageReportedSpecialArgs specialArgs = createMessageReportedSpecialArgs(message); 49 messageReportedBase(genericArgs, specialArgs); 50 } 51 52 /** 53 * Version of messageReported for received messages, where no error code is present. 54 * 55 * @param message The HDMI CEC message 56 * @param direction Whether the message is incoming, outgoing, or neither 57 * @param callingUid The calling uid of the app that triggered this message 58 */ messageReported(HdmiCecMessage message, int direction, int callingUid)59 public void messageReported(HdmiCecMessage message, int direction, int callingUid) { 60 messageReported(message, direction, callingUid, ERROR_CODE_UNKNOWN); 61 } 62 63 /** 64 * Constructs the generic arguments for logging a HDMI CEC message. 65 * 66 * @param message The HDMI CEC message 67 * @param direction Whether the message is incoming, outgoing, or neither 68 * @param errorCode The error code of the message if it's outgoing; 69 * otherwise, ERROR_CODE_UNKNOWN 70 */ createMessageReportedGenericArgs( HdmiCecMessage message, int direction, int errorCode, int callingUid)71 private MessageReportedGenericArgs createMessageReportedGenericArgs( 72 HdmiCecMessage message, int direction, int errorCode, int callingUid) { 73 int sendMessageResult = errorCode == ERROR_CODE_UNKNOWN 74 ? HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN 75 : errorCode + 10; 76 return new MessageReportedGenericArgs(callingUid, direction, message.getSource(), 77 message.getDestination(), message.getOpcode(), sendMessageResult); 78 } 79 80 /** 81 * Constructs the special arguments for logging an HDMI CEC message. 82 * 83 * @param message The HDMI CEC message to log 84 * @return An object containing the special arguments for the message 85 */ createMessageReportedSpecialArgs(HdmiCecMessage message)86 private MessageReportedSpecialArgs createMessageReportedSpecialArgs(HdmiCecMessage message) { 87 // Special arguments depend on message opcode 88 switch (message.getOpcode()) { 89 case Constants.MESSAGE_USER_CONTROL_PRESSED: 90 return createUserControlPressedSpecialArgs(message); 91 case Constants.MESSAGE_FEATURE_ABORT: 92 return createFeatureAbortSpecialArgs(message); 93 default: 94 return new MessageReportedSpecialArgs(); 95 } 96 } 97 98 /** 99 * Constructs the special arguments for a <User Control Pressed> message. 100 * 101 * @param message The HDMI CEC message to log 102 */ createUserControlPressedSpecialArgs( HdmiCecMessage message)103 private MessageReportedSpecialArgs createUserControlPressedSpecialArgs( 104 HdmiCecMessage message) { 105 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 106 107 if (message.getParams().length > 0) { 108 int keycode = message.getParams()[0]; 109 if (keycode >= 0x1E && keycode <= 0x29) { 110 specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER; 111 } else { 112 specialArgs.mUserControlPressedCommand = keycode + 0x100; 113 } 114 } 115 116 return specialArgs; 117 } 118 119 /** 120 * Constructs the special arguments for a <Feature Abort> message. 121 * 122 * @param message The HDMI CEC message to log 123 */ createFeatureAbortSpecialArgs(HdmiCecMessage message)124 private MessageReportedSpecialArgs createFeatureAbortSpecialArgs(HdmiCecMessage message) { 125 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 126 127 if (message.getParams().length > 0) { 128 specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte 129 if (message.getParams().length > 1) { 130 specialArgs.mFeatureAbortReason = message.getParams()[1] + 10; 131 } 132 } 133 134 return specialArgs; 135 } 136 137 /** 138 * Writes a HdmiCecMessageReported atom. 139 * 140 * @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms 141 * @param specialArgs Special arguments; depends on the opcode of the message 142 */ messageReportedBase(MessageReportedGenericArgs genericArgs, MessageReportedSpecialArgs specialArgs)143 private void messageReportedBase(MessageReportedGenericArgs genericArgs, 144 MessageReportedSpecialArgs specialArgs) { 145 writeHdmiCecMessageReportedAtom( 146 genericArgs.mUid, 147 genericArgs.mDirection, 148 genericArgs.mInitiatorLogicalAddress, 149 genericArgs.mDestinationLogicalAddress, 150 genericArgs.mOpcode, 151 genericArgs.mSendMessageResult, 152 specialArgs.mUserControlPressedCommand, 153 specialArgs.mFeatureAbortOpcode, 154 specialArgs.mFeatureAbortReason); 155 } 156 157 /** 158 * Writes a HdmiCecMessageReported atom representing an incoming or outgoing HDMI-CEC message. 159 */ 160 @VisibleForTesting writeHdmiCecMessageReportedAtom(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, int featureAbortReason)161 protected void writeHdmiCecMessageReportedAtom(int uid, int direction, 162 int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, 163 int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, 164 int featureAbortReason) { 165 FrameworkStatsLog.write( 166 FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED, 167 uid, 168 direction, 169 initiatorLogicalAddress, 170 destinationLogicalAddress, 171 opcode, 172 sendMessageResult, 173 userControlPressedCommand, 174 featureAbortOpcode, 175 featureAbortReason); 176 } 177 178 /** 179 * Writes a HdmiCecActiveSourceChanged atom representing a change in the active source. 180 * 181 * @param logicalAddress The Logical Address of the new active source 182 * @param physicalAddress The Physical Address of the new active source 183 * @param relationshipToActiveSource The relationship between this device and the active source 184 */ activeSourceChanged(int logicalAddress, int physicalAddress, @Constants.PathRelationship int relationshipToActiveSource)185 public void activeSourceChanged(int logicalAddress, int physicalAddress, 186 @Constants.PathRelationship int relationshipToActiveSource) { 187 FrameworkStatsLog.write( 188 FrameworkStatsLog.HDMI_CEC_ACTIVE_SOURCE_CHANGED, 189 logicalAddress, 190 physicalAddress, 191 relationshipToActiveSource 192 ); 193 } 194 195 /** 196 * Contains the required arguments for creating any HdmiCecMessageReported atom 197 */ 198 private class MessageReportedGenericArgs { 199 final int mUid; 200 final int mDirection; 201 final int mInitiatorLogicalAddress; 202 final int mDestinationLogicalAddress; 203 final int mOpcode; 204 final int mSendMessageResult; 205 MessageReportedGenericArgs(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult)206 MessageReportedGenericArgs(int uid, int direction, int initiatorLogicalAddress, 207 int destinationLogicalAddress, int opcode, int sendMessageResult) { 208 this.mUid = uid; 209 this.mDirection = direction; 210 this.mInitiatorLogicalAddress = initiatorLogicalAddress; 211 this.mDestinationLogicalAddress = destinationLogicalAddress; 212 this.mOpcode = opcode; 213 this.mSendMessageResult = sendMessageResult; 214 } 215 } 216 217 /** 218 * Contains the opcode-dependent arguments for creating a HdmiCecMessageReported atom. Each 219 * field is initialized to a null-like value by default. Therefore, a freshly constructed 220 * instance of this object represents a HDMI CEC message whose type does not require any 221 * additional arguments. 222 */ 223 private class MessageReportedSpecialArgs { 224 int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN; 225 int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN; 226 int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN; 227 } 228 } 229