Loading proto/src/persist_atoms.proto +46 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ option java_outer_classname = "PersistAtomsProto"; // Holds atoms to store on persist storage in case of power cycle or process crash. // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation. // Next id: 17 // Next id: 21 message PersistAtoms { /* Aggregated RAT usage during the call. */ repeated VoiceCallRatUsage voice_call_rat_usage = 1; Loading Loading @@ -72,6 +72,18 @@ message PersistAtoms { /* Timestamp of last cellular_data_service_switch pull. */ optional int64 cellular_data_service_switch_pull_timestamp_millis = 16; /* List of IMS registration terminations. */ repeated ImsRegistrationTermination ims_registration_termination = 17; /* Timestamp of last ims_registration_termination pull. */ optional int64 ims_registration_termination_pull_timestamp_millis = 18; /* Durations of IMS registrations and capabilities. */ repeated ImsRegistrationStats ims_registration_stats = 19; /* Timestamp of last ims_registration_stats pull. */ optional int64 ims_registration_stats_pull_timestamp_millis = 20; } // The canonical versions of the following enums live in: Loading Loading @@ -212,3 +224,36 @@ message CellularDataServiceSwitch { // Internal use only optional int64 last_used_millis = 10001; } message ImsRegistrationTermination { optional int32 carrier_id = 1; optional bool is_multi_sim = 2; optional int32 rat_at_end = 3; optional bool setup_failed = 4; optional int32 reason_code = 5; optional int32 extra_code = 6; optional string extra_message = 7; optional int32 count = 8; // Internal use only optional int64 last_used_millis = 10001; } message ImsRegistrationStats { optional int32 carrier_id = 1; optional int32 sim_slot_index = 2; optional int32 rat = 3; // Durations need to be rounded when pulled optional int64 registered_millis = 4; optional int64 voice_capable_millis = 5; optional int64 voice_available_millis = 6; optional int64 sms_capable_millis = 7; optional int64 sms_available_millis = 8; optional int64 video_capable_millis = 9; optional int64 video_available_millis = 10; optional int64 ut_capable_millis = 11; optional int64 ut_available_millis = 12; // Internal use only optional int64 last_used_millis = 10001; } src/java/com/android/internal/telephony/imsphone/ImsPhone.java +18 −1 Original line number Diff line number Diff line Loading @@ -106,8 +106,8 @@ import com.android.internal.telephony.TelephonyComponentFactory; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.dataconnection.TransportManager; import com.android.internal.telephony.emergency.EmergencyNumberTracker; import com.android.internal.telephony.gsm.GsmMmiCode; import com.android.internal.telephony.gsm.SuppServiceNotification; import com.android.internal.telephony.metrics.ImsStats; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.metrics.VoiceCallSessionStats; import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; Loading Loading @@ -262,6 +262,8 @@ public class ImsPhone extends ImsPhoneBase { // List of Registrants to send supplementary service notifications to. private RegistrantList mSsnRegistrants = new RegistrantList(); private ImsStats mImsStats; // A runnable which is used to automatically exit from Ecm after a period of time. private Runnable mExitEcmRunnable = new Runnable() { @Override Loading Loading @@ -404,6 +406,7 @@ public class ImsPhone extends ImsPhoneBase { mDefaultPhone = defaultPhone; mImsManagerFactory = imsManagerFactory; mImsStats = new ImsStats(this); // The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the // ImsPhoneCallTracker uses a thread to spool up the ImsManager. Part of this involves // setting the multiendpoint listener on the external call tracker. So we need to ensure Loading Loading @@ -2371,6 +2374,7 @@ public class ImsPhone extends ImsPhoneBase { + AccessNetworkConstants.transportTypeToString(imsRadioTech)); setServiceState(ServiceState.STATE_IN_SERVICE); mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null); mImsStats.onImsRegistered(imsRadioTech); } @Override Loading @@ -2384,6 +2388,7 @@ public class ImsPhone extends ImsPhoneBase { setServiceState(ServiceState.STATE_OUT_OF_SERVICE); mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING, null); mImsStats.onImsRegistering(imsRadioTech); } @Override Loading @@ -2394,6 +2399,7 @@ public class ImsPhone extends ImsPhoneBase { processDisconnectReason(imsReasonInfo); mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED, imsReasonInfo); mImsStats.onImsUnregistered(imsReasonInfo); } @Override Loading Loading @@ -2431,6 +2437,17 @@ public class ImsPhone extends ImsPhoneBase { return mDefaultPhone.getVoiceCallSessionStats(); } /** Returns the {@link ImsStats} for this IMS phone. */ public ImsStats getImsStats() { return mImsStats; } /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */ @VisibleForTesting public void setImsStats(ImsStats imsStats) { mImsStats = imsStats; } public boolean hasAliveCall() { return (getForegroundCall().getState() != Call.State.IDLE || getBackgroundCall().getState() != Call.State.IDLE); Loading src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +4 −2 Original line number Diff line number Diff line Loading @@ -3780,6 +3780,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { @Override public void onSetFeatureResponse(int feature, int network, int value, int status) { mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value); mPhone.getImsStats().onSetFeatureResponse(feature, network, value); } @Override Loading Loading @@ -4892,8 +4893,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { mPhone.onFeatureCapabilityChanged(); mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(), mMmTelCapabilities); int regTech = getImsRegistrationTech(); mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), regTech, mMmTelCapabilities); mPhone.getImsStats().onImsCapabilitiesChanged(regTech, mMmTelCapabilities); } @VisibleForTesting Loading src/java/com/android/internal/telephony/metrics/ImsStats.java 0 → 100644 +322 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.metrics; import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED; import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED; import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE; import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN; import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE; import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import android.annotation.Nullable; import android.os.SystemClock; import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.TransportType; import android.telephony.Annotation.NetworkType; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.RegistrationManager.ImsRegistrationState; import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities; import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability; import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; import com.android.telephony.Rlog; /** Tracks IMS registration metrics for each phone. */ public class ImsStats { private static final String TAG = ImsStats.class.getSimpleName(); /** * Minimal duration of the registration state. * * <p>Registration state (including changes in capable/available features) with duration shorter * than this will be ignored as they are considered transient states. */ private static final long MIN_REGISTRATION_DURATION_MILLIS = 1L * SECOND_IN_MILLIS; /** * Maximum length of the extra message in the termination reason. * * <p>If the extra message is longer than this length, it will be truncated. */ private static final int MAX_EXTRA_MESSAGE_LENGTH = 128; private final ImsPhone mPhone; private final PersistAtomsStorage mStorage; @ImsRegistrationState private int mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; private long mLastTimestamp; @Nullable private ImsRegistrationStats mLastRegistrationStats; // Available features are those reported by ImsService to be available for use. private MmTelCapabilities mLastAvailableFeatures = new MmTelCapabilities(); // Capable features (enabled by device/carrier). Theses are available before IMS is registered // and not necessarily updated when RAT changes. private final MmTelCapabilities mLastWwanCapableFeatures = new MmTelCapabilities(); private final MmTelCapabilities mLastWlanCapableFeatures = new MmTelCapabilities(); public ImsStats(ImsPhone phone) { mPhone = phone; mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage(); } /** * Finalizes the durations of the current IMS registration stats segment. * * <p>This method is also invoked whenever the registration state, feature capability, or * feature availability changes. */ public synchronized void conclude() { long now = getTimeMillis(); // Currently not tracking time spent on registering. if (mLastRegistrationState == REGISTRATION_STATE_REGISTERED) { ImsRegistrationStats stats = copyOf(mLastRegistrationStats); long duration = now - mLastTimestamp; if (duration < MIN_REGISTRATION_DURATION_MILLIS) { logw("conclude: discarding transient stats, duration=%d", duration); } else { stats.registeredMillis = duration; stats.voiceAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0; stats.videoAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0; stats.utAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0; stats.smsAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0; MmTelCapabilities lastCapableFeatures = stats.rat == TelephonyManager.NETWORK_TYPE_IWLAN ? mLastWlanCapableFeatures : mLastWwanCapableFeatures; stats.voiceCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0; stats.videoCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0; stats.utCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0; stats.smsCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0; mStorage.addImsRegistrationStats(stats); } } mLastTimestamp = now; } /** Updates the stats when registered features changed. */ public synchronized void onImsCapabilitiesChanged( @ImsRegistrationTech int radioTech, MmTelCapabilities capabilities) { conclude(); if (mLastRegistrationStats != null) { mLastRegistrationStats.rat = convertRegistrationTechToNetworkType(radioTech); } mLastAvailableFeatures = capabilities; } /** Updates the stats when capable features changed. */ public synchronized void onSetFeatureResponse( @MmTelCapability int feature, @ImsRegistrationTech int network, int value) { MmTelCapabilities lastCapableFeatures = getLastCapableFeaturesForTech(network); if (lastCapableFeatures != null) { conclude(); if (value == ProvisioningManager.PROVISIONING_VALUE_ENABLED) { lastCapableFeatures.addCapabilities(feature); } else { lastCapableFeatures.removeCapabilities(feature); } } } /** Updates the stats when IMS registration is progressing. */ public synchronized void onImsRegistering(@TransportType int imsRadioTech) { conclude(); mLastRegistrationStats = getDefaultImsRegistrationStats(); mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech); mLastRegistrationState = REGISTRATION_STATE_REGISTERING; } /** Updates the stats when IMS registration succeeds. */ public synchronized void onImsRegistered(@TransportType int imsRadioTech) { conclude(); // NOTE: mLastRegistrationStats can be null (no registering phase). if (mLastRegistrationStats == null) { mLastRegistrationStats = getDefaultImsRegistrationStats(); } mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech); mLastRegistrationState = REGISTRATION_STATE_REGISTERED; } /** Updates the stats and generates a termination atom when IMS registration fails/ends. */ public synchronized void onImsUnregistered(ImsReasonInfo reasonInfo) { conclude(); // Generate end reason atom. // NOTE: mLastRegistrationStats can be null (no registering phase). ImsRegistrationTermination termination = new ImsRegistrationTermination(); if (mLastRegistrationStats != null) { termination.carrierId = mLastRegistrationStats.carrierId; termination.ratAtEnd = getRatAtEnd(mLastRegistrationStats.rat); } else { termination.carrierId = mPhone.getDefaultPhone().getCarrierId(); // We cannot tell whether the registration was intended for WWAN or WLAN termination.ratAtEnd = TelephonyManager.NETWORK_TYPE_UNKNOWN; } termination.isMultiSim = SimSlotState.isMultiSim(); termination.setupFailed = (mLastRegistrationState != REGISTRATION_STATE_REGISTERED); termination.reasonCode = reasonInfo.getCode(); termination.extraCode = reasonInfo.getExtraCode(); termination.extraMessage = sanitizeExtraMessage(reasonInfo.getExtraMessage()); termination.count = 1; mStorage.addImsRegistrationTermination(termination); // Reset state to unregistered. mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; mLastRegistrationStats = null; mLastAvailableFeatures = new MmTelCapabilities(); } @NetworkType private int getRatAtEnd(@NetworkType int lastStateRat) { return lastStateRat == TelephonyManager.NETWORK_TYPE_IWLAN ? lastStateRat : getWwanPsRat(); } @NetworkType private int convertTransportTypeToNetworkType(@TransportType int transportType) { switch (transportType) { case AccessNetworkConstants.TRANSPORT_TYPE_WWAN: return getWwanPsRat(); case AccessNetworkConstants.TRANSPORT_TYPE_WLAN: return TelephonyManager.NETWORK_TYPE_IWLAN; default: return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } @NetworkType private int getWwanPsRat() { ServiceState state = mPhone.getServiceStateTracker().getServiceState(); final NetworkRegistrationInfo wwanRegInfo = state.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); return wwanRegInfo != null ? wwanRegInfo.getAccessNetworkTechnology() : TelephonyManager.NETWORK_TYPE_UNKNOWN; } private ImsRegistrationStats getDefaultImsRegistrationStats() { Phone phone = mPhone.getDefaultPhone(); ImsRegistrationStats stats = new ImsRegistrationStats(); stats.carrierId = phone.getCarrierId(); stats.simSlotIndex = phone.getPhoneId(); return stats; } @Nullable private MmTelCapabilities getLastCapableFeaturesForTech(@ImsRegistrationTech int radioTech) { switch (radioTech) { case REGISTRATION_TECH_NONE: return null; case REGISTRATION_TECH_IWLAN: return mLastWlanCapableFeatures; default: return mLastWwanCapableFeatures; } } @NetworkType private int convertRegistrationTechToNetworkType(@ImsRegistrationTech int radioTech) { switch (radioTech) { case REGISTRATION_TECH_NONE: return TelephonyManager.NETWORK_TYPE_UNKNOWN; case REGISTRATION_TECH_LTE: return TelephonyManager.NETWORK_TYPE_LTE; case REGISTRATION_TECH_IWLAN: return TelephonyManager.NETWORK_TYPE_IWLAN; default: // TODO: for VoNR, need to add registration tech to ImsRegistrationImplBase loge("convertRegistrationTechToNetworkType: unknown radio tech %d", radioTech); return getWwanPsRat(); } } private static ImsRegistrationStats copyOf(ImsRegistrationStats source) { ImsRegistrationStats dest = new ImsRegistrationStats(); dest.carrierId = source.carrierId; dest.simSlotIndex = source.simSlotIndex; dest.rat = source.rat; dest.registeredMillis = source.registeredMillis; dest.voiceCapableMillis = source.voiceCapableMillis; dest.voiceAvailableMillis = source.voiceAvailableMillis; dest.smsCapableMillis = source.smsCapableMillis; dest.smsAvailableMillis = source.smsAvailableMillis; dest.videoCapableMillis = source.videoCapableMillis; dest.videoAvailableMillis = source.videoAvailableMillis; dest.utCapableMillis = source.utCapableMillis; dest.utAvailableMillis = source.utAvailableMillis; return dest; } @VisibleForTesting protected long getTimeMillis() { return SystemClock.elapsedRealtime(); } private static String sanitizeExtraMessage(@Nullable String str) { if (str == null) { return ""; } return str.length() > MAX_EXTRA_MESSAGE_LENGTH ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH) : str; } private void logw(String format, Object... args) { Rlog.w(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args)); } private void loge(String format, Object... args) { Rlog.e(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args)); } } src/java/com/android/internal/telephony/metrics/MetricsCollector.java +80 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
proto/src/persist_atoms.proto +46 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ option java_outer_classname = "PersistAtomsProto"; // Holds atoms to store on persist storage in case of power cycle or process crash. // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation. // Next id: 17 // Next id: 21 message PersistAtoms { /* Aggregated RAT usage during the call. */ repeated VoiceCallRatUsage voice_call_rat_usage = 1; Loading Loading @@ -72,6 +72,18 @@ message PersistAtoms { /* Timestamp of last cellular_data_service_switch pull. */ optional int64 cellular_data_service_switch_pull_timestamp_millis = 16; /* List of IMS registration terminations. */ repeated ImsRegistrationTermination ims_registration_termination = 17; /* Timestamp of last ims_registration_termination pull. */ optional int64 ims_registration_termination_pull_timestamp_millis = 18; /* Durations of IMS registrations and capabilities. */ repeated ImsRegistrationStats ims_registration_stats = 19; /* Timestamp of last ims_registration_stats pull. */ optional int64 ims_registration_stats_pull_timestamp_millis = 20; } // The canonical versions of the following enums live in: Loading Loading @@ -212,3 +224,36 @@ message CellularDataServiceSwitch { // Internal use only optional int64 last_used_millis = 10001; } message ImsRegistrationTermination { optional int32 carrier_id = 1; optional bool is_multi_sim = 2; optional int32 rat_at_end = 3; optional bool setup_failed = 4; optional int32 reason_code = 5; optional int32 extra_code = 6; optional string extra_message = 7; optional int32 count = 8; // Internal use only optional int64 last_used_millis = 10001; } message ImsRegistrationStats { optional int32 carrier_id = 1; optional int32 sim_slot_index = 2; optional int32 rat = 3; // Durations need to be rounded when pulled optional int64 registered_millis = 4; optional int64 voice_capable_millis = 5; optional int64 voice_available_millis = 6; optional int64 sms_capable_millis = 7; optional int64 sms_available_millis = 8; optional int64 video_capable_millis = 9; optional int64 video_available_millis = 10; optional int64 ut_capable_millis = 11; optional int64 ut_available_millis = 12; // Internal use only optional int64 last_used_millis = 10001; }
src/java/com/android/internal/telephony/imsphone/ImsPhone.java +18 −1 Original line number Diff line number Diff line Loading @@ -106,8 +106,8 @@ import com.android.internal.telephony.TelephonyComponentFactory; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.dataconnection.TransportManager; import com.android.internal.telephony.emergency.EmergencyNumberTracker; import com.android.internal.telephony.gsm.GsmMmiCode; import com.android.internal.telephony.gsm.SuppServiceNotification; import com.android.internal.telephony.metrics.ImsStats; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.metrics.VoiceCallSessionStats; import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; Loading Loading @@ -262,6 +262,8 @@ public class ImsPhone extends ImsPhoneBase { // List of Registrants to send supplementary service notifications to. private RegistrantList mSsnRegistrants = new RegistrantList(); private ImsStats mImsStats; // A runnable which is used to automatically exit from Ecm after a period of time. private Runnable mExitEcmRunnable = new Runnable() { @Override Loading Loading @@ -404,6 +406,7 @@ public class ImsPhone extends ImsPhoneBase { mDefaultPhone = defaultPhone; mImsManagerFactory = imsManagerFactory; mImsStats = new ImsStats(this); // The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the // ImsPhoneCallTracker uses a thread to spool up the ImsManager. Part of this involves // setting the multiendpoint listener on the external call tracker. So we need to ensure Loading Loading @@ -2371,6 +2374,7 @@ public class ImsPhone extends ImsPhoneBase { + AccessNetworkConstants.transportTypeToString(imsRadioTech)); setServiceState(ServiceState.STATE_IN_SERVICE); mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null); mImsStats.onImsRegistered(imsRadioTech); } @Override Loading @@ -2384,6 +2388,7 @@ public class ImsPhone extends ImsPhoneBase { setServiceState(ServiceState.STATE_OUT_OF_SERVICE); mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING, null); mImsStats.onImsRegistering(imsRadioTech); } @Override Loading @@ -2394,6 +2399,7 @@ public class ImsPhone extends ImsPhoneBase { processDisconnectReason(imsReasonInfo); mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED, imsReasonInfo); mImsStats.onImsUnregistered(imsReasonInfo); } @Override Loading Loading @@ -2431,6 +2437,17 @@ public class ImsPhone extends ImsPhoneBase { return mDefaultPhone.getVoiceCallSessionStats(); } /** Returns the {@link ImsStats} for this IMS phone. */ public ImsStats getImsStats() { return mImsStats; } /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */ @VisibleForTesting public void setImsStats(ImsStats imsStats) { mImsStats = imsStats; } public boolean hasAliveCall() { return (getForegroundCall().getState() != Call.State.IDLE || getBackgroundCall().getState() != Call.State.IDLE); Loading
src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +4 −2 Original line number Diff line number Diff line Loading @@ -3780,6 +3780,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { @Override public void onSetFeatureResponse(int feature, int network, int value, int status) { mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value); mPhone.getImsStats().onSetFeatureResponse(feature, network, value); } @Override Loading Loading @@ -4892,8 +4893,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { mPhone.onFeatureCapabilityChanged(); mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(), mMmTelCapabilities); int regTech = getImsRegistrationTech(); mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), regTech, mMmTelCapabilities); mPhone.getImsStats().onImsCapabilitiesChanged(regTech, mMmTelCapabilities); } @VisibleForTesting Loading
src/java/com/android/internal/telephony/metrics/ImsStats.java 0 → 100644 +322 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.metrics; import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED; import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED; import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO; import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE; import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN; import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE; import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import android.annotation.Nullable; import android.os.SystemClock; import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.TransportType; import android.telephony.Annotation.NetworkType; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.RegistrationManager.ImsRegistrationState; import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities; import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability; import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; import com.android.telephony.Rlog; /** Tracks IMS registration metrics for each phone. */ public class ImsStats { private static final String TAG = ImsStats.class.getSimpleName(); /** * Minimal duration of the registration state. * * <p>Registration state (including changes in capable/available features) with duration shorter * than this will be ignored as they are considered transient states. */ private static final long MIN_REGISTRATION_DURATION_MILLIS = 1L * SECOND_IN_MILLIS; /** * Maximum length of the extra message in the termination reason. * * <p>If the extra message is longer than this length, it will be truncated. */ private static final int MAX_EXTRA_MESSAGE_LENGTH = 128; private final ImsPhone mPhone; private final PersistAtomsStorage mStorage; @ImsRegistrationState private int mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; private long mLastTimestamp; @Nullable private ImsRegistrationStats mLastRegistrationStats; // Available features are those reported by ImsService to be available for use. private MmTelCapabilities mLastAvailableFeatures = new MmTelCapabilities(); // Capable features (enabled by device/carrier). Theses are available before IMS is registered // and not necessarily updated when RAT changes. private final MmTelCapabilities mLastWwanCapableFeatures = new MmTelCapabilities(); private final MmTelCapabilities mLastWlanCapableFeatures = new MmTelCapabilities(); public ImsStats(ImsPhone phone) { mPhone = phone; mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage(); } /** * Finalizes the durations of the current IMS registration stats segment. * * <p>This method is also invoked whenever the registration state, feature capability, or * feature availability changes. */ public synchronized void conclude() { long now = getTimeMillis(); // Currently not tracking time spent on registering. if (mLastRegistrationState == REGISTRATION_STATE_REGISTERED) { ImsRegistrationStats stats = copyOf(mLastRegistrationStats); long duration = now - mLastTimestamp; if (duration < MIN_REGISTRATION_DURATION_MILLIS) { logw("conclude: discarding transient stats, duration=%d", duration); } else { stats.registeredMillis = duration; stats.voiceAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0; stats.videoAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0; stats.utAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0; stats.smsAvailableMillis = mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0; MmTelCapabilities lastCapableFeatures = stats.rat == TelephonyManager.NETWORK_TYPE_IWLAN ? mLastWlanCapableFeatures : mLastWwanCapableFeatures; stats.voiceCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0; stats.videoCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0; stats.utCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0; stats.smsCapableMillis = lastCapableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0; mStorage.addImsRegistrationStats(stats); } } mLastTimestamp = now; } /** Updates the stats when registered features changed. */ public synchronized void onImsCapabilitiesChanged( @ImsRegistrationTech int radioTech, MmTelCapabilities capabilities) { conclude(); if (mLastRegistrationStats != null) { mLastRegistrationStats.rat = convertRegistrationTechToNetworkType(radioTech); } mLastAvailableFeatures = capabilities; } /** Updates the stats when capable features changed. */ public synchronized void onSetFeatureResponse( @MmTelCapability int feature, @ImsRegistrationTech int network, int value) { MmTelCapabilities lastCapableFeatures = getLastCapableFeaturesForTech(network); if (lastCapableFeatures != null) { conclude(); if (value == ProvisioningManager.PROVISIONING_VALUE_ENABLED) { lastCapableFeatures.addCapabilities(feature); } else { lastCapableFeatures.removeCapabilities(feature); } } } /** Updates the stats when IMS registration is progressing. */ public synchronized void onImsRegistering(@TransportType int imsRadioTech) { conclude(); mLastRegistrationStats = getDefaultImsRegistrationStats(); mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech); mLastRegistrationState = REGISTRATION_STATE_REGISTERING; } /** Updates the stats when IMS registration succeeds. */ public synchronized void onImsRegistered(@TransportType int imsRadioTech) { conclude(); // NOTE: mLastRegistrationStats can be null (no registering phase). if (mLastRegistrationStats == null) { mLastRegistrationStats = getDefaultImsRegistrationStats(); } mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech); mLastRegistrationState = REGISTRATION_STATE_REGISTERED; } /** Updates the stats and generates a termination atom when IMS registration fails/ends. */ public synchronized void onImsUnregistered(ImsReasonInfo reasonInfo) { conclude(); // Generate end reason atom. // NOTE: mLastRegistrationStats can be null (no registering phase). ImsRegistrationTermination termination = new ImsRegistrationTermination(); if (mLastRegistrationStats != null) { termination.carrierId = mLastRegistrationStats.carrierId; termination.ratAtEnd = getRatAtEnd(mLastRegistrationStats.rat); } else { termination.carrierId = mPhone.getDefaultPhone().getCarrierId(); // We cannot tell whether the registration was intended for WWAN or WLAN termination.ratAtEnd = TelephonyManager.NETWORK_TYPE_UNKNOWN; } termination.isMultiSim = SimSlotState.isMultiSim(); termination.setupFailed = (mLastRegistrationState != REGISTRATION_STATE_REGISTERED); termination.reasonCode = reasonInfo.getCode(); termination.extraCode = reasonInfo.getExtraCode(); termination.extraMessage = sanitizeExtraMessage(reasonInfo.getExtraMessage()); termination.count = 1; mStorage.addImsRegistrationTermination(termination); // Reset state to unregistered. mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; mLastRegistrationStats = null; mLastAvailableFeatures = new MmTelCapabilities(); } @NetworkType private int getRatAtEnd(@NetworkType int lastStateRat) { return lastStateRat == TelephonyManager.NETWORK_TYPE_IWLAN ? lastStateRat : getWwanPsRat(); } @NetworkType private int convertTransportTypeToNetworkType(@TransportType int transportType) { switch (transportType) { case AccessNetworkConstants.TRANSPORT_TYPE_WWAN: return getWwanPsRat(); case AccessNetworkConstants.TRANSPORT_TYPE_WLAN: return TelephonyManager.NETWORK_TYPE_IWLAN; default: return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } @NetworkType private int getWwanPsRat() { ServiceState state = mPhone.getServiceStateTracker().getServiceState(); final NetworkRegistrationInfo wwanRegInfo = state.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); return wwanRegInfo != null ? wwanRegInfo.getAccessNetworkTechnology() : TelephonyManager.NETWORK_TYPE_UNKNOWN; } private ImsRegistrationStats getDefaultImsRegistrationStats() { Phone phone = mPhone.getDefaultPhone(); ImsRegistrationStats stats = new ImsRegistrationStats(); stats.carrierId = phone.getCarrierId(); stats.simSlotIndex = phone.getPhoneId(); return stats; } @Nullable private MmTelCapabilities getLastCapableFeaturesForTech(@ImsRegistrationTech int radioTech) { switch (radioTech) { case REGISTRATION_TECH_NONE: return null; case REGISTRATION_TECH_IWLAN: return mLastWlanCapableFeatures; default: return mLastWwanCapableFeatures; } } @NetworkType private int convertRegistrationTechToNetworkType(@ImsRegistrationTech int radioTech) { switch (radioTech) { case REGISTRATION_TECH_NONE: return TelephonyManager.NETWORK_TYPE_UNKNOWN; case REGISTRATION_TECH_LTE: return TelephonyManager.NETWORK_TYPE_LTE; case REGISTRATION_TECH_IWLAN: return TelephonyManager.NETWORK_TYPE_IWLAN; default: // TODO: for VoNR, need to add registration tech to ImsRegistrationImplBase loge("convertRegistrationTechToNetworkType: unknown radio tech %d", radioTech); return getWwanPsRat(); } } private static ImsRegistrationStats copyOf(ImsRegistrationStats source) { ImsRegistrationStats dest = new ImsRegistrationStats(); dest.carrierId = source.carrierId; dest.simSlotIndex = source.simSlotIndex; dest.rat = source.rat; dest.registeredMillis = source.registeredMillis; dest.voiceCapableMillis = source.voiceCapableMillis; dest.voiceAvailableMillis = source.voiceAvailableMillis; dest.smsCapableMillis = source.smsCapableMillis; dest.smsAvailableMillis = source.smsAvailableMillis; dest.videoCapableMillis = source.videoCapableMillis; dest.videoAvailableMillis = source.videoAvailableMillis; dest.utCapableMillis = source.utCapableMillis; dest.utAvailableMillis = source.utAvailableMillis; return dest; } @VisibleForTesting protected long getTimeMillis() { return SystemClock.elapsedRealtime(); } private static String sanitizeExtraMessage(@Nullable String str) { if (str == null) { return ""; } return str.length() > MAX_EXTRA_MESSAGE_LENGTH ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH) : str; } private void logw(String format, Object... args) { Rlog.w(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args)); } private void loge(String format, Object... args) { Rlog.e(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args)); } }
src/java/com/android/internal/telephony/metrics/MetricsCollector.java +80 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes