1 /* 2 * Copyright (C) 2021 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.car.rotary; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.content.ComponentName; 22 import android.graphics.Rect; 23 import android.view.KeyEvent; 24 import android.view.View; 25 import android.view.accessibility.AccessibilityNodeInfo; 26 import android.view.accessibility.AccessibilityWindowInfo; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 32 import com.android.internal.util.dump.DualDumpOutputStream; 33 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Map; 37 38 /** Utility methods for dumpsys. */ 39 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) 40 final class DumpUtils { DumpUtils()41 private DumpUtils() {} 42 43 /** Writes {@code focusDirection} to a dump in text or proto format. */ writeFocusDirection(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @View.FocusRealDirection int focusDirection)44 static void writeFocusDirection(@NonNull DualDumpOutputStream dumpOutputStream, 45 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 46 @View.FocusRealDirection int focusDirection) { 47 if (!dumpAsProto) { 48 dumpOutputStream.write(fieldName, fieldId, Navigator.directionToString(focusDirection)); 49 return; 50 } 51 int val; 52 switch (focusDirection) { 53 case View.FOCUS_LEFT: 54 val = RotaryProtos.FOCUS_LEFT; 55 break; 56 case View.FOCUS_UP: 57 val = RotaryProtos.FOCUS_UP; 58 break; 59 case View.FOCUS_RIGHT: 60 val = RotaryProtos.FOCUS_RIGHT; 61 break; 62 case View.FOCUS_DOWN: 63 val = RotaryProtos.FOCUS_DOWN; 64 break; 65 default: 66 throw new IllegalArgumentException("Invalid direction: " + focusDirection); 67 } 68 dumpOutputStream.write(fieldName, fieldId, val); 69 } 70 71 /** Writes {@code rect} to a dump in text or proto format. */ writeRect(@onNull DualDumpOutputStream dumpOutputStream, @NonNull Rect rect, @NonNull String fieldName, long fieldId)72 static void writeRect(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull Rect rect, 73 @NonNull String fieldName, long fieldId) { 74 long fieldToken = dumpOutputStream.start(fieldName, fieldId); 75 dumpOutputStream.write("left", RotaryProtos.Rect.LEFT, rect.left); 76 dumpOutputStream.write("top", RotaryProtos.Rect.TOP, rect.top); 77 dumpOutputStream.write("right", RotaryProtos.Rect.RIGHT, rect.right); 78 dumpOutputStream.write("bottom", RotaryProtos.Rect.BOTTOM, rect.bottom); 79 dumpOutputStream.end(fieldToken); 80 } 81 82 /** Writes {@code afterScrollAction} to a dump in text or proto format. */ writeAfterScrollAction(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, RotaryService.AfterScrollAction afterScrollAction)83 static void writeAfterScrollAction(@NonNull DualDumpOutputStream dumpOutputStream, 84 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 85 RotaryService.AfterScrollAction afterScrollAction) { 86 if (!dumpAsProto) { 87 dumpOutputStream.write(fieldName, fieldId, afterScrollAction.name()); 88 return; 89 } 90 int val; 91 switch (afterScrollAction) { 92 case NONE: 93 val = RotaryProtos.AFTER_SCROLL_DO_NOTHING; 94 break; 95 case FOCUS_PREVIOUS: 96 val = RotaryProtos.AFTER_SCROLL_FOCUS_PREVIOUS; 97 break; 98 case FOCUS_NEXT: 99 val = RotaryProtos.AFTER_SCROLL_FOCUS_NEXT; 100 break; 101 case FOCUS_FIRST: 102 val = RotaryProtos.AFTER_SCROLL_FOCUS_FIRST; 103 break; 104 case FOCUS_LAST: 105 val = RotaryProtos.AFTER_SCROLL_FOCUS_LAST; 106 break; 107 default: 108 throw new IllegalArgumentException( 109 "Invalid after scroll action: " + afterScrollAction); 110 } 111 dumpOutputStream.write(fieldName, fieldId, val); 112 } 113 114 /** Writes {@code componentName} to a dump in text or proto format. */ writeComponentNameToString(@onNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName, long fieldId, @Nullable ComponentName componentName)115 static void writeComponentNameToString(@NonNull DualDumpOutputStream dumpOutputStream, 116 @NonNull String fieldName, long fieldId, @Nullable ComponentName componentName) { 117 dumpOutputStream.write(fieldName, fieldId, 118 componentName == null ? null : componentName.flattenToShortString()); 119 } 120 121 /** Writes {@code object.toString()} to a dump in text or proto format. */ writeObject(@onNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName, long fieldId, @Nullable Object object)122 static void writeObject(@NonNull DualDumpOutputStream dumpOutputStream, 123 @NonNull String fieldName, long fieldId, @Nullable Object object) { 124 dumpOutputStream.write(fieldName, fieldId, object == null ? null : object.toString()); 125 } 126 127 /** 128 * Writes the result of {@link Object#toString} on each of {@code objects}' elements to a dump 129 * in text or proto format. In the latter case, the field must be {@code repeated}. 130 */ writeObjects(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull Object[] objects)131 static void writeObjects(@NonNull DualDumpOutputStream dumpOutputStream, 132 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 133 @NonNull Object[] objects) { 134 if (!dumpAsProto) { 135 dumpOutputStream.write(fieldName, fieldId, Arrays.toString(objects)); 136 return; 137 } 138 for (Object object : objects) { 139 writeObject(dumpOutputStream, fieldName, fieldId, object); 140 } 141 } 142 143 /** 144 * Writes the given integers to a dump in text or proto format. In the latter case, the field 145 * must be {@code repeated}. 146 */ writeInts(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull int[] vals)147 static void writeInts(@NonNull DualDumpOutputStream dumpOutputStream, 148 boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull int[] vals) { 149 if (!dumpAsProto) { 150 dumpOutputStream.write(fieldName, fieldId, Arrays.toString(vals)); 151 return; 152 } 153 for (int val : vals) { 154 dumpOutputStream.write(fieldName, fieldId, val); 155 } 156 } 157 158 /** 159 * Writes the given keycodes to a dump in text or proto format. In the former case, the keycodes 160 * are written as {@link KeyCode} constants. In the latter case, the field must be {@code 161 * repeated}. 162 */ writeKeyCodes(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull int[] vals)163 static void writeKeyCodes(@NonNull DualDumpOutputStream dumpOutputStream, 164 boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull int[] vals) { 165 if (!dumpAsProto) { 166 StringBuilder sb = new StringBuilder(); 167 sb.append('['); 168 for (int i = 0; i < vals.length; i++) { 169 if (i > 0) { 170 sb.append(", "); 171 } 172 sb.append(KeyEvent.keyCodeToString(vals[i])); 173 } 174 sb.append(']'); 175 dumpOutputStream.write(fieldName, fieldId, sb.toString()); 176 return; 177 } 178 for (int val : vals) { 179 dumpOutputStream.write(fieldName, fieldId, val); 180 } 181 } 182 183 /** 184 * Writes the given CharSequences to a dump in text or proto format, converting them to strings. 185 * In the latter case, the field must be {@code repeated}. 186 */ writeCharSequences(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull Collection<CharSequence> vals)187 static void writeCharSequences(@NonNull DualDumpOutputStream dumpOutputStream, 188 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 189 @NonNull Collection<CharSequence> vals) { 190 if (!dumpAsProto) { 191 dumpOutputStream.write(fieldName, fieldId, vals.toString()); 192 return; 193 } 194 for (CharSequence val : vals) { 195 dumpOutputStream.write(fieldName, fieldId, val.toString()); 196 } 197 } 198 199 /** 200 * Writes the given integers to a dump in text or proto format. In the latter case, the field 201 * must be {@code repeated}. 202 */ writeIntegers(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull Collection<Integer> vals)203 static void writeIntegers(@NonNull DualDumpOutputStream dumpOutputStream, 204 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 205 @NonNull Collection<Integer> vals) { 206 if (!dumpAsProto) { 207 dumpOutputStream.write(fieldName, fieldId, vals.toString()); 208 return; 209 } 210 for (Integer val : vals) { 211 dumpOutputStream.write(fieldName, fieldId, val); 212 } 213 } 214 215 /** 216 * Writes the given map from window ID to window type to a dump in text or proto format. In the 217 * former case, the window types are written as {@link AccessibilityWindowInfo} constants. In 218 * the latter case, the field must be a {@code map}. 219 */ writeWindowTypes(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull Map<Integer, Integer> map)220 static void writeWindowTypes(@NonNull DualDumpOutputStream dumpOutputStream, 221 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 222 @NonNull Map<Integer, Integer> map) { 223 if (!dumpAsProto) { 224 long fieldToken = dumpOutputStream.start(fieldName, fieldId); 225 for (Map.Entry<Integer, Integer> entry : map.entrySet()) { 226 dumpOutputStream.write(/* fieldName= */ entry.getKey().toString(), /* fieldId= */ 0, 227 AccessibilityWindowInfo.typeToString(entry.getValue())); 228 } 229 dumpOutputStream.end(fieldToken); 230 return; 231 } 232 for (Map.Entry<Integer, Integer> entry : map.entrySet()) { 233 long fieldToken = dumpOutputStream.start(fieldName, fieldId); 234 dumpOutputStream.write("key", /* fieldId= */ 1, entry.getKey()); 235 dumpOutputStream.write("value", /* fieldId= */ 2, entry.getValue()); 236 dumpOutputStream.end(fieldToken); 237 } 238 } 239 240 /** 241 * Writes the given map from window ID to node to a dump in text or proto format. In both cases, 242 * the nodes are written as {@code toString}s. In the latter case, the field must be a {@code 243 * map}. 244 */ writeFocusedNodes(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId, @NonNull Map<Integer, AccessibilityNodeInfo> map)245 static void writeFocusedNodes(@NonNull DualDumpOutputStream dumpOutputStream, 246 boolean dumpAsProto, @NonNull String fieldName, long fieldId, 247 @NonNull Map<Integer, AccessibilityNodeInfo> map) { 248 if (!dumpAsProto) { 249 long fieldToken = dumpOutputStream.start(fieldName, fieldId); 250 for (Map.Entry<Integer, AccessibilityNodeInfo> entry : map.entrySet()) { 251 dumpOutputStream.write(/* fieldName= */ entry.getKey().toString(), /* fieldId= */ 0, 252 entry.getValue().toString()); 253 } 254 dumpOutputStream.end(fieldToken); 255 return; 256 } 257 for (Map.Entry<Integer, AccessibilityNodeInfo> entry : map.entrySet()) { 258 long fieldToken = dumpOutputStream.start(fieldName, fieldId); 259 dumpOutputStream.write("key", /* fieldId= */ 1, entry.getKey()); 260 dumpOutputStream.write("value", /* fieldId= */ 2, entry.getValue().toString()); 261 dumpOutputStream.end(fieldToken); 262 } 263 } 264 } 265