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