1 /*
2  * Copyright (C) 2015 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 android.telephony;
18 
19 import android.annotation.DurationMillisLong;
20 import android.annotation.ElapsedRealtimeLong;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.SystemClock;
28 import android.telephony.ServiceState.FrequencyRange;
29 import android.util.Range;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import java.util.Arrays;
34 import java.util.Objects;
35 
36 /**
37  * Contains information about the modem's activity. May be useful for power stats reporting.
38  * @hide
39  */
40 @android.ravenwood.annotation.RavenwoodKeepWholeClass
41 @SystemApi
42 public final class ModemActivityInfo implements Parcelable {
43     private static final int TX_POWER_LEVELS = 5;
44 
45     /**
46      * Corresponds to transmit power of less than 0dBm.
47      */
48     public static final int TX_POWER_LEVEL_0 = 0;
49 
50     /**
51      * Corresponds to transmit power between 0dBm and 5dBm.
52      */
53     public static final int TX_POWER_LEVEL_1 = 1;
54 
55     /**
56      * Corresponds to transmit power between 5dBm and 15dBm.
57      */
58     public static final int TX_POWER_LEVEL_2 = 2;
59 
60     /**
61      * Corresponds to transmit power between 15dBm and 20dBm.
62      */
63     public static final int TX_POWER_LEVEL_3 = 3;
64 
65     /**
66      * Corresponds to transmit power above 20dBm.
67      */
68     public static final int TX_POWER_LEVEL_4 = 4;
69 
70     /**
71      * The number of transmit power levels. Fixed by HAL definition.
72      */
getNumTxPowerLevels()73     public static int getNumTxPowerLevels() {
74         return TX_POWER_LEVELS;
75     }
76 
77     /** @hide */
78     @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = {
79             TX_POWER_LEVEL_0,
80             TX_POWER_LEVEL_1,
81             TX_POWER_LEVEL_2,
82             TX_POWER_LEVEL_3,
83             TX_POWER_LEVEL_4,
84     })
85     @Retention(RetentionPolicy.SOURCE)
86     public @interface TxPowerLevel {}
87 
88     private static final Range<Integer>[] TX_POWER_RANGES = new Range[] {
89         new Range<>(Integer.MIN_VALUE, 0),
90         new Range<>(0, 5),
91         new Range<>(5, 15),
92         new Range<>(15, 20),
93         new Range<>(20, Integer.MAX_VALUE)
94     };
95 
96     private long mTimestamp;
97     private int mSleepTimeMs;
98     private int mIdleTimeMs;
99     private int[] mTotalTxTimeMs;
100     private int mTotalRxTimeMs;
101     private int mSizeOfSpecificInfo;
102     private ActivityStatsTechSpecificInfo[] mActivityStatsTechSpecificInfo;
103 
104     /**
105      * @hide
106      */
107     @TestApi
ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, @NonNull int[] txTimeMs, int rxTimeMs)108     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
109                         @NonNull int[] txTimeMs, int rxTimeMs) {
110         Objects.requireNonNull(txTimeMs);
111         if (txTimeMs.length != TX_POWER_LEVELS) {
112             throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
113         }
114         mTimestamp = timestamp;
115         mSleepTimeMs = sleepTimeMs;
116         mIdleTimeMs = idleTimeMs;
117         mTotalTxTimeMs = txTimeMs;
118         mTotalRxTimeMs = rxTimeMs;
119 
120         mActivityStatsTechSpecificInfo = new ActivityStatsTechSpecificInfo[1];
121         mSizeOfSpecificInfo = mActivityStatsTechSpecificInfo.length;
122         mActivityStatsTechSpecificInfo[0] =
123                 new ActivityStatsTechSpecificInfo(
124                         AccessNetworkConstants.AccessNetworkType.UNKNOWN,
125                         ServiceState.FREQUENCY_RANGE_UNKNOWN,
126                         txTimeMs,
127                         rxTimeMs);
128     }
129 
130     /**
131      * Provided for convenience in manipulation since the API exposes long values but internal
132      * representations are ints.
133      * @hide
134      */
ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs, @NonNull int[] txTimeMs, long rxTimeMs)135     public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
136             @NonNull int[] txTimeMs, long rxTimeMs) {
137         this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
138     }
139 
140     /** @hide */
ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo)141     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
142                         @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo) {
143         mTimestamp = timestamp;
144         mSleepTimeMs = sleepTimeMs;
145         mIdleTimeMs = idleTimeMs;
146         mActivityStatsTechSpecificInfo = activityStatsTechSpecificInfo;
147         mSizeOfSpecificInfo = mActivityStatsTechSpecificInfo.length;
148         mTotalTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
149         for (int i = 0; i < getNumTxPowerLevels(); i++) {
150             for (int j = 0; j < getSpecificInfoLength(); j++) {
151                 mTotalTxTimeMs[i] = mTotalTxTimeMs[i]
152                             + (int) mActivityStatsTechSpecificInfo[j].getTransmitTimeMillis(i);
153             }
154         }
155         mTotalRxTimeMs = 0;
156         for (int i = 0; i < getSpecificInfoLength(); i++) {
157             mTotalRxTimeMs =
158                     mTotalRxTimeMs + (int) mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
159         }
160     }
161 
162     /**
163      * Provided for convenience in manipulation since the API exposes long values but internal
164      * representations are ints.
165      * @hide
166      */
ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs, @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo)167     public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
168                         @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo) {
169         this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, activityStatsTechSpecificInfo);
170     }
171 
172     @Override
toString()173     public String toString() {
174         return "ModemActivityInfo{"
175             + " mTimestamp="
176             + mTimestamp
177             + " mSleepTimeMs="
178             + mSleepTimeMs
179             + " mIdleTimeMs="
180             + mIdleTimeMs
181             + " mActivityStatsTechSpecificInfo="
182             + Arrays.toString(mActivityStatsTechSpecificInfo)
183             + "}";
184     }
185 
describeContents()186     public int describeContents() {
187         return 0;
188     }
189 
190     public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
191             new Parcelable.Creator<ModemActivityInfo>() {
192         public ModemActivityInfo createFromParcel(@NonNull Parcel in) {
193             long timestamp = in.readLong();
194             int sleepTimeMs = in.readInt();
195             int idleTimeMs = in.readInt();
196             Parcelable[] tempSpecifiers =
197                     in.createTypedArray(ActivityStatsTechSpecificInfo.CREATOR);
198             ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo;
199             activityStatsTechSpecificInfo =
200                     new ActivityStatsTechSpecificInfo[tempSpecifiers.length];
201             for (int i = 0; i < tempSpecifiers.length; i++) {
202                 activityStatsTechSpecificInfo[i] =
203                                 (ActivityStatsTechSpecificInfo) tempSpecifiers[i];
204                     }
205             return new ModemActivityInfo(
206                     timestamp, sleepTimeMs, idleTimeMs, activityStatsTechSpecificInfo);
207         }
208 
209         public ModemActivityInfo[] newArray(int size) {
210             return new ModemActivityInfo[size];
211         }
212     };
213 
214     /**
215      * @param dest The Parcel in which the object should be written.
216      * @param flags Additional flags about how the object should be written.
217      */
writeToParcel(@onNull Parcel dest, int flags)218     public void writeToParcel(@NonNull Parcel dest, int flags) {
219         dest.writeLong(mTimestamp);
220         dest.writeInt(mSleepTimeMs);
221         dest.writeInt(mIdleTimeMs);
222         dest.writeTypedArray(mActivityStatsTechSpecificInfo, flags);
223     }
224 
225     /**
226      * Gets the timestamp at which this modem activity info was recorded.
227      *
228      * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this {@link
229      *     ModemActivityInfo} was recorded.
230      */
getTimestampMillis()231     public @ElapsedRealtimeLong long getTimestampMillis() {
232         return mTimestamp;
233     }
234 
235     /** @hide */
setTimestamp(long timestamp)236     public void setTimestamp(long timestamp) {
237         mTimestamp = timestamp;
238     }
239 
240     /**
241      * Gets the amount of time the modem spent transmitting at a certain power level.
242      *
243      * @param powerLevel The power level to query.
244      * @return The amount of time, in milliseconds, that the modem spent transmitting at the given
245      *     power level.
246      */
getTransmitDurationMillisAtPowerLevel( @xPowerLevel int powerLevel)247     public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
248             @TxPowerLevel int powerLevel) {
249         long txTimeMsAtPowerLevel = 0;
250         for (int i = 0; i < getSpecificInfoLength(); i++) {
251             txTimeMsAtPowerLevel +=
252                     mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
253         }
254         return txTimeMsAtPowerLevel;
255     }
256 
257     /** @hide */
getTransmitDurationMillisAtPowerLevel( @xPowerLevel int powerLevel, int rat)258     public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
259             @TxPowerLevel int powerLevel, int rat) {
260         for (int i = 0; i < getSpecificInfoLength(); i++) {
261             if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
262                 return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
263             }
264         }
265         return 0;
266     }
267 
268     /** @hide */
getTransmitDurationMillisAtPowerLevel( @xPowerLevel int powerLevel, int rat, @FrequencyRange int freq)269     public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
270             @TxPowerLevel int powerLevel, int rat, @FrequencyRange int freq) {
271         for (int i = 0; i < getSpecificInfoLength(); i++) {
272             if (mActivityStatsTechSpecificInfo[i].getRat() == rat
273                     && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
274                 return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
275             }
276         }
277         return 0;
278     }
279     /**
280      * Gets the range of transmit powers corresponding to a certain power level.
281      *
282      * @param powerLevel The power level to query
283      * @return A {@link Range} object representing the range of intensities (in dBm) to which this
284      * power level corresponds.
285      */
getTransmitPowerRange(@xPowerLevel int powerLevel)286     public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) {
287         return TX_POWER_RANGES[powerLevel];
288     }
289 
290     /** @hide */
getSpecificInfoRat(int index)291     public int getSpecificInfoRat(int index) {
292         return mActivityStatsTechSpecificInfo[index].getRat();
293     }
294 
295     /** @hide */
getSpecificInfoFrequencyRange(int index)296     public int getSpecificInfoFrequencyRange(int index) {
297         return mActivityStatsTechSpecificInfo[index].getFrequencyRange();
298     }
299     /** @hide */
setTransmitTimeMillis(int[] txTimeMs)300     public void setTransmitTimeMillis(int[] txTimeMs) {
301         mTotalTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
302     }
303     /** @hide */
setTransmitTimeMillis(int rat, int[] txTimeMs)304     public void setTransmitTimeMillis(int rat, int[] txTimeMs) {
305         for (int i = 0; i < getSpecificInfoLength(); i++) {
306             if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
307                 mActivityStatsTechSpecificInfo[i].setTransmitTimeMillis(txTimeMs);
308             }
309         }
310     }
311     /** @hide */
setTransmitTimeMillis(int rat, int freq, int[] txTimeMs)312     public void setTransmitTimeMillis(int rat, int freq, int[] txTimeMs) {
313         for (int i = 0; i < getSpecificInfoLength(); i++) {
314             if (mActivityStatsTechSpecificInfo[i].getRat() == rat
315                     && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
316                 mActivityStatsTechSpecificInfo[i].setTransmitTimeMillis(txTimeMs);
317             }
318         }
319     }
320     /**
321      * @return The raw array of transmit power durations
322      * @hide
323      */
324     @NonNull
getTransmitTimeMillis()325     public int[] getTransmitTimeMillis() {
326         return mTotalTxTimeMs;
327     }
328 
329     /** @hide */
getTransmitTimeMillis(@ccessNetworkConstants.RadioAccessNetworkType int rat)330     public int[] getTransmitTimeMillis(@AccessNetworkConstants.RadioAccessNetworkType int rat) {
331         for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
332             if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
333                 return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis();
334             }
335         }
336         return new int[5];
337     }
338 
339     /** @hide */
getTransmitTimeMillis( @ccessNetworkConstants.RadioAccessNetworkType int rat, @FrequencyRange int freq)340     public int[] getTransmitTimeMillis(
341             @AccessNetworkConstants.RadioAccessNetworkType int rat, @FrequencyRange int freq) {
342         for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
343             if (mActivityStatsTechSpecificInfo[i].getRat() == rat
344                     && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
345                 return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis();
346             }
347         }
348         return new int[5];
349     }
350 
351     /**
352      * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state.
353      *
354      * @return Time in milliseconds.
355      */
getSleepTimeMillis()356     public @DurationMillisLong long getSleepTimeMillis() {
357         return mSleepTimeMs;
358     }
359 
360     /** @hide */
setSleepTimeMillis(int sleepTimeMillis)361     public void setSleepTimeMillis(int sleepTimeMillis) {
362         mSleepTimeMs = sleepTimeMillis;
363     }
364 
365     /**
366      * Provided for convenience, since the API surface needs to return longs but internal
367      * representations are ints.
368      *
369      * @hide
370      */
setSleepTimeMillis(long sleepTimeMillis)371     public void setSleepTimeMillis(long sleepTimeMillis) {
372         mSleepTimeMs = (int) sleepTimeMillis;
373     }
374 
375     /**
376      * Computes the difference between this instance of {@link ModemActivityInfo} and another
377      * instance.
378      *
379      * This method should be used to compute the amount of activity that has happened between two
380      * samples of modem activity taken at separate times. The sample passed in as an argument to
381      * this method should be the one that's taken later in time (and therefore has more activity).
382      * @param other The other instance of {@link ModemActivityInfo} to diff against.
383      * @return An instance of {@link ModemActivityInfo} representing the difference in modem
384      * activity.
385      */
getDelta(@onNull ModemActivityInfo other)386     public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
387         ActivityStatsTechSpecificInfo[] mDeltaSpecificInfo;
388         mDeltaSpecificInfo = new ActivityStatsTechSpecificInfo[other.getSpecificInfoLength()];
389 
390         boolean matched;
391         for (int i = 0; i < other.getSpecificInfoLength(); i++) {
392             matched = false;
393             for (int j = 0; j < getSpecificInfoLength(); j++) {
394                 int rat = mActivityStatsTechSpecificInfo[j].getRat();
395                 if (rat == other.mActivityStatsTechSpecificInfo[i].getRat() && !matched) {
396                     if (mActivityStatsTechSpecificInfo[j].getRat()
397                             == AccessNetworkConstants.AccessNetworkType.NGRAN) {
398                         if (other.mActivityStatsTechSpecificInfo[i].getFrequencyRange()
399                                 == mActivityStatsTechSpecificInfo[j].getFrequencyRange()) {
400                             int freq = mActivityStatsTechSpecificInfo[j].getFrequencyRange();
401                             int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
402                             for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
403                                 txTimeMs[lvl] =
404                                         (int) (other.getTransmitDurationMillisAtPowerLevel(
405                                                             lvl, rat, freq)
406                                                         - getTransmitDurationMillisAtPowerLevel(
407                                                             lvl, rat, freq));
408                             }
409                             matched = true;
410                             mDeltaSpecificInfo[i] =
411                                     new ActivityStatsTechSpecificInfo(
412                                             rat,
413                                             freq,
414                                             txTimeMs,
415                                             (int) (other.getReceiveTimeMillis(rat, freq)
416                                                         - getReceiveTimeMillis(rat, freq)));
417                         }
418                     } else {
419                         int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
420                         for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
421                             txTimeMs[lvl] =
422                                     (int) (other.getTransmitDurationMillisAtPowerLevel(lvl, rat)
423                                                 - getTransmitDurationMillisAtPowerLevel(lvl, rat));
424                         }
425                         matched = true;
426                         mDeltaSpecificInfo[i] =
427                                 new ActivityStatsTechSpecificInfo(
428                                         rat,
429                                         ServiceState.FREQUENCY_RANGE_UNKNOWN,
430                                         txTimeMs,
431                                         (int) (other.getReceiveTimeMillis(rat)
432                                                      - getReceiveTimeMillis(rat)));
433                     }
434                 }
435             }
436             if (!matched) {
437                 mDeltaSpecificInfo[i] = other.mActivityStatsTechSpecificInfo[i];
438             }
439         }
440         return new ModemActivityInfo(
441                 other.getTimestampMillis(),
442                 other.getSleepTimeMillis() - getSleepTimeMillis(),
443                 other.getIdleTimeMillis() - getIdleTimeMillis(),
444                 mDeltaSpecificInfo);
445     }
446 
447     /**
448      * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting
449      * nor receiving.
450      *
451      * @return Time in milliseconds.
452      */
getIdleTimeMillis()453     public @DurationMillisLong long getIdleTimeMillis() {
454         return mIdleTimeMs;
455     }
456 
457     /** @hide */
setIdleTimeMillis(int idleTimeMillis)458     public void setIdleTimeMillis(int idleTimeMillis) {
459         mIdleTimeMs = idleTimeMillis;
460     }
461 
462     /**
463      * Provided for convenience, since the API surface needs to return longs but internal
464      * representations are ints.
465      *
466      * @hide
467      */
setIdleTimeMillis(long idleTimeMillis)468     public void setIdleTimeMillis(long idleTimeMillis) {
469         mIdleTimeMs = (int) idleTimeMillis;
470     }
471 
472     /**
473      * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
474      *
475      * @return Time in milliseconds.
476      */
getReceiveTimeMillis()477     public @DurationMillisLong long getReceiveTimeMillis() {
478         return mTotalRxTimeMs;
479     }
480 
481     /** @hide */
getReceiveTimeMillis(int rat)482     public @DurationMillisLong long getReceiveTimeMillis(int rat) {
483         for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
484             if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
485                 return mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
486             }
487         }
488         return 0;
489     }
490     /** @hide */
getReceiveTimeMillis(int rat, int freq)491     public @DurationMillisLong long getReceiveTimeMillis(int rat, int freq) {
492         for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
493             if (mActivityStatsTechSpecificInfo[i].getRat() == rat
494                     && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
495                 return mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
496             }
497         }
498         return 0;
499     }
500 
501     /** @hide */
setReceiveTimeMillis(int rxTimeMillis)502     public void setReceiveTimeMillis(int rxTimeMillis) {
503         mTotalRxTimeMs = rxTimeMillis;
504     }
505 
506     /**
507      * Provided for convenience, since the API surface needs to return longs but internal
508      * representations are ints.
509      *
510      * @hide
511      */
setReceiveTimeMillis(long receiveTimeMillis)512     public void setReceiveTimeMillis(long receiveTimeMillis) {
513         mTotalRxTimeMs = (int) receiveTimeMillis;
514     }
515 
516     /** @hide */
setReceiveTimeMillis(int rat, long receiveTimeMillis)517     public void setReceiveTimeMillis(int rat, long receiveTimeMillis) {
518         for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
519             if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
520                 mActivityStatsTechSpecificInfo[i].setReceiveTimeMillis(receiveTimeMillis);
521             }
522         }
523     }
524 
525     /** @hide */
setReceiveTimeMillis(int rat, int freq, long receiveTimeMillis)526     public void setReceiveTimeMillis(int rat, int freq, long receiveTimeMillis) {
527         for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
528             if (mActivityStatsTechSpecificInfo[i].getRat() == rat
529                     && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
530                 mActivityStatsTechSpecificInfo[i].setReceiveTimeMillis(receiveTimeMillis);
531             }
532         }
533     }
534 
535     /** @hide */
getSpecificInfoLength()536     public int getSpecificInfoLength() {
537         return mSizeOfSpecificInfo;
538     }
539 
540     /**
541      * Indicates if the modem has reported valid {@link ModemActivityInfo}.
542      *
543      * @return {@code true} if this {@link ModemActivityInfo} record is valid,
544      * {@code false} otherwise.
545      * @hide
546      */
547     @TestApi
isValid()548     public boolean isValid() {
549         if (mActivityStatsTechSpecificInfo == null) {
550             return false;
551         } else {
552             boolean isTxPowerValid = true;
553             boolean isRxPowerValid = true;
554             for (int i = 0; i < getSpecificInfoLength(); i++) {
555                 if (!mActivityStatsTechSpecificInfo[i].isTxPowerValid()) {
556                     isTxPowerValid = false;
557                 }
558                 if (!mActivityStatsTechSpecificInfo[i].isRxPowerValid()) {
559                     isRxPowerValid = false;
560                 }
561             }
562             return isTxPowerValid
563                     && isRxPowerValid
564                     && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) && !isEmpty());
565         }
566     }
567 
568     /** @hide */
569     @TestApi
isEmpty()570     public boolean isEmpty() {
571         boolean isTxPowerEmpty = true;
572         boolean isRxPowerEmpty = true;
573         for (int i = 0; i < getSpecificInfoLength(); i++) {
574             if (!mActivityStatsTechSpecificInfo[i].isTxPowerEmpty()) {
575                 isTxPowerEmpty = false;
576             }
577             if (!mActivityStatsTechSpecificInfo[i].isRxPowerEmpty()) {
578                 isRxPowerEmpty = false;
579             }
580         }
581         return isTxPowerEmpty
582                 && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) && isRxPowerEmpty);
583     }
584 
585     @Override
equals(Object o)586     public boolean equals(Object o) {
587         if (this == o) return true;
588         if (o == null || getClass() != o.getClass()) return false;
589         ModemActivityInfo that = (ModemActivityInfo) o;
590         return mTimestamp == that.mTimestamp
591                 && mSleepTimeMs == that.mSleepTimeMs
592                 && mIdleTimeMs == that.mIdleTimeMs
593                 && mSizeOfSpecificInfo == that.mSizeOfSpecificInfo
594                 && Arrays.equals(
595                         mActivityStatsTechSpecificInfo, that.mActivityStatsTechSpecificInfo);
596     }
597 
598     @Override
hashCode()599     public int hashCode() {
600         int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mTotalRxTimeMs);
601         result = 31 * result + Arrays.hashCode(mTotalTxTimeMs);
602         return result;
603     }
604 }
605