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