• 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 static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
29 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
30 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.internal.telephony.Phone;
34 import com.android.internal.telephony.PhoneFactory;
35 import com.android.internal.telephony.ServiceStateTracker;
36 import com.android.internal.telephony.imsphone.ImsPhone;
37 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
38 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
39 import com.android.telephony.Rlog;
40 
41 import java.util.concurrent.atomic.AtomicReference;
42 
43 /** Tracks service state duration and switch metrics for each phone. */
44 public class ServiceStateStats {
45     private static final String TAG = ServiceStateStats.class.getSimpleName();
46 
47     private final AtomicReference<TimestampedServiceState> mLastState =
48             new AtomicReference<>(new TimestampedServiceState(null, 0L));
49     private final Phone mPhone;
50     private final PersistAtomsStorage mStorage;
51 
ServiceStateStats(Phone phone)52     public ServiceStateStats(Phone phone) {
53         mPhone = phone;
54         mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
55     }
56 
57     /** Finalizes the durations of the current service state segment. */
conclude()58     public void conclude() {
59         final long now = getTimeMillis();
60         TimestampedServiceState lastState =
61                 mLastState.getAndUpdate(
62                         state -> new TimestampedServiceState(state.mServiceState, now));
63         addServiceState(lastState, now);
64     }
65 
66     /** Updates service state when IMS voice registration changes. */
onImsVoiceRegistrationChanged()67     public void onImsVoiceRegistrationChanged() {
68         final long now = getTimeMillis();
69         TimestampedServiceState lastState =
70                 mLastState.getAndUpdate(
71                         state -> {
72                             if (state.mServiceState == null) {
73                                 return new TimestampedServiceState(null, now);
74                             }
75                             CellularServiceState newServiceState = copyOf(state.mServiceState);
76                             newServiceState.voiceRat =
77                                     getVoiceRat(mPhone, getServiceStateForPhone(mPhone));
78                             return new TimestampedServiceState(newServiceState, now);
79                         });
80         addServiceState(lastState, now);
81     }
82 
83     /** Updates the current service state. */
onServiceStateChanged(ServiceState serviceState)84     public void onServiceStateChanged(ServiceState serviceState) {
85         final long now = getTimeMillis();
86         if (isModemOff(serviceState)) {
87             // Finish the duration of last service state and mark modem off
88             addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now);
89         } else {
90             CellularServiceState newState = new CellularServiceState();
91             newState.voiceRat = getVoiceRat(mPhone, serviceState);
92             newState.dataRat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
93             newState.voiceRoamingType = serviceState.getVoiceRoamingType();
94             newState.dataRoamingType = serviceState.getDataRoamingType();
95             newState.isEndc = isEndc(serviceState);
96             newState.simSlotIndex = mPhone.getPhoneId();
97             newState.isMultiSim = SimSlotState.isMultiSim();
98             newState.carrierId = mPhone.getCarrierId();
99             newState.isEmergencyOnly = isEmergencyOnly(serviceState);
100 
101             TimestampedServiceState prevState =
102                     mLastState.getAndSet(new TimestampedServiceState(newState, now));
103             addServiceStateAndSwitch(
104                     prevState, now, getDataServiceSwitch(prevState.mServiceState, newState));
105         }
106     }
107 
addServiceState(TimestampedServiceState prevState, long now)108     private void addServiceState(TimestampedServiceState prevState, long now) {
109         addServiceStateAndSwitch(prevState, now, null);
110     }
111 
addServiceStateAndSwitch( TimestampedServiceState prevState, long now, @Nullable CellularDataServiceSwitch serviceSwitch)112     private void addServiceStateAndSwitch(
113             TimestampedServiceState prevState,
114             long now,
115             @Nullable CellularDataServiceSwitch serviceSwitch) {
116         if (prevState.mServiceState == null) {
117             // Skip duration when modem is off
118             return;
119         }
120         if (now >= prevState.mTimestamp) {
121             CellularServiceState state = copyOf(prevState.mServiceState);
122             state.totalTimeMillis = now - prevState.mTimestamp;
123             mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch);
124         } else {
125             Rlog.e(TAG, "addServiceState: durationMillis<0");
126         }
127     }
128 
129     @Nullable
getDataServiceSwitch( @ullable CellularServiceState prevState, CellularServiceState nextState)130     private CellularDataServiceSwitch getDataServiceSwitch(
131             @Nullable CellularServiceState prevState, CellularServiceState nextState) {
132         // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs.
133         if (prevState != null
134                 && prevState.isMultiSim == nextState.isMultiSim
135                 && prevState.carrierId == nextState.carrierId
136                 && prevState.dataRat != nextState.dataRat) {
137             CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
138             serviceSwitch.ratFrom = prevState.dataRat;
139             serviceSwitch.ratTo = nextState.dataRat;
140             serviceSwitch.isMultiSim = nextState.isMultiSim;
141             serviceSwitch.simSlotIndex = nextState.simSlotIndex;
142             serviceSwitch.carrierId = nextState.carrierId;
143             serviceSwitch.switchCount = 1;
144             return serviceSwitch;
145         } else {
146             return null;
147         }
148     }
149 
150     /** Returns the service state for the given phone, or {@code null} if it cannot be obtained. */
151     @Nullable
getServiceStateForPhone(Phone phone)152     private static ServiceState getServiceStateForPhone(Phone phone) {
153         ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
154         return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
155     }
156 
157     /**
158      * Returns the band used from the given phone, or {@code 0} if it is invalid or cannot be
159      * determined.
160      */
getBand(Phone phone)161     static int getBand(Phone phone) {
162         ServiceState serviceState = getServiceStateForPhone(phone);
163         return getBand(serviceState);
164     }
165 
166     /**
167      * Returns the band used from the given service state, or {@code 0} if it is invalid or cannot
168      * be determined.
169      */
getBand(@ullable ServiceState serviceState)170     static int getBand(@Nullable ServiceState serviceState) {
171         if (serviceState == null) {
172             Rlog.w(TAG, "getBand: serviceState=null");
173             return 0; // Band unknown
174         }
175         int chNumber = serviceState.getChannelNumber();
176         int band;
177         @NetworkType int rat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
178         if (rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
179             rat = serviceState.getVoiceNetworkType();
180         }
181         switch (rat) {
182             case TelephonyManager.NETWORK_TYPE_GSM:
183             case TelephonyManager.NETWORK_TYPE_GPRS:
184             case TelephonyManager.NETWORK_TYPE_EDGE:
185                 band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber);
186                 break;
187             case TelephonyManager.NETWORK_TYPE_UMTS:
188             case TelephonyManager.NETWORK_TYPE_HSDPA:
189             case TelephonyManager.NETWORK_TYPE_HSUPA:
190             case TelephonyManager.NETWORK_TYPE_HSPA:
191             case TelephonyManager.NETWORK_TYPE_HSPAP:
192                 band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber);
193                 break;
194             case TelephonyManager.NETWORK_TYPE_LTE:
195             case TelephonyManager.NETWORK_TYPE_LTE_CA:
196                 band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber);
197                 break;
198             case TelephonyManager.NETWORK_TYPE_NR:
199                 band = AccessNetworkUtils.getOperatingBandForNrarfcn(chNumber);
200                 break;
201             default:
202                 Rlog.w(TAG, "getBand: unknown WWAN RAT " + rat);
203                 band = 0;
204                 break;
205         }
206         if (band == AccessNetworkUtils.INVALID_BAND) {
207             Rlog.w(TAG, "getBand: band invalid for rat=" + rat + " ch=" + chNumber);
208             return 0;
209         } else {
210             return band;
211         }
212     }
213 
copyOf(CellularServiceState state)214     private static CellularServiceState copyOf(CellularServiceState state) {
215         // MessageNano does not support clone, have to copy manually
216         CellularServiceState copy = new CellularServiceState();
217         copy.voiceRat = state.voiceRat;
218         copy.dataRat = state.dataRat;
219         copy.voiceRoamingType = state.voiceRoamingType;
220         copy.dataRoamingType = state.dataRoamingType;
221         copy.isEndc = state.isEndc;
222         copy.simSlotIndex = state.simSlotIndex;
223         copy.isMultiSim = state.isMultiSim;
224         copy.carrierId = state.carrierId;
225         copy.totalTimeMillis = state.totalTimeMillis;
226         copy.isEmergencyOnly = state.isEmergencyOnly;
227         return copy;
228     }
229 
230     /**
231      * Returns {@code true} if modem radio is turned off (e.g. airplane mode).
232      *
233      * <p>Currently this is approximated by voice service state being {@code STATE_POWER_OFF}.
234      */
isModemOff(ServiceState state)235     private static boolean isModemOff(ServiceState state) {
236         // TODO(b/189335473): we should get this info from phone's radio power state, which is
237         // updated separately
238         return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
239     }
240 
241     /**
242      * Returns the current voice RAT from IMS registration if present, otherwise from the service
243      * state.
244      *
245      * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned
246      * despite that the device may have emergency service over a certain RAT.
247      */
getVoiceRat(Phone phone, @Nullable ServiceState state)248     static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state) {
249         return getVoiceRat(phone, state, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN);
250     }
251 
252     /**
253      * Returns the current voice RAT according to the bearer.
254      *
255      * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned
256      * despite that the device may have emergency service over a certain RAT.
257      */
258     @VisibleForTesting public
getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer)259     static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer) {
260         if (state == null) {
261             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
262         }
263         ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
264         if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) {
265             @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech();
266             if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
267                 // If IMS is registered over WWAN but WWAN PS is not in service,
268                 // fallback to WWAN CS RAT
269                 boolean isImsVoiceRatValid =
270                         (imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
271                                 || getRat(state, NetworkRegistrationInfo.DOMAIN_PS)
272                                         != TelephonyManager.NETWORK_TYPE_UNKNOWN);
273                 if (isImsVoiceRatValid) {
274                     return imsVoiceRat;
275                 }
276             }
277         }
278         if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
279             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
280         } else {
281             return getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
282         }
283     }
284 
285     /** Returns RAT used by WWAN if WWAN is in service. */
getRat( ServiceState state, @NetworkRegistrationInfo.Domain int domain)286     public static @NetworkType int getRat(
287             ServiceState state, @NetworkRegistrationInfo.Domain int domain) {
288         final NetworkRegistrationInfo wwanRegInfo =
289                 state.getNetworkRegistrationInfo(
290                         domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
291         return wwanRegInfo != null && wwanRegInfo.isInService()
292                 ? wwanRegInfo.getAccessNetworkTechnology()
293                 : TelephonyManager.NETWORK_TYPE_UNKNOWN;
294     }
295 
isEmergencyOnly(ServiceState state)296     private static boolean isEmergencyOnly(ServiceState state) {
297         NetworkRegistrationInfo regInfo =
298                 state.getNetworkRegistrationInfo(
299                         NetworkRegistrationInfo.DOMAIN_CS,
300                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
301         return regInfo != null && !regInfo.isInService() && regInfo.isEmergencyEnabled();
302     }
303 
isEndc(ServiceState state)304     private static boolean isEndc(ServiceState state) {
305         if (getRat(state, NetworkRegistrationInfo.DOMAIN_PS) != TelephonyManager.NETWORK_TYPE_LTE) {
306             return false;
307         }
308         int nrState = state.getNrState();
309         return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED
310                 || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
311     }
312 
313     @VisibleForTesting
getTimeMillis()314     protected long getTimeMillis() {
315         return SystemClock.elapsedRealtime();
316     }
317 
318     private static final class TimestampedServiceState {
319         private final CellularServiceState mServiceState;
320         private final long mTimestamp; // Start time of the service state segment
321 
TimestampedServiceState(CellularServiceState serviceState, long timestamp)322         TimestampedServiceState(CellularServiceState serviceState, long timestamp) {
323             mServiceState = serviceState;
324             mTimestamp = timestamp;
325         }
326     }
327 }
328