• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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