Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0131aef1 authored by Chi Zhang's avatar Chi Zhang Committed by Gerrit Code Review
Browse files

Merge "Filter IMS extra message to remove IDs."

parents 08e53879 476090f9
Loading
Loading
Loading
Loading
+134 −2
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TE
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static android.util.Patterns.EMAIL_ADDRESS;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.os.SystemClock;
@@ -51,6 +52,8 @@ import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStat
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.telephony.Rlog;
import com.android.telephony.Rlog;


import java.util.regex.Pattern;

/** Tracks IMS registration metrics for each phone. */
/** Tracks IMS registration metrics for each phone. */
public class ImsStats {
public class ImsStats {
    private static final String TAG = ImsStats.class.getSimpleName();
    private static final String TAG = ImsStats.class.getSimpleName();
@@ -70,6 +73,123 @@ public class ImsStats {
     */
     */
    private static final int MAX_EXTRA_MESSAGE_LENGTH = 128;
    private static final int MAX_EXTRA_MESSAGE_LENGTH = 128;


    /** Pattern used to match UUIDs in IMS extra messages for filtering. */
    private static final Pattern PATTERN_UUID =
            Pattern.compile(
                    "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}");

    /** Replacement for UUIDs. */
    private static final String REPLACEMENT_UUID = "<UUID_REDACTED>";

    /**
     * Pattern used to match URI (e.g. sip, tel) in IMS extra messages for filtering.
     *
     * <p>NOTE: this simple pattern aims to catch the most common URI schemes. It is not meant to be
     * RFC-complaint.
     */
    private static final Pattern PATTERN_URI =
            Pattern.compile("([a-zA-Z]{2,}:)" + EMAIL_ADDRESS.pattern());

    /** Replacement for URI. */
    private static final String REPLACEMENT_URI = "$1<REDACTED>";

    /**
     * Pattern used to match IPv4 addresses in IMS extra messages for filtering.
     *
     * <p>This is a copy of {@code android.util.Patterns.IP_ADDRESS}, which is deprecated and might
     * be removed in the future.
     */
    private static final Pattern PATTERN_IPV4 =
            Pattern.compile(
                    "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
                            + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
                            + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
                            + "|[1-9][0-9]|[0-9]))");

    /** Replacement for IPv4 addresses. */
    private static final String REPLACEMENT_IPV4 = "<IPV4_REDACTED>";

    /**
     * Pattern used to match IPv6 addresses in IMS extra messages for filtering.
     *
     * <p>NOTE: this pattern aims to catch the most common IPv6 addresses. It is not meant to be
     * RFC-complaint or free of false positives.
     */
    private static final Pattern PATTERN_IPV6 =
            Pattern.compile(
                    // Full address
                    "([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}"
                            // Abbreviated address, e.g. 2001:4860:4860::8888
                            + "|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1,6}"
                            // Abbreviated network address, e.g. 2607:F8B0::
                            + "|([0-9a-fA-F]{1,4}:){1,7}:"
                            // Abbreviated address, e.g. ::1
                            + "|:(:[0-9a-fA-F]{1,4}){1,7}");

    /** Replacement for IPv6 addresses. */
    private static final String REPLACEMENT_IPV6 = "<IPV6_REDACTED>";

    /**
     * Pattern used to match potential IMEI values in IMS extra messages for filtering.
     *
     * <p>This includes segmented IMEI or IMEI/SV, as well as unsegmented IMEI/SV.
     */
    private static final Pattern PATTERN_IMEI =
            Pattern.compile(
                    "(^|[^0-9])(?:"
                            // IMEI, AABBBBBB-CCCCCC-D format; IMEI/SV, AABBBBBB-CCCCCC-EE format
                            + "[0-9]{8}-[0-9]{6}-[0-9][0-9]?"
                            // IMEI, AA-BBBBBB-CCCCCC-D format; IMEI/SV, AA-BBBBBB-CCCCCC-EE format
                            + "|[0-9]{2}-[0-9]{6}-[0-9]{6}-[0-9][0-9]?"
                            // IMEI/SV, unsegmented
                            + "|[0-9]{16}"
                            + ")($|[^0-9])");

    /** Replacement for IMEI. */
    private static final String REPLACEMENT_IMEI = "$1<IMEI_REDACTED>$2";

    /**
     * Pattern used to match potential unsegmented IMEI/IMSI values in IMS extra messages for
     * filtering.
     */
    private static final Pattern PATTERN_UNSEGMENTED_IMEI_IMSI =
            Pattern.compile("(^|[^0-9])[0-9]{15}($|[^0-9])");

    /** Replacement for unsegmented IMEI/IMSI. */
    private static final String REPLACEMENT_UNSEGMENTED_IMEI_IMSI = "$1<IMEI_IMSI_REDACTED>$2";

    /**
     * Pattern used to match hostnames in IMS extra messages for filtering.
     *
     * <p>This pattern differs from {@link android.util.Patterns.DOMAIN_NAME} in a few ways: it
     * requires the name to have at least 3 segments (shorter names are nearly always public or
     * typos, i.e. missing space after period), does not check the validity of TLDs, and does not
     * support punycodes in TLDs.
     */
    private static final Pattern PATTERN_HOSTNAME =
            Pattern.compile("([0-9a-zA-Z][0-9a-zA-Z_\\-]{0,61}[0-9a-zA-Z]\\.){2,}[a-zA-Z]{2,}");

    /** Replacement for hostnames. */
    private static final String REPLACEMENT_HOSTNAME = "<HOSTNAME_REDACTED>";

    /**
     * Pattern used to match potential IDs in IMS extra messages for filtering.
     *
     * <p>This pattern target numbers that are potential IDs in unknown formats. It should be
     * replaced after all other replacements are done to ensure complete and correct filtering.
     *
     * <p>Specifically, this pattern looks for any number (including hex) that is separated by dots
     * or dashes has at least 6 significant digits, and any unsegmented numbers that has at least 5
     * significant digits.
     */
    private static final Pattern PATTERN_UNKNOWN_ID =
            Pattern.compile(
                    "(^|[^0-9a-fA-F])(([-\\.]?0)*[1-9a-fA-F]([-\\.]?[0-9a-fA-F]){5,}"
                            + "|0*[1-9a-fA-F]([0-9a-fA-F]){4,})");

    /** Replacement for potential IDs. */
    private static final String REPLACEMENT_UNKNOWN_ID = "$1<ID_REDACTED>";

    private final ImsPhone mPhone;
    private final ImsPhone mPhone;
    private final PersistAtomsStorage mStorage;
    private final PersistAtomsStorage mStorage;


@@ -204,7 +324,7 @@ public class ImsStats {
        termination.setupFailed = (mLastRegistrationState != REGISTRATION_STATE_REGISTERED);
        termination.setupFailed = (mLastRegistrationState != REGISTRATION_STATE_REGISTERED);
        termination.reasonCode = reasonInfo.getCode();
        termination.reasonCode = reasonInfo.getCode();
        termination.extraCode = reasonInfo.getExtraCode();
        termination.extraCode = reasonInfo.getExtraCode();
        termination.extraMessage = sanitizeExtraMessage(reasonInfo.getExtraMessage());
        termination.extraMessage = filterExtraMessage(reasonInfo.getExtraMessage());
        termination.count = 1;
        termination.count = 1;
        mStorage.addImsRegistrationTermination(termination);
        mStorage.addImsRegistrationTermination(termination);


@@ -303,10 +423,22 @@ public class ImsStats {
        return SystemClock.elapsedRealtime();
        return SystemClock.elapsedRealtime();
    }
    }


    private static String sanitizeExtraMessage(@Nullable String str) {
    /** Filters IMS extra messages to ensure length limit and remove IDs. */
    public static String filterExtraMessage(@Nullable String str) {
        if (str == null) {
        if (str == null) {
            return "";
            return "";
        }
        }

        str = PATTERN_UUID.matcher(str).replaceAll(REPLACEMENT_UUID);
        str = PATTERN_URI.matcher(str).replaceAll(REPLACEMENT_URI);
        str = PATTERN_HOSTNAME.matcher(str).replaceAll(REPLACEMENT_HOSTNAME);
        str = PATTERN_IPV4.matcher(str).replaceAll(REPLACEMENT_IPV4);
        str = PATTERN_IPV6.matcher(str).replaceAll(REPLACEMENT_IPV6);
        str = PATTERN_IMEI.matcher(str).replaceAll(REPLACEMENT_IMEI);
        str = PATTERN_UNSEGMENTED_IMEI_IMSI.matcher(str)
                .replaceAll(REPLACEMENT_UNSEGMENTED_IMEI_IMSI);
        str = PATTERN_UNKNOWN_ID.matcher(str).replaceAll(REPLACEMENT_UNKNOWN_ID);

        return str.length() > MAX_EXTRA_MESSAGE_LENGTH
        return str.length() > MAX_EXTRA_MESSAGE_LENGTH
                ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH)
                ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH)
                : str;
                : str;
+1 −1
Original line number Original line Diff line number Diff line
@@ -533,7 +533,7 @@ public class VoiceCallSessionStats {
        proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
        proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
        proto.disconnectReasonCode = reasonInfo.mCode;
        proto.disconnectReasonCode = reasonInfo.mCode;
        proto.disconnectExtraCode = reasonInfo.mExtraCode;
        proto.disconnectExtraCode = reasonInfo.mExtraCode;
        proto.disconnectExtraMessage = reasonInfo.mExtraMessage;
        proto.disconnectExtraMessage = ImsStats.filterExtraMessage(reasonInfo.mExtraMessage);
        finishCall(id);
        finishCall(id);
    }
    }


+124 −2
Original line number Original line Diff line number Diff line
@@ -50,12 +50,16 @@ import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTerm
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.uicc.UiccSlot;


import com.google.common.collect.ImmutableMap;

import org.junit.After;
import org.junit.After;
import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mock;


import java.util.Map;

public class ImsStatsTest extends TelephonyTest {
public class ImsStatsTest extends TelephonyTest {
    private static final long START_TIME_MILLIS = 2000L;
    private static final long START_TIME_MILLIS = 2000L;
    private static final int CARRIER1_ID = 1;
    private static final int CARRIER1_ID = 1;
@@ -523,8 +527,8 @@ public class ImsStatsTest extends TelephonyTest {
    public void onImsUnregistered_longMessage() throws Exception {
    public void onImsUnregistered_longMessage() throws Exception {
        String longExtraMessage =
        String longExtraMessage =
                "This message is too long -- it has more than 128 characters: "
                "This message is too long -- it has more than 128 characters: "
                        + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
                        + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
                        + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
                        + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
                        + " This is the end of the message.";
                        + " This is the end of the message.";
        mImsStats.onImsUnregistered(
        mImsStats.onImsUnregistered(
                new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, longExtraMessage));
                new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, longExtraMessage));
@@ -545,6 +549,124 @@ public class ImsStatsTest extends TelephonyTest {
        verifyNoMoreInteractions(mPersistAtomsStorage);
        verifyNoMoreInteractions(mPersistAtomsStorage);
    }
    }


    @Test
    @SmallTest
    public void filterExtraMessage_noNeedToFilter() throws Exception {
        final String[] messages = {
            "Q.850;cause=16",
            "SIP;cause=200",
            "q.850",
            "600",
            "Call ended during conference merge process.",
            "cc_term_noreply_tmr_expired",
            "[e] user triggered",
            "normal end of call",
            "cc_q850_017_user_busy",
            "service unavailable (1:223)",
            "IP Change",
            "rtp-rtcp timeout",
            "0x0000030b",
            "CD-021: ISP Problem",
            "IP;cause=487;text=\"Originator canceled;Canceled(t),iCode=CC_SIP_REQUEST_TERMINATED\"",
            "pt: asr: insufficient_bearer_resources",
            "po: aaa: result_code=0 exp_result_code=5065",
            "a peer released.total external peer:1. allowed:2. clear the remain parties(o),"
                    + "icode=cc_sip_request_timeout"
        };

        for (String message : messages) {
            assertEquals(message, ImsStats.filterExtraMessage(message));
        }
    }

    @Test
    @SmallTest
    public void filterExtraMessage_needToFilter() throws Exception {
        Map<String, String> originalAndExpectedMessages = ImmutableMap.<String, String>builder()
                // UUIDs
                .put(
                        "Q.850;cause=34;text=\"12345678-abcd-ef12-34ab-000000000012;"
                                + "User is busy and currently active on another call.\"",
                        "Q.850;cause=34;text=\"<UUID_REDACTED>;"
                                + "User is busy and currently active on another call.\"")
                .put(
                        "Q.850;cause=34;text=\"12345678-ABCD-EF12-34AB-000000000012;"
                                + "User is busy and currently active on another call.\"",
                        "Q.850;cause=34;text=\"<UUID_REDACTED>;"
                                + "User is busy and currently active on another call.\"")
                // URIs
                .put(
                        "SIP;cause=500;text=\"sip:+1234567890@irps.voip.telefonica.de;user=phone"
                                + " clear the call.;Canceled(t)\"",
                        "SIP;cause=500;text=\"sip:<REDACTED>;user=phone"
                                + " clear the call.;Canceled(t)\"")
                .put(
                        "SIP;cause=500;text=\"SIP:+1234567890@irps.voip.telefonica.de;user=phone"
                                + " clear the call.;Canceled(t)\"",
                        "SIP;cause=500;text=\"SIP:<REDACTED>;user=phone"
                                + " clear the call.;Canceled(t)\"")
                // IP addresses
                .put(
                        "dtls handshake error[timeout][2607:F8B0::1] and client disconnected",
                        "dtls handshake error[timeout][<IPV6_REDACTED>] and client disconnected")
                .put(
                        "dtls handshake error[timeout][2607:f8b0::1] and client disconnected",
                        "dtls handshake error[timeout][<IPV6_REDACTED>] and client disconnected")
                .put(
                        "dtls handshake error 2607:f8b0:1:2:3:4:56:789",
                        "dtls handshake error <IPV6_REDACTED>")
                .put(
                        "dtls handshake error[timeout][8.8.8.8] and client disconnected",
                        "dtls handshake error[timeout][<IPV4_REDACTED>] and client disconnected")
                .put("8.8.8.8 client disconnected", "<IPV4_REDACTED> client disconnected")
                // IMEIs/IMSIs
                .put(
                        "call completed elsewhere by instance 313460000000001",
                        "call completed elsewhere by instance <IMEI_IMSI_REDACTED>")
                .put(
                        "call completed elsewhere by instance 31346000-000000-1",
                        "call completed elsewhere by instance <IMEI_REDACTED>")
                .put(
                        "call completed elsewhere by instance 31-346000-000000-1",
                        "call completed elsewhere by instance <IMEI_REDACTED>")
                .put(
                        "call completed elsewhere by instance 31-346000-000000-12",
                        "call completed elsewhere by instance <IMEI_REDACTED>")
                .put(
                        "399 123.4567.89.ATS.blah.ims.mnc123.mcc456.3gppnetwork.org"
                                + " \"Failure cause code is sip status code.\"",
                        "399 <HOSTNAME_REDACTED> \"Failure cause code is sip status code.\"")
                // Unknown IDs
                .put(
                        "01200.30004.a.560.789.123.0.0.00045.00000006"
                                + " released the session because of netfail by no media",
                        // "123.0.0.0" looks like IPv4
                        "<ID_REDACTED><IPV4_REDACTED><ID_REDACTED>"
                                + " released the session because of netfail by no media")
                .put(
                        "example.cpp,1234,12-300-450-67-89123456:-12345678,"
                                + "tyringtimeout:timer b expired(t)",
                        "example.cpp,1234,<ID_REDACTED>:-<ID_REDACTED>,"
                                + "tyringtimeout:timer b expired(t)")
                .put(
                        "ss120000f123l1234 invite 2xx after cancel rsp has been received",
                        "ss<ID_REDACTED>l1234 invite 2xx after cancel rsp has been received")
                .put(
                        "X.int;reasoncode=0x00000123;add-info=0123.00AB.0001",
                        "X.int;reasoncode=0x00000123;add-info=<ID_REDACTED>")
                .put(
                        "X.int;reasoncode=0x00123abc;add-info=0123.00AB.0001",
                        "X.int;reasoncode=0x<ID_REDACTED>;add-info=<ID_REDACTED>")
                .put(
                        "Cx Unable To Comply 1203045067D8009",
                        "Cx Unable To Comply <ID_REDACTED>")
                .build();

        for (Map.Entry<String, String> entry : originalAndExpectedMessages.entrySet()) {
            assertEquals(entry.getValue(), ImsStats.filterExtraMessage(entry.getKey()));
        }
    }

    @Test
    @Test
    @SmallTest
    @SmallTest
    public void onImsUnregistered_multiSim() throws Exception {
    public void onImsUnregistered_multiSim() throws Exception {