1 /* 2 * Copyright (C) 2022 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.mms.service.metrics; 18 19 import static com.android.mms.MmsStatsLog.INCOMING_MMS__RESULT__MMS_RESULT_ERROR_UNSPECIFIED; 20 import static com.android.mms.MmsStatsLog.INCOMING_MMS__RESULT__MMS_RESULT_SUCCESS; 21 import static com.android.mms.MmsStatsLog.OUTGOING_MMS__RESULT__MMS_RESULT_ERROR_UNSPECIFIED; 22 import static com.android.mms.MmsStatsLog.OUTGOING_MMS__RESULT__MMS_RESULT_SUCCESS; 23 24 import android.app.Activity; 25 import android.content.Context; 26 import android.os.Binder; 27 import android.os.SystemClock; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.telephony.ServiceState; 31 import android.telephony.SmsManager; 32 import android.telephony.SubscriptionInfo; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.telephony.UiccCardInfo; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.telephony.Phone; 40 import com.android.internal.telephony.PhoneFactory; 41 import com.android.internal.telephony.SmsApplication; 42 import com.android.internal.telephony.flags.Flags; 43 import com.android.internal.telephony.satellite.SatelliteController; 44 import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats; 45 import com.android.mms.IncomingMms; 46 import com.android.mms.OutgoingMms; 47 48 import java.util.List; 49 50 /** Collects mms events for the pulled atom. */ 51 public class MmsStats { 52 private static final String TAG = MmsStats.class.getSimpleName(); 53 54 private final Context mContext; 55 private final PersistMmsAtomsStorage mPersistMmsAtomsStorage; 56 private final String mCallingPkg; 57 private final boolean mIsIncomingMms; 58 private final long mTimestamp; 59 private int mSubId; 60 private TelephonyManager mTelephonyManager; 61 MmsStats(Context context, PersistMmsAtomsStorage persistMmsAtomsStorage, int subId, TelephonyManager telephonyManager, String callingPkg, boolean isIncomingMms)62 public MmsStats(Context context, PersistMmsAtomsStorage persistMmsAtomsStorage, int subId, 63 TelephonyManager telephonyManager, String callingPkg, boolean isIncomingMms) { 64 mContext = context; 65 mPersistMmsAtomsStorage = persistMmsAtomsStorage; 66 mSubId = subId; 67 mTelephonyManager = telephonyManager; 68 mCallingPkg = callingPkg; 69 mIsIncomingMms = isIncomingMms; 70 mTimestamp = SystemClock.elapsedRealtime(); 71 } 72 73 /** Updates subId and corresponding telephonyManager. */ updateSubId(int subId, TelephonyManager telephonyManager)74 public void updateSubId(int subId, TelephonyManager telephonyManager) { 75 mSubId = subId; 76 mTelephonyManager = telephonyManager; 77 } 78 79 /** Adds incoming or outgoing mms atom to storage. */ addAtomToStorage(int result)80 public void addAtomToStorage(int result) { 81 addAtomToStorage(result, 0, false, 0); 82 } 83 84 /** Adds incoming or outgoing mms atom to storage. */ addAtomToStorage(int result, int retryId, boolean handledByCarrierApp, long mMessageId)85 public void addAtomToStorage(int result, int retryId, boolean handledByCarrierApp, 86 long mMessageId) { 87 long identity = Binder.clearCallingIdentity(); 88 try { 89 if (mIsIncomingMms) { 90 onIncomingMms(result, retryId, handledByCarrierApp); 91 } else { 92 onOutgoingMms(result, retryId, handledByCarrierApp); 93 } 94 if (isUsingNonTerrestrialNetwork()) { 95 CarrierRoamingSatelliteSessionStats carrierRoamingSatelliteSessionStats = 96 CarrierRoamingSatelliteSessionStats.getInstance(mSubId); 97 carrierRoamingSatelliteSessionStats.onMms(mIsIncomingMms, mMessageId); 98 } 99 } finally { 100 Binder.restoreCallingIdentity(identity); 101 } 102 } 103 104 /** Creates a new atom when MMS is received. */ onIncomingMms(int result, int retryId, boolean handledByCarrierApp)105 private void onIncomingMms(int result, int retryId, boolean handledByCarrierApp) { 106 IncomingMms incomingMms = IncomingMms.newBuilder() 107 .setRat(getDataNetworkType()) 108 .setResult(getIncomingMmsResult(result)) 109 .setRoaming(getDataRoamingType()) 110 .setSimSlotIndex(getSlotIndex()) 111 .setIsMultiSim(getIsMultiSim()) 112 .setIsEsim(getIsEuicc()) 113 .setCarrierId(getSimCarrierId()) 114 .setAvgIntervalMillis(getInterval()) 115 .setMmsCount(1) 116 .setRetryId(retryId) 117 .setHandledByCarrierApp(handledByCarrierApp) 118 .setIsManagedProfile(isManagedProfile()) 119 .setIsNtn(isUsingNonTerrestrialNetwork()) 120 .setIsNbIotNtn(isNbIotNtn(mSubId)) 121 .build(); 122 mPersistMmsAtomsStorage.addIncomingMms(incomingMms); 123 } 124 125 /** Creates a new atom when MMS is sent. */ onOutgoingMms(int result, int retryId, boolean handledByCarrierApp)126 private void onOutgoingMms(int result, int retryId, boolean handledByCarrierApp) { 127 OutgoingMms outgoingMms = OutgoingMms.newBuilder() 128 .setRat(getDataNetworkType()) 129 .setResult(getOutgoingMmsResult(result)) 130 .setRoaming(getDataRoamingType()) 131 .setSimSlotIndex(getSlotIndex()) 132 .setIsMultiSim(getIsMultiSim()) 133 .setIsEsim(getIsEuicc()) 134 .setCarrierId(getSimCarrierId()) 135 .setAvgIntervalMillis(getInterval()) 136 .setMmsCount(1) 137 .setIsFromDefaultApp(isDefaultMmsApp()) 138 .setRetryId(retryId) 139 .setHandledByCarrierApp(handledByCarrierApp) 140 .setIsManagedProfile(isManagedProfile()) 141 .setIsNtn(isUsingNonTerrestrialNetwork()) 142 .setIsNbIotNtn(isNbIotNtn(mSubId)) 143 .build(); 144 mPersistMmsAtomsStorage.addOutgoingMms(outgoingMms); 145 } 146 147 /** @return {@code true} if this SIM is dedicated to work profile */ isManagedProfile()148 private boolean isManagedProfile() { 149 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 150 if (subManager == null || !subManager.isActiveSubscriptionId(mSubId)) return false; 151 UserHandle userHandle = subManager.getSubscriptionUserHandle(mSubId); 152 UserManager userManager = mContext.getSystemService(UserManager.class); 153 if (userHandle == null || userManager == null) return false; 154 return userManager.isManagedProfile(userHandle.getIdentifier()); 155 } 156 157 /** Returns data network type of current subscription. */ getDataNetworkType()158 private int getDataNetworkType() { 159 return mTelephonyManager.getDataNetworkType(); 160 } 161 162 /** Returns incoming mms result. */ getIncomingMmsResult(int result)163 private int getIncomingMmsResult(int result) { 164 switch (result) { 165 case SmsManager.MMS_ERROR_UNSPECIFIED: 166 // SmsManager.MMS_ERROR_UNSPECIFIED(1) -> MMS_RESULT_ERROR_UNSPECIFIED(0) 167 return INCOMING_MMS__RESULT__MMS_RESULT_ERROR_UNSPECIFIED; 168 case Activity.RESULT_OK: 169 // Activity.RESULT_OK -> MMS_RESULT_SUCCESS(1) 170 return INCOMING_MMS__RESULT__MMS_RESULT_SUCCESS; 171 default: 172 // Int value of other SmsManager.MMS_ERROR matches MMS_RESULT_ERROR 173 return result; 174 } 175 } 176 177 /** Returns outgoing mms result. */ getOutgoingMmsResult(int result)178 private int getOutgoingMmsResult(int result) { 179 switch (result) { 180 case SmsManager.MMS_ERROR_UNSPECIFIED: 181 // SmsManager.MMS_ERROR_UNSPECIFIED(1) -> MMS_RESULT_ERROR_UNSPECIFIED(0) 182 return OUTGOING_MMS__RESULT__MMS_RESULT_ERROR_UNSPECIFIED; 183 case Activity.RESULT_OK: 184 // Activity.RESULT_OK -> MMS_RESULT_SUCCESS(1) 185 return OUTGOING_MMS__RESULT__MMS_RESULT_SUCCESS; 186 default: 187 // Int value of other SmsManager.MMS_ERROR matches MMS_RESULT_ERROR 188 return result; 189 } 190 } 191 192 /** Returns data network roaming type of current subscription. */ getDataRoamingType()193 private int getDataRoamingType() { 194 ServiceState serviceState = mTelephonyManager.getServiceState(); 195 return (serviceState != null) ? serviceState.getDataRoamingType() : 196 ServiceState.ROAMING_TYPE_NOT_ROAMING; 197 } 198 199 /** Returns slot index associated with the subscription. */ getSlotIndex()200 private int getSlotIndex() { 201 return SubscriptionManager.getSlotIndex(mSubId); 202 } 203 204 /** Returns whether the device has multiple active SIM profiles. */ getIsMultiSim()205 private boolean getIsMultiSim() { 206 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 207 if(subManager == null) { 208 return false; 209 } 210 if (Flags.workProfileApiSplit()) { 211 subManager = subManager.createForAllUserProfiles(); 212 } 213 List<SubscriptionInfo> activeSubscriptionInfo = subManager.getActiveSubscriptionInfoList(); 214 return (activeSubscriptionInfo.size() > 1); 215 } 216 217 /** Returns if current subscription is embedded subscription. */ getIsEuicc()218 private boolean getIsEuicc() { 219 List<UiccCardInfo> uiccCardInfoList = mTelephonyManager.getUiccCardsInfo(); 220 for (UiccCardInfo card : uiccCardInfoList) { 221 if (card.getPhysicalSlotIndex() == getSlotIndex()) { 222 return card.isEuicc(); 223 } 224 } 225 return false; 226 } 227 228 /** Returns carrier id of the current subscription used by MMS. */ getSimCarrierId()229 private int getSimCarrierId() { 230 return mTelephonyManager.getSimCarrierId(); 231 } 232 233 /** Returns if the MMS was originated from the default MMS application. */ isDefaultMmsApp()234 private boolean isDefaultMmsApp() { 235 UserHandle userHandle = null; 236 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 237 if ((subManager != null) && (subManager.isActiveSubscriptionId(mSubId))) { 238 userHandle = subManager.getSubscriptionUserHandle(mSubId); 239 } 240 return SmsApplication.isDefaultMmsApplicationAsUser(mContext, mCallingPkg, userHandle); 241 } 242 243 /** Determines whether device is non-terrestrial network or not. */ isUsingNonTerrestrialNetwork()244 private boolean isUsingNonTerrestrialNetwork() { 245 ServiceState ss = mTelephonyManager.getServiceState(); 246 if (ss != null) { 247 return ss.isUsingNonTerrestrialNetwork(); 248 } else { 249 Log.e(TAG, "isUsingNonTerrestrialNetwork(): ServiceState is null"); 250 } 251 return false; 252 } 253 254 /** Determines whether the subscription is in carrier roaming NB-IoT NTN or not. */ 255 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) isNbIotNtn(int subId)256 public boolean isNbIotNtn(int subId) { 257 Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 258 if (phone == null) { 259 Log.e(TAG, "isNbIotNtn(): phone is null"); 260 return false; 261 } 262 263 SatelliteController satelliteController = SatelliteController.getInstance(); 264 if (satelliteController == null) { 265 Log.e(TAG, "isNbIotNtn(): satelliteController is null"); 266 return false; 267 } 268 269 return satelliteController.isInCarrierRoamingNbIotNtn(phone); 270 } 271 272 /** 273 * Returns the interval in milliseconds between sending/receiving MMS message and current time. 274 * Calculates the time taken to send message to the network 275 * or download message from the network. 276 */ getInterval()277 private long getInterval() { 278 return (SystemClock.elapsedRealtime() - mTimestamp); 279 } 280 } 281