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