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.internal.power; 18 19 import android.annotation.IntDef; 20 import android.os.BatteryStats; 21 import android.telephony.ModemActivityInfo; 22 import android.telephony.ServiceState; 23 import android.telephony.TelephonyManager; 24 import android.util.Log; 25 import android.util.Slog; 26 import android.util.SparseArray; 27 import android.util.SparseDoubleArray; 28 29 import com.android.internal.os.PowerProfile; 30 import com.android.internal.util.XmlUtils; 31 32 import org.xmlpull.v1.XmlPullParser; 33 import org.xmlpull.v1.XmlPullParserException; 34 35 import java.io.IOException; 36 import java.io.PrintWriter; 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.Arrays; 40 41 /** 42 * ModemPowerProfile for handling the modem element in the power_profile.xml 43 */ 44 @android.ravenwood.annotation.RavenwoodKeepWholeClass 45 public class ModemPowerProfile { 46 private static final String TAG = "ModemPowerProfile"; 47 48 private static final String TAG_SLEEP = "sleep"; 49 private static final String TAG_IDLE = "idle"; 50 private static final String TAG_ACTIVE = "active"; 51 private static final String TAG_RECEIVE = "receive"; 52 private static final String TAG_TRANSMIT = "transmit"; 53 private static final String ATTR_RAT = "rat"; 54 private static final String ATTR_NR_FREQUENCY = "nrFrequency"; 55 private static final String ATTR_LEVEL = "level"; 56 57 /** 58 * A flattened list of the modem power constant extracted from the given XML parser. 59 * 60 * The bitfields of a key describes what its corresponding power constant represents: 61 * [31:28] - {@link ModemDrainType} (max count = 16). 62 * [27:24] - {@link ModemTxLevel} (only for {@link MODEM_DRAIN_TYPE_TX}) (max count = 16). 63 * [23:20] - {@link ModemRatType} (max count = 16). 64 * [19:16] - {@link ModemNrFrequencyRange} (only for {@link MODEM_RAT_TYPE_NR}) 65 * (max count = 16). 66 * [15:0] - RESERVED 67 */ 68 private final SparseDoubleArray mPowerConstants = new SparseDoubleArray(); 69 70 private static final int MODEM_DRAIN_TYPE_MASK = 0xF000_0000; 71 private static final int MODEM_TX_LEVEL_MASK = 0x0F00_0000; 72 private static final int MODEM_RAT_TYPE_MASK = 0x00F0_0000; 73 private static final int MODEM_NR_FREQUENCY_RANGE_MASK = 0x000F_0000; 74 75 /** 76 * Corresponds to the overall modem battery drain while asleep. 77 */ 78 public static final int MODEM_DRAIN_TYPE_SLEEP = 0x0000_0000; 79 80 /** 81 * Corresponds to the overall modem battery drain while idle. 82 */ 83 public static final int MODEM_DRAIN_TYPE_IDLE = 0x1000_0000; 84 85 /** 86 * Corresponds to the modem battery drain while receiving data. A specific Rx battery drain 87 * power constant can be selected using a bitwise OR (|) with {@link ModemRatType} and 88 * {@link ModemNrFrequencyRange} (when applicable). 89 */ 90 public static final int MODEM_DRAIN_TYPE_RX = 0x2000_0000; 91 92 /** 93 * Corresponds to the modem battery drain while receiving data. 94 * {@link ModemTxLevel} must be specified with this drain type. 95 * Specific Tx battery drain power constanta can be selected using a bitwise OR (|) with 96 * {@link ModemRatType} and {@link ModemNrFrequencyRange} (when applicable). 97 */ 98 public static final int MODEM_DRAIN_TYPE_TX = 0x3000_0000; 99 100 private static final int IGNORE = -1; 101 102 @IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = { 103 MODEM_DRAIN_TYPE_SLEEP, 104 MODEM_DRAIN_TYPE_IDLE, 105 MODEM_DRAIN_TYPE_RX, 106 MODEM_DRAIN_TYPE_TX, 107 }) 108 @Retention(RetentionPolicy.SOURCE) 109 public @interface ModemDrainType { 110 } 111 112 113 private static final SparseArray<String> MODEM_DRAIN_TYPE_NAMES = new SparseArray<>(4); 114 static { MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_SLEEP, "SLEEP")115 MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_SLEEP, "SLEEP"); MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_IDLE, "IDLE")116 MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_IDLE, "IDLE"); MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_RX, "RX")117 MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_RX, "RX"); MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_TX, "TX")118 MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_TX, "TX"); 119 } 120 121 /** 122 * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_0}. 123 */ 124 125 public static final int MODEM_TX_LEVEL_0 = 0x0000_0000; 126 127 /** 128 * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_1}. 129 */ 130 131 public static final int MODEM_TX_LEVEL_1 = 0x0100_0000; 132 133 /** 134 * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_2}. 135 */ 136 137 public static final int MODEM_TX_LEVEL_2 = 0x0200_0000; 138 139 /** 140 * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_3}. 141 */ 142 143 public static final int MODEM_TX_LEVEL_3 = 0x0300_0000; 144 145 /** 146 * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_4}. 147 */ 148 149 public static final int MODEM_TX_LEVEL_4 = 0x0400_0000; 150 151 private static final int MODEM_TX_LEVEL_COUNT = 5; 152 153 @IntDef(prefix = {"MODEM_TX_LEVEL_"}, value = { 154 MODEM_TX_LEVEL_0, 155 MODEM_TX_LEVEL_1, 156 MODEM_TX_LEVEL_2, 157 MODEM_TX_LEVEL_3, 158 MODEM_TX_LEVEL_4, 159 }) 160 @Retention(RetentionPolicy.SOURCE) 161 public @interface ModemTxLevel { 162 } 163 164 private static final SparseArray<String> MODEM_TX_LEVEL_NAMES = new SparseArray<>(5); 165 static { MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_0, "0")166 MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_0, "0"); MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_1, "1")167 MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_1, "1"); MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_2, "2")168 MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_2, "2"); MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_3, "3")169 MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_3, "3"); MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_4, "4")170 MODEM_TX_LEVEL_NAMES.put(MODEM_TX_LEVEL_4, "4"); 171 } 172 173 private static final int[] MODEM_TX_LEVEL_MAP = new int[]{ 174 MODEM_TX_LEVEL_0, 175 MODEM_TX_LEVEL_1, 176 MODEM_TX_LEVEL_2, 177 MODEM_TX_LEVEL_3, 178 MODEM_TX_LEVEL_4}; 179 180 /** 181 * Fallback for any active modem usage that does not match specified Radio Access Technology 182 * (RAT) power constants. 183 */ 184 public static final int MODEM_RAT_TYPE_DEFAULT = 0x0000_0000; 185 186 /** 187 * Corresponds to active modem usage on 4G {@link TelephonyManager#NETWORK_TYPE_LTE} RAT. 188 */ 189 public static final int MODEM_RAT_TYPE_LTE = 0x0010_0000; 190 191 /** 192 * Corresponds to active modem usage on 5G {@link TelephonyManager#NETWORK_TYPE_NR} RAT. 193 */ 194 public static final int MODEM_RAT_TYPE_NR = 0x0020_0000; 195 196 @IntDef(prefix = {"MODEM_RAT_TYPE_"}, value = { 197 MODEM_RAT_TYPE_DEFAULT, 198 MODEM_RAT_TYPE_LTE, 199 MODEM_RAT_TYPE_NR, 200 }) 201 @Retention(RetentionPolicy.SOURCE) 202 public @interface ModemRatType { 203 } 204 205 private static final SparseArray<String> MODEM_RAT_TYPE_NAMES = new SparseArray<>(3); 206 static { MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_DEFAULT, "DEFAULT")207 MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_DEFAULT, "DEFAULT"); MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_LTE, "LTE")208 MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_LTE, "LTE"); MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_NR, "NR")209 MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_NR, "NR"); 210 } 211 212 /** 213 * Fallback for any active 5G modem usage that does not match specified NR frequency power 214 * constants. 215 */ 216 public static final int MODEM_NR_FREQUENCY_RANGE_DEFAULT = 0x0000_0000; 217 218 /** 219 * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_LOW}. 220 */ 221 public static final int MODEM_NR_FREQUENCY_RANGE_LOW = 0x0001_0000; 222 223 /** 224 * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MID}. 225 */ 226 public static final int MODEM_NR_FREQUENCY_RANGE_MID = 0x0002_0000; 227 228 /** 229 * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_HIGH}. 230 */ 231 public static final int MODEM_NR_FREQUENCY_RANGE_HIGH = 0x0003_0000; 232 233 /** 234 * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MMWAVE}. 235 */ 236 public static final int MODEM_NR_FREQUENCY_RANGE_MMWAVE = 0x0004_0000; 237 238 @IntDef(prefix = {"MODEM_NR_FREQUENCY_RANGE_"}, value = { 239 MODEM_RAT_TYPE_DEFAULT, 240 MODEM_NR_FREQUENCY_RANGE_LOW, 241 MODEM_NR_FREQUENCY_RANGE_MID, 242 MODEM_NR_FREQUENCY_RANGE_HIGH, 243 MODEM_NR_FREQUENCY_RANGE_MMWAVE, 244 }) 245 @Retention(RetentionPolicy.SOURCE) 246 public @interface ModemNrFrequencyRange { 247 } 248 private static final SparseArray<String> MODEM_NR_FREQUENCY_RANGE_NAMES = new SparseArray<>(5); 249 static { MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_DEFAULT, "DEFAULT")250 MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_DEFAULT, "DEFAULT"); MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_LOW, "LOW")251 MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_LOW, "LOW"); MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MID, "MID")252 MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MID, "MID"); MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_HIGH, "HIGH")253 MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_HIGH, "HIGH"); MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MMWAVE, "MMWAVE")254 MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MMWAVE, "MMWAVE"); 255 } 256 ModemPowerProfile()257 public ModemPowerProfile() { 258 } 259 260 /** 261 * Generates a ModemPowerProfile object from the <modem /> element of a power_profile.xml 262 */ parseFromXml(XmlPullParser parser)263 public void parseFromXml(XmlPullParser parser) throws IOException, 264 XmlPullParserException { 265 final int depth = parser.getDepth(); 266 while (XmlUtils.nextElementWithin(parser, depth)) { 267 final String name = parser.getName(); 268 switch (name) { 269 case TAG_SLEEP: 270 if (parser.next() != XmlPullParser.TEXT) { 271 continue; 272 } 273 final String sleepDrain = parser.getText(); 274 setPowerConstant(MODEM_DRAIN_TYPE_SLEEP, sleepDrain); 275 break; 276 case TAG_IDLE: 277 if (parser.next() != XmlPullParser.TEXT) { 278 continue; 279 } 280 final String idleDrain = parser.getText(); 281 setPowerConstant(MODEM_DRAIN_TYPE_IDLE, idleDrain); 282 break; 283 case TAG_ACTIVE: 284 parseActivePowerConstantsFromXml(parser); 285 break; 286 default: 287 Slog.e(TAG, "Unexpected element parsed: " + name); 288 } 289 } 290 } 291 292 /** Parse the <active /> XML element */ parseActivePowerConstantsFromXml(XmlPullParser parser)293 private void parseActivePowerConstantsFromXml(XmlPullParser parser) 294 throws IOException, XmlPullParserException { 295 // Parse attributes to get the type of active modem usage the power constants are for. 296 final int ratType; 297 final int nrfType; 298 try { 299 ratType = getTypeFromAttribute(parser, ATTR_RAT, MODEM_RAT_TYPE_NAMES); 300 if (ratType == MODEM_RAT_TYPE_NR) { 301 nrfType = getTypeFromAttribute(parser, ATTR_NR_FREQUENCY, 302 MODEM_NR_FREQUENCY_RANGE_NAMES); 303 } else { 304 nrfType = 0; 305 } 306 } catch (IllegalArgumentException iae) { 307 Slog.e(TAG, "Failed parse to active modem power constants", iae); 308 return; 309 } 310 311 // Parse and populate the active modem use power constants. 312 final int depth = parser.getDepth(); 313 while (XmlUtils.nextElementWithin(parser, depth)) { 314 final String name = parser.getName(); 315 switch (name) { 316 case TAG_RECEIVE: 317 if (parser.next() != XmlPullParser.TEXT) { 318 continue; 319 } 320 final String rxDrain = parser.getText(); 321 final int rxKey = MODEM_DRAIN_TYPE_RX | ratType | nrfType; 322 setPowerConstant(rxKey, rxDrain); 323 break; 324 case TAG_TRANSMIT: 325 final int level = XmlUtils.readIntAttribute(parser, ATTR_LEVEL, -1); 326 if (parser.next() != XmlPullParser.TEXT) { 327 continue; 328 } 329 final String txDrain = parser.getText(); 330 if (level < 0 || level >= MODEM_TX_LEVEL_COUNT) { 331 Slog.e(TAG, 332 "Unexpected tx level: " + level + ". Must be between 0 and " + ( 333 MODEM_TX_LEVEL_COUNT - 1)); 334 continue; 335 } 336 final int modemTxLevel = MODEM_TX_LEVEL_MAP[level]; 337 final int txKey = MODEM_DRAIN_TYPE_TX | modemTxLevel | ratType | nrfType; 338 setPowerConstant(txKey, txDrain); 339 break; 340 default: 341 Slog.e(TAG, "Unexpected element parsed: " + name); 342 } 343 } 344 } 345 getTypeFromAttribute(XmlPullParser parser, String attr, SparseArray<String> names)346 private static int getTypeFromAttribute(XmlPullParser parser, String attr, 347 SparseArray<String> names) { 348 final String value = XmlUtils.readStringAttribute(parser, attr); 349 if (value == null) { 350 // Attribute was not specified, just use the default. 351 return 0; 352 } 353 int index = -1; 354 final int size = names.size(); 355 // Manual linear search for string. (SparseArray uses == not equals.) 356 for (int i = 0; i < size; i++) { 357 if (value.equals(names.valueAt(i))) { 358 index = i; 359 } 360 } 361 if (index < 0) { 362 final String[] stringNames = new String[size]; 363 for (int i = 0; i < size; i++) { 364 stringNames[i] = names.valueAt(i); 365 } 366 throw new IllegalArgumentException( 367 "Unexpected " + attr + " value : " + value + ". Acceptable values are " 368 + Arrays.toString(stringNames)); 369 } 370 return names.keyAt(index); 371 } 372 373 /** 374 * Set the average battery drain in milli-amps of the modem for a given drain type. 375 * 376 * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel}, 377 * {@link ModemRatType}, and {@link ModemNrFrequencyRange}.key 378 * @param value the battery dram in milli-amps for the given key. 379 */ setPowerConstant(int key, String value)380 public void setPowerConstant(int key, String value) { 381 try { 382 mPowerConstants.put(key, Double.valueOf(value)); 383 } catch (Exception e) { 384 Slog.e(TAG, "Failed to set power constant 0x" + Integer.toHexString( 385 key) + "(" + keyToString(key) + ") to " + value, e); 386 } 387 } 388 getAverageBatteryDrainKey(@odemDrainType int drainType, @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, int txLevel)389 public static long getAverageBatteryDrainKey(@ModemDrainType int drainType, 390 @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, 391 int txLevel) { 392 long key = PowerProfile.SUBSYSTEM_MODEM; 393 394 // Attach Modem drain type to the key if specified. 395 if (drainType != IGNORE) { 396 key |= drainType; 397 } 398 399 // Attach RadioAccessTechnology to the key if specified. 400 switch (rat) { 401 case IGNORE: 402 // do nothing 403 break; 404 case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER: 405 key |= MODEM_RAT_TYPE_DEFAULT; 406 break; 407 case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE: 408 key |= MODEM_RAT_TYPE_LTE; 409 break; 410 case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR: 411 key |= MODEM_RAT_TYPE_NR; 412 break; 413 default: 414 Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat); 415 } 416 417 // Attach NR Frequency Range to the key if specified. 418 switch (freqRange) { 419 case IGNORE: 420 // do nothing 421 break; 422 case ServiceState.FREQUENCY_RANGE_UNKNOWN: 423 key |= MODEM_NR_FREQUENCY_RANGE_DEFAULT; 424 break; 425 case ServiceState.FREQUENCY_RANGE_LOW: 426 key |= MODEM_NR_FREQUENCY_RANGE_LOW; 427 break; 428 case ServiceState.FREQUENCY_RANGE_MID: 429 key |= MODEM_NR_FREQUENCY_RANGE_MID; 430 break; 431 case ServiceState.FREQUENCY_RANGE_HIGH: 432 key |= MODEM_NR_FREQUENCY_RANGE_HIGH; 433 break; 434 case ServiceState.FREQUENCY_RANGE_MMWAVE: 435 key |= MODEM_NR_FREQUENCY_RANGE_MMWAVE; 436 break; 437 default: 438 Log.w(TAG, "Unexpected NR frequency range : " + freqRange); 439 } 440 441 // Attach transmission level to the key if specified. 442 switch (txLevel) { 443 case IGNORE: 444 // do nothing 445 break; 446 case 0: 447 key |= MODEM_TX_LEVEL_0; 448 break; 449 case 1: 450 key |= MODEM_TX_LEVEL_1; 451 break; 452 case 2: 453 key |= MODEM_TX_LEVEL_2; 454 break; 455 case 3: 456 key |= MODEM_TX_LEVEL_3; 457 break; 458 case 4: 459 key |= MODEM_TX_LEVEL_4; 460 break; 461 default: 462 Log.w(TAG, "Unexpected transmission level : " + txLevel); 463 } 464 return key; 465 } 466 467 /** 468 * Returns the average battery drain in milli-amps of the modem for a given drain type. 469 * Returns {@link Double.NaN} if a suitable value is not found for the given key. 470 * 471 * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel}, 472 * {@link ModemRatType}, and {@link ModemNrFrequencyRange}. 473 */ getAverageBatteryDrainMa(int key)474 public double getAverageBatteryDrainMa(int key) { 475 int bestKey = key; 476 double value; 477 value = mPowerConstants.get(bestKey, Double.NaN); 478 if (!Double.isNaN(value)) return value; 479 // The power constant for given key was not explicitly set. Try to fallback to possible 480 // defaults. 481 482 if ((bestKey & MODEM_NR_FREQUENCY_RANGE_MASK) != MODEM_NR_FREQUENCY_RANGE_DEFAULT) { 483 // Fallback to NR Frequency default value 484 bestKey &= ~MODEM_NR_FREQUENCY_RANGE_MASK; 485 bestKey |= MODEM_NR_FREQUENCY_RANGE_DEFAULT; 486 value = mPowerConstants.get(bestKey, Double.NaN); 487 if (!Double.isNaN(value)) return value; 488 } 489 490 if ((bestKey & MODEM_RAT_TYPE_MASK) != MODEM_RAT_TYPE_DEFAULT) { 491 // Fallback to RAT default value 492 bestKey &= ~MODEM_RAT_TYPE_MASK; 493 bestKey |= MODEM_RAT_TYPE_DEFAULT; 494 value = mPowerConstants.get(bestKey, Double.NaN); 495 if (!Double.isNaN(value)) return value; 496 } 497 498 Slog.w(TAG, 499 "getAverageBatteryDrainMaH called with unexpected key: 0x" + Integer.toHexString( 500 key) + ", " + keyToString(key)); 501 return Double.NaN; 502 } 503 504 /** 505 * Returns a human readable version of a key. 506 */ keyToString(int key)507 public static String keyToString(int key) { 508 StringBuilder sb = new StringBuilder(); 509 final int drainType = key & MODEM_DRAIN_TYPE_MASK; 510 appendFieldToString(sb, "drain", MODEM_DRAIN_TYPE_NAMES, drainType); 511 sb.append(","); 512 513 if (drainType == MODEM_DRAIN_TYPE_TX) { 514 final int txLevel = key & MODEM_TX_LEVEL_MASK; 515 appendFieldToString(sb, "level", MODEM_TX_LEVEL_NAMES, txLevel); 516 sb.append(","); 517 } 518 519 final int ratType = key & MODEM_RAT_TYPE_MASK; 520 appendFieldToString(sb, "RAT", MODEM_RAT_TYPE_NAMES, ratType); 521 522 if (ratType == MODEM_RAT_TYPE_NR) { 523 sb.append(","); 524 final int nrFreq = key & MODEM_NR_FREQUENCY_RANGE_MASK; 525 appendFieldToString(sb, "nrFreq", MODEM_NR_FREQUENCY_RANGE_NAMES, nrFreq); 526 } 527 return sb.toString(); 528 } 529 appendFieldToString(StringBuilder sb, String fieldName, SparseArray<String> names, int key)530 private static void appendFieldToString(StringBuilder sb, String fieldName, 531 SparseArray<String> names, int key) { 532 sb.append(fieldName); 533 sb.append(":"); 534 final String name = names.get(key, null); 535 if (name == null) { 536 sb.append("UNKNOWN(0x"); 537 sb.append(Integer.toHexString(key)); 538 sb.append(")"); 539 } else { 540 sb.append(name); 541 } 542 } 543 544 /** 545 * Clear this ModemPowerProfile power constants. 546 */ clear()547 public void clear() { 548 mPowerConstants.clear(); 549 } 550 551 552 /** 553 * Dump this ModemPowerProfile power constants. 554 */ dump(PrintWriter pw)555 public void dump(PrintWriter pw) { 556 final int size = mPowerConstants.size(); 557 for (int i = 0; i < size; i++) { 558 pw.print(keyToString(mPowerConstants.keyAt(i))); 559 pw.print("="); 560 pw.println(mPowerConstants.valueAt(i)); 561 } 562 } 563 } 564