1 /* 2 * Copyright (C) 2024 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.satellite.metrics; 18 19 import android.annotation.NonNull; 20 import android.telephony.CellInfo; 21 import android.telephony.CellSignalStrength; 22 import android.telephony.CellSignalStrengthLte; 23 import android.telephony.NetworkRegistrationInfo; 24 import android.telephony.ServiceState; 25 import android.telephony.SignalStrength; 26 import android.telephony.TelephonyManager; 27 import android.util.Log; 28 import android.util.SparseArray; 29 30 import com.android.internal.telephony.MccTable; 31 import com.android.internal.telephony.Phone; 32 import com.android.internal.telephony.metrics.SatelliteStats; 33 import com.android.internal.telephony.subscription.SubscriptionInfoInternal; 34 import com.android.internal.telephony.subscription.SubscriptionManagerService; 35 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.List; 39 40 public class CarrierRoamingSatelliteSessionStats { 41 private static final String TAG = CarrierRoamingSatelliteSessionStats.class.getSimpleName(); 42 private static final SparseArray<CarrierRoamingSatelliteSessionStats> 43 sCarrierRoamingSatelliteSessionStats = new SparseArray<>(); 44 @NonNull private final SubscriptionManagerService mSubscriptionManagerService; 45 private int mCarrierId; 46 private boolean mIsNtnRoamingInHomeCountry; 47 private int mCountOfIncomingSms; 48 private int mCountOfOutgoingSms; 49 private int mCountOfIncomingMms; 50 private int mCountOfOutgoingMms; 51 private long mIncomingMessageId; 52 53 private int mSessionStartTimeSec; 54 private List<Long> mConnectionStartTimeList; 55 private List<Long> mConnectionEndTimeList; 56 private List<Integer> mRsrpList; 57 private List<Integer> mRssnrList; 58 CarrierRoamingSatelliteSessionStats(int subId)59 public CarrierRoamingSatelliteSessionStats(int subId) { 60 logd("Create new CarrierRoamingSatelliteSessionStats. subId=" + subId); 61 initializeParams(); 62 mSubscriptionManagerService = SubscriptionManagerService.getInstance(); 63 } 64 65 /** Gets a CarrierRoamingSatelliteSessionStats instance. */ getInstance(int subId)66 public static CarrierRoamingSatelliteSessionStats getInstance(int subId) { 67 synchronized (sCarrierRoamingSatelliteSessionStats) { 68 if (sCarrierRoamingSatelliteSessionStats.get(subId) == null) { 69 sCarrierRoamingSatelliteSessionStats.put(subId, 70 new CarrierRoamingSatelliteSessionStats(subId)); 71 } 72 return sCarrierRoamingSatelliteSessionStats.get(subId); 73 } 74 } 75 76 /** Log carrier roaming satellite session start */ onSessionStart(int carrierId, Phone phone)77 public void onSessionStart(int carrierId, Phone phone) { 78 mCarrierId = carrierId; 79 mSessionStartTimeSec = getCurrentTimeInSec(); 80 mIsNtnRoamingInHomeCountry = false; 81 onConnectionStart(phone); 82 } 83 84 /** Log carrier roaming satellite connection start */ onConnectionStart(Phone phone)85 public void onConnectionStart(Phone phone) { 86 mConnectionStartTimeList.add(getCurrentTime()); 87 updateNtnRoamingInHomeCountry(phone); 88 } 89 90 /** Log carrier roaming satellite session end */ onSessionEnd()91 public void onSessionEnd() { 92 onConnectionEnd(); 93 reportMetrics(); 94 mIsNtnRoamingInHomeCountry = false; 95 } 96 97 /** Log carrier roaming satellite connection end */ onConnectionEnd()98 public void onConnectionEnd() { 99 mConnectionEndTimeList.add(getCurrentTime()); 100 } 101 102 /** Log rsrp and rssnr when occurred the service state change with NTN is connected. */ onSignalStrength(Phone phone)103 public void onSignalStrength(Phone phone) { 104 CellSignalStrengthLte cellSignalStrengthLte = getCellSignalStrengthLte(phone); 105 int rsrp = cellSignalStrengthLte.getRsrp(); 106 int rssnr = cellSignalStrengthLte.getRssnr(); 107 if (rsrp == CellInfo.UNAVAILABLE) { 108 logd("onSignalStrength: rsrp unavailable"); 109 return; 110 } 111 if (rssnr == CellInfo.UNAVAILABLE) { 112 logd("onSignalStrength: rssnr unavailable"); 113 return; 114 } 115 mRsrpList.add(rsrp); 116 mRssnrList.add(rssnr); 117 logd("onSignalStrength : rsrp=" + rsrp + ", rssnr=" + rssnr); 118 } 119 120 /** Log incoming sms success case */ onIncomingSms(int subId)121 public void onIncomingSms(int subId) { 122 if (!isNtnConnected()) { 123 return; 124 } 125 mCountOfIncomingSms += 1; 126 logd("onIncomingSms: subId=" + subId + ", count=" + mCountOfIncomingSms); 127 } 128 129 /** Log outgoing sms success case */ onOutgoingSms(int subId)130 public void onOutgoingSms(int subId) { 131 if (!isNtnConnected()) { 132 return; 133 } 134 mCountOfOutgoingSms += 1; 135 logd("onOutgoingSms: subId=" + subId + ", count=" + mCountOfOutgoingSms); 136 } 137 138 /** Log incoming or outgoing mms success case */ onMms(boolean isIncomingMms, long messageId)139 public void onMms(boolean isIncomingMms, long messageId) { 140 if (!isNtnConnected()) { 141 return; 142 } 143 if (isIncomingMms) { 144 mIncomingMessageId = messageId; 145 mCountOfIncomingMms += 1; 146 logd("onMms: messageId=" + messageId + ", countOfIncomingMms=" + mCountOfIncomingMms); 147 } else { 148 if (mIncomingMessageId == messageId) { 149 logd("onMms: NotifyResponse ignore it."); 150 mIncomingMessageId = 0; 151 return; 152 } 153 mCountOfOutgoingMms += 1; 154 logd("onMms: countOfOutgoingMms=" + mCountOfOutgoingMms); 155 } 156 } 157 reportMetrics()158 private void reportMetrics() { 159 int totalSatelliteModeTimeSec = mSessionStartTimeSec > 0 160 ? getCurrentTimeInSec() - mSessionStartTimeSec : 0; 161 int numberOfSatelliteConnections = getNumberOfSatelliteConnections(); 162 int avgDurationOfSatelliteConnectionSec = getAvgDurationOfSatelliteConnection( 163 numberOfSatelliteConnections); 164 165 List<Integer> connectionGapList = getSatelliteConnectionGapList( 166 numberOfSatelliteConnections); 167 int satelliteConnectionGapMinSec = 0; 168 int satelliteConnectionGapMaxSec = 0; 169 if (!connectionGapList.isEmpty()) { 170 satelliteConnectionGapMinSec = Collections.min(connectionGapList); 171 satelliteConnectionGapMaxSec = Collections.max(connectionGapList); 172 } 173 174 SatelliteStats.CarrierRoamingSatelliteSessionParams params = 175 new SatelliteStats.CarrierRoamingSatelliteSessionParams.Builder() 176 .setCarrierId(mCarrierId) 177 .setIsNtnRoamingInHomeCountry(mIsNtnRoamingInHomeCountry) 178 .setTotalSatelliteModeTimeSec(totalSatelliteModeTimeSec) 179 .setNumberOfSatelliteConnections(numberOfSatelliteConnections) 180 .setAvgDurationOfSatelliteConnectionSec(avgDurationOfSatelliteConnectionSec) 181 .setSatelliteConnectionGapMinSec(satelliteConnectionGapMinSec) 182 .setSatelliteConnectionGapAvgSec(getAvg(connectionGapList)) 183 .setSatelliteConnectionGapMaxSec(satelliteConnectionGapMaxSec) 184 .setRsrpAvg(getAvg(mRsrpList)) 185 .setRsrpMedian(getMedian(mRsrpList)) 186 .setRssnrAvg(getAvg(mRssnrList)) 187 .setRssnrMedian(getMedian(mRssnrList)) 188 .setCountOfIncomingSms(mCountOfIncomingSms) 189 .setCountOfOutgoingSms(mCountOfOutgoingSms) 190 .setCountOfIncomingMms(mCountOfIncomingMms) 191 .setCountOfOutgoingMms(mCountOfOutgoingMms) 192 .build(); 193 SatelliteStats.getInstance().onCarrierRoamingSatelliteSessionMetrics(params); 194 logd("reportMetrics: " + params); 195 initializeParams(); 196 } 197 initializeParams()198 private void initializeParams() { 199 mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 200 mIsNtnRoamingInHomeCountry = false; 201 mCountOfIncomingSms = 0; 202 mCountOfOutgoingSms = 0; 203 mCountOfIncomingMms = 0; 204 mCountOfOutgoingMms = 0; 205 mIncomingMessageId = 0; 206 207 mSessionStartTimeSec = 0; 208 mConnectionStartTimeList = new ArrayList<>(); 209 mConnectionEndTimeList = new ArrayList<>(); 210 mRsrpList = new ArrayList<>(); 211 mRssnrList = new ArrayList<>(); 212 logd("initializeParams"); 213 } 214 getCellSignalStrengthLte(Phone phone)215 private CellSignalStrengthLte getCellSignalStrengthLte(Phone phone) { 216 SignalStrength signalStrength = phone.getSignalStrength(); 217 List<CellSignalStrength> cellSignalStrengths = signalStrength.getCellSignalStrengths(); 218 for (CellSignalStrength cellSignalStrength : cellSignalStrengths) { 219 if (cellSignalStrength instanceof CellSignalStrengthLte) { 220 return (CellSignalStrengthLte) cellSignalStrength; 221 } 222 } 223 224 return new CellSignalStrengthLte(); 225 } 226 getNumberOfSatelliteConnections()227 private int getNumberOfSatelliteConnections() { 228 return Math.min(mConnectionStartTimeList.size(), mConnectionEndTimeList.size()); 229 } 230 getAvgDurationOfSatelliteConnection(int numberOfSatelliteConnections)231 private int getAvgDurationOfSatelliteConnection(int numberOfSatelliteConnections) { 232 if (numberOfSatelliteConnections == 0) { 233 return 0; 234 } 235 236 long totalConnectionsDuration = 0; 237 for (int i = 0; i < numberOfSatelliteConnections; i++) { 238 long endTime = mConnectionEndTimeList.get(i); 239 long startTime = mConnectionStartTimeList.get(i); 240 if (endTime >= startTime && startTime > 0) { 241 totalConnectionsDuration += endTime - startTime; 242 } 243 } 244 245 long avgConnectionDuration = totalConnectionsDuration / numberOfSatelliteConnections; 246 return (int) (avgConnectionDuration / 1000L); 247 } 248 getSatelliteConnectionGapList(int numberOfSatelliteConnections)249 private List<Integer> getSatelliteConnectionGapList(int numberOfSatelliteConnections) { 250 if (numberOfSatelliteConnections == 0) { 251 return new ArrayList<>(); 252 } 253 254 List<Integer> connectionGapList = new ArrayList<>(); 255 for (int i = 1; i < numberOfSatelliteConnections; i++) { 256 long prevConnectionEndTime = mConnectionEndTimeList.get(i - 1); 257 long currentConnectionStartTime = mConnectionStartTimeList.get(i); 258 if (currentConnectionStartTime > prevConnectionEndTime && prevConnectionEndTime > 0) { 259 connectionGapList.add((int) ( 260 (currentConnectionStartTime - prevConnectionEndTime) / 1000)); 261 } 262 } 263 return connectionGapList; 264 } 265 getAvg(@onNull List<Integer> list)266 private int getAvg(@NonNull List<Integer> list) { 267 if (list.isEmpty()) { 268 return 0; 269 } 270 271 int total = 0; 272 for (int num : list) { 273 total += num; 274 } 275 276 return total / list.size(); 277 } 278 getMedian(@onNull List<Integer> list)279 private int getMedian(@NonNull List<Integer> list) { 280 if (list.isEmpty()) { 281 return 0; 282 } 283 int size = list.size(); 284 if (size == 1) { 285 return list.get(0); 286 } 287 288 Collections.sort(list); 289 return size % 2 == 0 ? (list.get(size / 2 - 1) + list.get(size / 2)) / 2 290 : list.get(size / 2); 291 } 292 getCurrentTimeInSec()293 private int getCurrentTimeInSec() { 294 return (int) (System.currentTimeMillis() / 1000); 295 } 296 getCurrentTime()297 private long getCurrentTime() { 298 return System.currentTimeMillis(); 299 } 300 isNtnConnected()301 private boolean isNtnConnected() { 302 return mSessionStartTimeSec != 0; 303 } 304 updateNtnRoamingInHomeCountry(Phone phone)305 private void updateNtnRoamingInHomeCountry(Phone phone) { 306 int subId = phone.getSubId(); 307 ServiceState serviceState = phone.getServiceState(); 308 if (serviceState == null) { 309 logd("ServiceState is null"); 310 return; 311 } 312 313 String satelliteRegisteredPlmn = ""; 314 for (NetworkRegistrationInfo nri 315 : serviceState.getNetworkRegistrationInfoList()) { 316 if (nri.isNonTerrestrialNetwork()) { 317 satelliteRegisteredPlmn = nri.getRegisteredPlmn(); 318 } 319 } 320 321 SubscriptionInfoInternal subscriptionInfoInternal = 322 mSubscriptionManagerService.getSubscriptionInfoInternal(subId); 323 if (subscriptionInfoInternal == null) { 324 logd("SubscriptionInfoInternal is null"); 325 return; 326 } 327 String simCountry = MccTable.countryCodeForMcc(subscriptionInfoInternal.getMcc()); 328 String satelliteRegisteredCountry = MccTable.countryCodeForMcc( 329 satelliteRegisteredPlmn.substring(0, 3)); 330 if (simCountry.equalsIgnoreCase(satelliteRegisteredCountry)) { 331 mIsNtnRoamingInHomeCountry = false; 332 } else { 333 // If device is connected to roaming non-terrestrial network, update to true. 334 mIsNtnRoamingInHomeCountry = true; 335 } 336 logd("updateNtnRoamingInHomeCountry: mIsNtnRoamingInHomeCountry=" 337 + mIsNtnRoamingInHomeCountry); 338 } 339 logd(@onNull String log)340 private void logd(@NonNull String log) { 341 Log.d(TAG, log); 342 } 343 loge(@onNull String log)344 private void loge(@NonNull String log) { 345 Log.e(TAG, log); 346 } 347 } 348