• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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