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