• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.telephony.metrics;
18 
19 import android.annotation.Nullable;
20 import android.os.SystemClock;
21 import android.telephony.AccessNetworkConstants;
22 import android.telephony.AccessNetworkUtils;
23 import android.telephony.Annotation.NetworkType;
24 import android.telephony.NetworkRegistrationInfo;
25 import android.telephony.ServiceState;
26 import android.telephony.TelephonyManager;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.telephony.Phone;
30 import com.android.internal.telephony.PhoneFactory;
31 import com.android.internal.telephony.ServiceStateTracker;
32 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
33 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
34 import com.android.telephony.Rlog;
35 
36 import java.util.concurrent.atomic.AtomicReference;
37 
38 /** Tracks service state duration and switch metrics for each phone. */
39 public class ServiceStateStats {
40     private static final String TAG = ServiceStateStats.class.getSimpleName();
41 
42     private final AtomicReference<TimestampedServiceState> mLastState =
43             new AtomicReference<>(new TimestampedServiceState(null, 0L));
44     private final Phone mPhone;
45     private final PersistAtomsStorage mStorage;
46 
ServiceStateStats(Phone phone)47     public ServiceStateStats(Phone phone) {
48         mPhone = phone;
49         mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
50     }
51 
52     /** Finalizes the durations of the current service state segment. */
conclude()53     public void conclude() {
54         final long now = getTimeMillis();
55         TimestampedServiceState lastState =
56                 mLastState.getAndUpdate(
57                         state -> new TimestampedServiceState(state.mServiceState, now));
58         addServiceState(lastState, now);
59     }
60 
61     /** Updates the current service state. */
onServiceStateChanged(ServiceState serviceState)62     public void onServiceStateChanged(ServiceState serviceState) {
63         final long now = getTimeMillis();
64         if (isModemOff(serviceState)) {
65             // Finish the duration of last service state and mark modem off
66             addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now);
67         } else {
68             CellularServiceState newState = new CellularServiceState();
69             newState.voiceRat = getVoiceRat(mPhone, serviceState);
70             newState.dataRat = getDataRat(serviceState);
71             newState.voiceRoamingType = serviceState.getVoiceRoamingType();
72             newState.dataRoamingType = serviceState.getDataRoamingType();
73             newState.isEndc = isEndc(serviceState);
74             newState.simSlotIndex = mPhone.getPhoneId();
75             newState.isMultiSim = SimSlotState.isMultiSim();
76             newState.carrierId = mPhone.getCarrierId();
77 
78             TimestampedServiceState prevState =
79                     mLastState.getAndSet(new TimestampedServiceState(newState, now));
80             addServiceStateAndSwitch(
81                     prevState, now, getDataServiceSwitch(prevState.mServiceState, newState));
82         }
83     }
84 
addServiceState(TimestampedServiceState prevState, long now)85     private void addServiceState(TimestampedServiceState prevState, long now) {
86         addServiceStateAndSwitch(prevState, now, null);
87     }
88 
addServiceStateAndSwitch( TimestampedServiceState prevState, long now, @Nullable CellularDataServiceSwitch serviceSwitch)89     private void addServiceStateAndSwitch(
90             TimestampedServiceState prevState,
91             long now,
92             @Nullable CellularDataServiceSwitch serviceSwitch) {
93         if (prevState.mServiceState == null) {
94             // Skip duration when modem is off
95             return;
96         }
97         if (now >= prevState.mTimestamp) {
98             CellularServiceState state = copyOf(prevState.mServiceState);
99             state.totalTimeMillis = now - prevState.mTimestamp;
100             mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch);
101         } else {
102             Rlog.e(TAG, "addServiceState: durationMillis<0");
103         }
104     }
105 
106     @Nullable
getDataServiceSwitch( @ullable CellularServiceState prevState, CellularServiceState nextState)107     private CellularDataServiceSwitch getDataServiceSwitch(
108             @Nullable CellularServiceState prevState, CellularServiceState nextState) {
109         // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs.
110         if (prevState != null
111                 && prevState.isMultiSim == nextState.isMultiSim
112                 && prevState.carrierId == nextState.carrierId
113                 && prevState.dataRat != nextState.dataRat) {
114             CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
115             serviceSwitch.ratFrom = prevState.dataRat;
116             serviceSwitch.ratTo = nextState.dataRat;
117             serviceSwitch.isMultiSim = nextState.isMultiSim;
118             serviceSwitch.simSlotIndex = nextState.simSlotIndex;
119             serviceSwitch.carrierId = nextState.carrierId;
120             serviceSwitch.switchCount = 1;
121             return serviceSwitch;
122         } else {
123             return null;
124         }
125     }
126 
127     /** Returns the service state for the given phone, or {@code null} if it cannot be obtained. */
128     @Nullable
getServiceStateForPhone(Phone phone)129     private static ServiceState getServiceStateForPhone(Phone phone) {
130         ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
131         return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
132     }
133 
134     /**
135      * Returns the band used from the given phone and RAT, or {@code 0} if it is invalid or cannot
136      * be determined.
137      */
getBand(Phone phone, @NetworkType int rat)138     static int getBand(Phone phone, @NetworkType int rat) {
139         ServiceState serviceState = getServiceStateForPhone(phone);
140         return getBand(serviceState, rat);
141     }
142 
143     /**
144      * Returns the band used from the given service state and RAT, or {@code 0} if it is invalid or
145      * cannot be determined.
146      */
getBand(@ullable ServiceState serviceState, @NetworkType int rat)147     static int getBand(@Nullable ServiceState serviceState, @NetworkType int rat) {
148         if (serviceState == null) {
149             return 0; // Band unknown
150         }
151         int chNumber = serviceState.getChannelNumber();
152         int band;
153         switch (rat) {
154             case TelephonyManager.NETWORK_TYPE_GSM:
155             case TelephonyManager.NETWORK_TYPE_GPRS:
156             case TelephonyManager.NETWORK_TYPE_EDGE:
157                 band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber);
158                 break;
159             case TelephonyManager.NETWORK_TYPE_UMTS:
160             case TelephonyManager.NETWORK_TYPE_HSDPA:
161             case TelephonyManager.NETWORK_TYPE_HSUPA:
162             case TelephonyManager.NETWORK_TYPE_HSPA:
163             case TelephonyManager.NETWORK_TYPE_HSPAP:
164                 band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber);
165                 break;
166             case TelephonyManager.NETWORK_TYPE_LTE:
167             case TelephonyManager.NETWORK_TYPE_LTE_CA:
168                 band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber);
169                 break;
170             default:
171                 band = 0;
172                 break;
173         }
174         return band == AccessNetworkUtils.INVALID_BAND ? 0 : band;
175     }
176 
copyOf(CellularServiceState state)177     private static CellularServiceState copyOf(CellularServiceState state) {
178         // MessageNano does not support clone, have to copy manually
179         CellularServiceState copy = new CellularServiceState();
180         copy.voiceRat = state.voiceRat;
181         copy.dataRat = state.dataRat;
182         copy.voiceRoamingType = state.voiceRoamingType;
183         copy.dataRoamingType = state.dataRoamingType;
184         copy.isEndc = state.isEndc;
185         copy.simSlotIndex = state.simSlotIndex;
186         copy.isMultiSim = state.isMultiSim;
187         copy.carrierId = state.carrierId;
188         copy.totalTimeMillis = state.totalTimeMillis;
189         return copy;
190     }
191 
192     /**
193      * Returns {@code true} if modem radio is turned off (e.g. airplane mode).
194      *
195      * <p>Currently this is approximated by voice service state being {@code STATE_POWER_OFF}.
196      */
isModemOff(ServiceState state)197     private static boolean isModemOff(ServiceState state) {
198         // TODO(b/189335473): we should get this info from phone's radio power state, which is
199         // updated separately
200         return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
201     }
202 
getVoiceRat(Phone phone, ServiceState state)203     private static @NetworkType int getVoiceRat(Phone phone, ServiceState state) {
204         boolean isWifiCall =
205                 phone.getImsPhone() != null
206                         && phone.getImsPhone().isWifiCallingEnabled()
207                         && state.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN;
208         return isWifiCall ? TelephonyManager.NETWORK_TYPE_IWLAN : state.getVoiceNetworkType();
209     }
210 
getDataRat(ServiceState state)211     private static @NetworkType int getDataRat(ServiceState state) {
212         final NetworkRegistrationInfo wwanRegInfo =
213                 state.getNetworkRegistrationInfo(
214                         NetworkRegistrationInfo.DOMAIN_PS,
215                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
216         return wwanRegInfo != null
217                 ? wwanRegInfo.getAccessNetworkTechnology()
218                 : TelephonyManager.NETWORK_TYPE_UNKNOWN;
219     }
220 
isEndc(ServiceState state)221     private static boolean isEndc(ServiceState state) {
222         if (getDataRat(state) != TelephonyManager.NETWORK_TYPE_LTE) {
223             return false;
224         }
225         int nrState = state.getNrState();
226         return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED
227                 || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
228     }
229 
230     @VisibleForTesting
getTimeMillis()231     protected long getTimeMillis() {
232         return SystemClock.elapsedRealtime();
233     }
234 
235     private static final class TimestampedServiceState {
236         private final CellularServiceState mServiceState;
237         private final long mTimestamp; // Start time of the service state segment
238 
TimestampedServiceState(CellularServiceState serviceState, long timestamp)239         TimestampedServiceState(CellularServiceState serviceState, long timestamp) {
240             mServiceState = serviceState;
241             mTimestamp = timestamp;
242         }
243     }
244 }
245