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

Commit a5ceec6b authored by Chi Zhang's avatar Chi Zhang Committed by Automerger Merge Worker
Browse files

Merge "Filter IMS extra message to remove IDs." am: 0131aef1

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/1578883

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ib72ed4b0e4cc2e2c3559ecdb510f265091657afa
parents 0c7656bb 0131aef1
Loading
Loading
Loading
Loading
+134 −2
Original line number 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_NONE;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static android.util.Patterns.EMAIL_ADDRESS;

import android.annotation.Nullable;
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.telephony.Rlog;

import java.util.regex.Pattern;

/** Tracks IMS registration metrics for each phone. */
public class ImsStats {
    private static final String TAG = ImsStats.class.getSimpleName();
@@ -70,6 +73,123 @@ public class ImsStats {
     */
    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 PersistAtomsStorage mStorage;

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

@@ -303,10 +423,22 @@ public class ImsStats {
        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) {
            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
                ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH)
                : str;
+1 −1
Original line number Diff line number Diff line
@@ -533,7 +533,7 @@ public class VoiceCallSessionStats {
        proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
        proto.disconnectReasonCode = reasonInfo.mCode;
        proto.disconnectExtraCode = reasonInfo.mExtraCode;
        proto.disconnectExtraMessage = reasonInfo.mExtraMessage;
        proto.disconnectExtraMessage = ImsStats.filterExtraMessage(reasonInfo.mExtraMessage);
        finishCall(id);
    }

+124 −2
Original line number 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.UiccSlot;

import com.google.common.collect.ImmutableMap;

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

import java.util.Map;

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