• 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 static android.telephony.TelephonyManager.DATA_CONNECTED;
20 
21 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
22 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
23 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.os.SystemClock;
28 import android.telephony.AccessNetworkConstants;
29 import android.telephony.AccessNetworkUtils;
30 import android.telephony.Annotation.NetworkType;
31 import android.telephony.NetworkRegistrationInfo;
32 import android.telephony.ServiceState;
33 import android.telephony.ServiceState.RoamingType;
34 import android.telephony.TelephonyManager;
35 
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.internal.telephony.Phone;
38 import com.android.internal.telephony.PhoneFactory;
39 import com.android.internal.telephony.ServiceStateTracker;
40 import com.android.internal.telephony.data.DataNetwork;
41 import com.android.internal.telephony.data.DataNetworkController;
42 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
43 import com.android.internal.telephony.imsphone.ImsPhone;
44 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
45 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
46 import com.android.telephony.Rlog;
47 
48 import java.util.List;
49 import java.util.concurrent.atomic.AtomicBoolean;
50 import java.util.concurrent.atomic.AtomicReference;
51 
52 /** Tracks service state duration and switch metrics for each phone. */
53 public class ServiceStateStats extends DataNetworkControllerCallback {
54     private static final String TAG = ServiceStateStats.class.getSimpleName();
55 
56     private final AtomicReference<TimestampedServiceState> mLastState =
57             new AtomicReference<>(new TimestampedServiceState(null, 0L));
58     private final AtomicBoolean mOverrideVoiceService = new AtomicBoolean(false);
59     private final Phone mPhone;
60     private final PersistAtomsStorage mStorage;
61     private final DeviceStateHelper mDeviceStateHelper;
62 
ServiceStateStats(Phone phone)63     public ServiceStateStats(Phone phone) {
64         super(Runnable::run);
65         mPhone = phone;
66         mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
67         mDeviceStateHelper = PhoneFactory.getMetricsCollector().getDeviceStateHelper();
68     }
69 
70     /** Finalizes the durations of the current service state segment. */
conclude()71     public void conclude() {
72         final long now = getTimeMillis();
73         TimestampedServiceState lastState =
74                 mLastState.getAndUpdate(
75                         state -> new TimestampedServiceState(state.mServiceState, now));
76         addServiceState(lastState, now);
77     }
78 
79     /** Updates service state when IMS voice registration changes. */
onImsVoiceRegistrationChanged()80     public void onImsVoiceRegistrationChanged() {
81         final long now = getTimeMillis();
82         TimestampedServiceState lastState =
83                 mLastState.getAndUpdate(
84                         state -> {
85                             if (state.mServiceState == null) {
86                                 return new TimestampedServiceState(null, now);
87                             }
88                             CellularServiceState newServiceState = copyOf(state.mServiceState);
89                             newServiceState.voiceRat =
90                                     getVoiceRat(mPhone, getServiceStateForPhone(mPhone));
91                             return new TimestampedServiceState(newServiceState, now);
92                         });
93         addServiceState(lastState, now);
94     }
95 
96     /** Registers for internet pdn connected callback. */
registerDataNetworkControllerCallback()97     public void registerDataNetworkControllerCallback() {
98         mPhone.getDataNetworkController().registerDataNetworkControllerCallback(this);
99     }
100 
101     /** Updates service state when internet pdn gets connected. */
onInternetDataNetworkConnected(@onNull List<DataNetwork> internetNetworks)102     public void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {
103         onInternetDataNetworkChanged(true);
104     }
105 
106     /** Updates service state when internet pdn gets disconnected. */
onInternetDataNetworkDisconnected()107     public void onInternetDataNetworkDisconnected() {
108         onInternetDataNetworkChanged(false);
109     }
110 
111     /** Updates the current service state. */
onServiceStateChanged(ServiceState serviceState)112     public void onServiceStateChanged(ServiceState serviceState) {
113         final long now = getTimeMillis();
114         if (isModemOff(serviceState)) {
115             // Finish the duration of last service state and mark modem off
116             addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now);
117         } else {
118             CellularServiceState newState = new CellularServiceState();
119             newState.voiceRat = getVoiceRat(mPhone, serviceState);
120             newState.dataRat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
121             newState.voiceRoamingType =
122                     getNetworkRoamingState(serviceState, NetworkRegistrationInfo.DOMAIN_CS);
123             newState.dataRoamingType =
124                     getNetworkRoamingState(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
125             newState.isEndc = isEndc(serviceState);
126             newState.simSlotIndex = mPhone.getPhoneId();
127             newState.isMultiSim = SimSlotState.isMultiSim();
128             newState.carrierId = mPhone.getCarrierId();
129             newState.isEmergencyOnly = isEmergencyOnly(serviceState);
130             newState.isInternetPdnUp = isInternetPdnUp(mPhone);
131             newState.foldState = mDeviceStateHelper.getFoldState();
132             newState.overrideVoiceService = mOverrideVoiceService.get();
133             newState.isDataEnabled = mPhone.getDataSettingsManager().isDataEnabled();
134             TimestampedServiceState prevState =
135                     mLastState.getAndSet(new TimestampedServiceState(newState, now));
136             addServiceStateAndSwitch(
137                     prevState, now, getDataServiceSwitch(prevState.mServiceState, newState));
138         }
139     }
140 
141     /** Updates the fold state of the device for the current service state. */
onFoldStateChanged(int foldState)142     public void onFoldStateChanged(int foldState) {
143         final long now = getTimeMillis();
144         CellularServiceState lastServiceState = mLastState.get().mServiceState;
145         if (lastServiceState == null || lastServiceState.foldState == foldState) {
146             // Not need to update the fold state if modem is off or if is the
147             // same fold state
148             return;
149         } else {
150             TimestampedServiceState lastState =
151                     mLastState.getAndUpdate(
152                             state -> {
153                                 CellularServiceState newServiceState = copyOf(state.mServiceState);
154                                 newServiceState.foldState = foldState;
155                                 return new TimestampedServiceState(newServiceState, now);
156                             });
157             addServiceState(lastState, now);
158         }
159     }
160 
161     /** Updates override state for voice service state when voice calling capability changes */
onVoiceServiceStateOverrideChanged(boolean override)162     public void onVoiceServiceStateOverrideChanged(boolean override) {
163         if (override == mOverrideVoiceService.get()) {
164             return;
165         }
166         mOverrideVoiceService.set(override);
167         final long now = getTimeMillis();
168         TimestampedServiceState lastState =
169                 mLastState.getAndUpdate(
170                         state -> {
171                             if (state.mServiceState == null) {
172                                 return new TimestampedServiceState(null, now);
173                             }
174                             CellularServiceState newServiceState = copyOf(state.mServiceState);
175                             newServiceState.overrideVoiceService = mOverrideVoiceService.get();
176                             return new TimestampedServiceState(newServiceState, now);
177                         });
178         addServiceState(lastState, now);
179     }
180 
addServiceState(TimestampedServiceState prevState, long now)181     private void addServiceState(TimestampedServiceState prevState, long now) {
182         addServiceStateAndSwitch(prevState, now, null);
183     }
184 
addServiceStateAndSwitch( TimestampedServiceState prevState, long now, @Nullable CellularDataServiceSwitch serviceSwitch)185     private void addServiceStateAndSwitch(
186             TimestampedServiceState prevState,
187             long now,
188             @Nullable CellularDataServiceSwitch serviceSwitch) {
189         if (prevState.mServiceState == null) {
190             // Skip duration when modem is off
191             return;
192         }
193         if (now >= prevState.mTimestamp) {
194             CellularServiceState state = copyOf(prevState.mServiceState);
195             state.totalTimeMillis = now - prevState.mTimestamp;
196             mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch);
197         } else {
198             Rlog.e(TAG, "addServiceState: durationMillis<0");
199         }
200     }
201 
202     @Nullable
getDataServiceSwitch( @ullable CellularServiceState prevState, CellularServiceState nextState)203     private CellularDataServiceSwitch getDataServiceSwitch(
204             @Nullable CellularServiceState prevState, CellularServiceState nextState) {
205         // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs.
206         if (prevState != null
207                 && prevState.isMultiSim == nextState.isMultiSim
208                 && prevState.carrierId == nextState.carrierId
209                 && prevState.dataRat != nextState.dataRat) {
210             CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
211             serviceSwitch.ratFrom = prevState.dataRat;
212             serviceSwitch.ratTo = nextState.dataRat;
213             serviceSwitch.isMultiSim = nextState.isMultiSim;
214             serviceSwitch.simSlotIndex = nextState.simSlotIndex;
215             serviceSwitch.carrierId = nextState.carrierId;
216             serviceSwitch.switchCount = 1;
217             return serviceSwitch;
218         } else {
219             return null;
220         }
221     }
222 
223     /** Returns the service state for the given phone, or {@code null} if it cannot be obtained. */
224     @Nullable
getServiceStateForPhone(Phone phone)225     private static ServiceState getServiceStateForPhone(Phone phone) {
226         ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
227         return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
228     }
229 
230     /**
231      * Returns the band used from the given phone, or {@code 0} if it is invalid or cannot be
232      * determined.
233      */
getBand(Phone phone)234     static int getBand(Phone phone) {
235         ServiceState serviceState = getServiceStateForPhone(phone);
236         return getBand(serviceState);
237     }
238 
239     /**
240      * Returns the band used from the given service state, or {@code 0} if it is invalid or cannot
241      * be determined.
242      */
getBand(@ullable ServiceState serviceState)243     static int getBand(@Nullable ServiceState serviceState) {
244         if (serviceState == null) {
245             Rlog.w(TAG, "getBand: serviceState=null");
246             return 0; // Band unknown
247         }
248         int chNumber = serviceState.getChannelNumber();
249         int band;
250         @NetworkType int rat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
251         if (rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
252             rat = serviceState.getVoiceNetworkType();
253         }
254         switch (rat) {
255             case TelephonyManager.NETWORK_TYPE_GSM:
256             case TelephonyManager.NETWORK_TYPE_GPRS:
257             case TelephonyManager.NETWORK_TYPE_EDGE:
258                 band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber);
259                 break;
260             case TelephonyManager.NETWORK_TYPE_UMTS:
261             case TelephonyManager.NETWORK_TYPE_HSDPA:
262             case TelephonyManager.NETWORK_TYPE_HSUPA:
263             case TelephonyManager.NETWORK_TYPE_HSPA:
264             case TelephonyManager.NETWORK_TYPE_HSPAP:
265                 band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber);
266                 break;
267             case TelephonyManager.NETWORK_TYPE_LTE:
268             case TelephonyManager.NETWORK_TYPE_LTE_CA:
269                 band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber);
270                 break;
271             case TelephonyManager.NETWORK_TYPE_NR:
272                 band = AccessNetworkUtils.getOperatingBandForNrarfcn(chNumber);
273                 break;
274             default:
275                 Rlog.w(TAG, "getBand: unknown WWAN RAT " + rat);
276                 band = 0;
277                 break;
278         }
279         if (band == AccessNetworkUtils.INVALID_BAND) {
280             Rlog.w(TAG, "getBand: band invalid for rat=" + rat + " ch=" + chNumber);
281             return 0;
282         } else {
283             return band;
284         }
285     }
286 
copyOf(CellularServiceState state)287     private static CellularServiceState copyOf(CellularServiceState state) {
288         // MessageNano does not support clone, have to copy manually
289         CellularServiceState copy = new CellularServiceState();
290         copy.voiceRat = state.voiceRat;
291         copy.dataRat = state.dataRat;
292         copy.voiceRoamingType = state.voiceRoamingType;
293         copy.dataRoamingType = state.dataRoamingType;
294         copy.isEndc = state.isEndc;
295         copy.simSlotIndex = state.simSlotIndex;
296         copy.isMultiSim = state.isMultiSim;
297         copy.carrierId = state.carrierId;
298         copy.totalTimeMillis = state.totalTimeMillis;
299         copy.isEmergencyOnly = state.isEmergencyOnly;
300         copy.isInternetPdnUp = state.isInternetPdnUp;
301         copy.foldState = state.foldState;
302         copy.overrideVoiceService = state.overrideVoiceService;
303         copy.isDataEnabled = state.isDataEnabled;
304         return copy;
305     }
306 
307     /**
308      * Returns {@code true} if modem radio is turned off (e.g. airplane mode).
309      *
310      * <p>Currently this is approximated by voice service state being {@code STATE_POWER_OFF}.
311      */
isModemOff(ServiceState state)312     private static boolean isModemOff(ServiceState state) {
313         // TODO(b/189335473): we should get this info from phone's radio power state, which is
314         // updated separately
315         return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
316     }
317 
318     /**
319      * Returns the current voice RAT from IMS registration if present, otherwise from the service
320      * state.
321      *
322      * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned
323      * despite that the device may have emergency service over a certain RAT.
324      */
getVoiceRat(Phone phone, @Nullable ServiceState state)325     static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state) {
326         return getVoiceRat(phone, state, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN);
327     }
328 
329     /**
330      * Returns the current voice RAT according to the bearer.
331      *
332      * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned
333      * despite that the device may have emergency service over a certain RAT.
334      */
335     @VisibleForTesting public
getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer)336     static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer) {
337         if (state == null) {
338             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
339         }
340         ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
341         if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) {
342             @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech();
343             if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
344                 // If IMS is registered over WWAN but WWAN PS is not in service,
345                 // fallback to WWAN CS RAT
346                 boolean isImsVoiceRatValid =
347                         (imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
348                                 || getRat(state, NetworkRegistrationInfo.DOMAIN_PS)
349                                         != TelephonyManager.NETWORK_TYPE_UNKNOWN);
350                 if (isImsVoiceRatValid) {
351                     return imsVoiceRat;
352                 }
353             }
354         }
355         if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
356             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
357         } else {
358             return getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
359         }
360     }
361 
362     /** Returns RAT used by WWAN if WWAN is in service. */
getRat( ServiceState state, @NetworkRegistrationInfo.Domain int domain)363     public static @NetworkType int getRat(
364             ServiceState state, @NetworkRegistrationInfo.Domain int domain) {
365         final NetworkRegistrationInfo wwanRegInfo =
366                 state.getNetworkRegistrationInfo(
367                         domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
368         return wwanRegInfo != null && wwanRegInfo.isInService()
369                 ? wwanRegInfo.getAccessNetworkTechnology()
370                 : TelephonyManager.NETWORK_TYPE_UNKNOWN;
371     }
372 
isEmergencyOnly(ServiceState state)373     private static boolean isEmergencyOnly(ServiceState state) {
374         NetworkRegistrationInfo regInfo =
375                 state.getNetworkRegistrationInfo(
376                         NetworkRegistrationInfo.DOMAIN_CS,
377                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
378         return regInfo != null && !regInfo.isInService() && regInfo.isEmergencyEnabled();
379     }
380 
isEndc(ServiceState state)381     private static boolean isEndc(ServiceState state) {
382         if (getRat(state, NetworkRegistrationInfo.DOMAIN_PS) != TelephonyManager.NETWORK_TYPE_LTE) {
383             return false;
384         }
385         int nrState = state.getNrState();
386         return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED
387                 || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
388     }
389 
isInternetPdnUp(Phone phone)390     private static boolean isInternetPdnUp(Phone phone) {
391         DataNetworkController dataNetworkController = phone.getDataNetworkController();
392         if (dataNetworkController != null) {
393             return dataNetworkController.getInternetDataNetworkState() == DATA_CONNECTED;
394         }
395         return false;
396     }
397 
onInternetDataNetworkChanged(boolean internetPdnUp)398     private void onInternetDataNetworkChanged(boolean internetPdnUp) {
399         final long now = getTimeMillis();
400         TimestampedServiceState lastState =
401                 mLastState.getAndUpdate(
402                         state -> {
403                             if (state.mServiceState == null) {
404                                 return new TimestampedServiceState(null, now);
405                             }
406                             CellularServiceState newServiceState = copyOf(state.mServiceState);
407                             newServiceState.isInternetPdnUp = internetPdnUp;
408                             return new TimestampedServiceState(newServiceState, now);
409                         });
410         addServiceState(lastState, now);
411     }
412 
getNetworkRoamingState( ServiceState ss, @NetworkRegistrationInfo.Domain int domain)413     private static @RoamingType int getNetworkRoamingState(
414             ServiceState ss, @NetworkRegistrationInfo.Domain int domain) {
415         final NetworkRegistrationInfo nri =
416                 ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
417         if (nri == null) {
418             // No registration for domain
419             return ServiceState.ROAMING_TYPE_NOT_ROAMING;
420         }
421         @RoamingType int roamingType = nri.getRoamingType();
422         if (nri.isNetworkRoaming() && roamingType == ServiceState.ROAMING_TYPE_NOT_ROAMING) {
423             // Roaming is overridden, exact roaming type unknown.
424             return ServiceState.ROAMING_TYPE_UNKNOWN;
425         }
426         return roamingType;
427     }
428 
429     /** Determines whether device is roaming, bypassing carrier overrides. */
isNetworkRoaming( ServiceState ss, @NetworkRegistrationInfo.Domain int domain)430     public static boolean isNetworkRoaming(
431             ServiceState ss, @NetworkRegistrationInfo.Domain int domain) {
432         if (ss == null) {
433             return false;
434         }
435         final NetworkRegistrationInfo nri =
436                 ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
437         return nri != null && nri.isNetworkRoaming();
438     }
439 
440     /** Determines whether device is roaming in any domain, bypassing carrier overrides. */
isNetworkRoaming(ServiceState ss)441     public static boolean isNetworkRoaming(ServiceState ss) {
442         return isNetworkRoaming(ss, NetworkRegistrationInfo.DOMAIN_CS)
443                 || isNetworkRoaming(ss, NetworkRegistrationInfo.DOMAIN_PS);
444     }
445 
446     @VisibleForTesting
getTimeMillis()447     protected long getTimeMillis() {
448         return SystemClock.elapsedRealtime();
449     }
450 
451     private static final class TimestampedServiceState {
452         private final CellularServiceState mServiceState;
453         private final long mTimestamp; // Start time of the service state segment
454 
TimestampedServiceState(CellularServiceState serviceState, long timestamp)455         TimestampedServiceState(CellularServiceState serviceState, long timestamp) {
456             mServiceState = serviceState;
457             mTimestamp = timestamp;
458         }
459     }
460 }
461