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

Commit 8de15f93 authored by Manish Dungriyal's avatar Manish Dungriyal
Browse files

Log atoms for emergency

Collectig metrics for consolidated emergency numbers updated in
framework and dialed emergency number,

Bug: 233263956
Test: m statsd_testdrive
Change-Id: I5f4c53cbdb4796f57d75b11855093a9d9b384022
parent 4eba968a
Loading
Loading
Loading
Loading
+42 −1
Original line number Diff line number Diff line
@@ -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: 53
// Next id: 58
message PersistAtoms {
    /* Aggregated RAT usage during the call. */
    repeated VoiceCallRatUsage voice_call_rat_usage = 1;
@@ -189,6 +189,12 @@ message PersistAtoms {

    /* Number of time the user toggled the data switch feature since the last collection. */
    optional int32 auto_data_switch_toggle_count = 55;

    /* Consolidated emergency numbers list information. */
    repeated EmergencyNumbersInfo emergency_numbers_info = 56;

    /* Timestamp of last emergency number pull. */
    optional int64 emergency_number_pull_timestamp_millis = 57;
}

// The canonical versions of the following enums live in:
@@ -538,3 +544,38 @@ message OutgoingShortCodeSms {
    optional int32 xml_version = 2;
    optional int32 short_code_sms_count = 3;
}

message EmergencyNumbersInfo {
    enum ServiceCategory {
        EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0;
        EMERGENCY_SERVICE_CATEGORY_POLICE = 1;
        EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2;
        EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 3;
        EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 4;
        EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 5;
        EMERGENCY_SERVICE_CATEGORY_MIEC = 6;
        EMERGENCY_SERVICE_CATEGORY_AIEC = 7;
    }
    enum Source {
        EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 0;
        EMERGENCY_NUMBER_SOURCE_SIM = 1;
        EMERGENCY_NUMBER_SOURCE_DATABASE = 2;
        EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 3;
        EMERGENCY_NUMBER_SOURCE_DEFAULT = 4;
    }
    enum CallRoute {
        EMERGENCY_CALL_ROUTE_UNKNOWN = 0;
        EMERGENCY_CALL_ROUTE_EMERGENCY = 1;
        EMERGENCY_CALL_ROUTE_NORMAL = 2;
    }
    optional bool is_db_version_ignored = 1;
    optional int32 asset_version = 2;
    optional int32 ota_version = 3;
    optional string number = 4;
    optional string country_iso = 5;
    optional string mnc = 6;
    optional CallRoute route = 7;
    repeated string urns = 8;
    repeated ServiceCategory service_categories = 9;
    repeated Source sources = 10;
}
+28 −5
Original line number Diff line number Diff line
@@ -54,7 +54,9 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.metrics.EmergencyNumberStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.PersistAtomsProto;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.ecc.nano.ProtobufEccData;
@@ -110,6 +112,7 @@ public class EmergencyNumberTracker extends Handler {
    private String mCountryIso;
    private String mLastKnownEmergencyCountryIso = "";
    private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION;
    private int mCurrentOtaDatabaseVersion = INVALID_DATABASE_VERSION;
    private boolean mIsHalVersionLessThan1Dot4 = false;
    private Resources mResources = null;
    /**
@@ -572,20 +575,20 @@ public class EmergencyNumberTracker extends Handler {
        }

        // Cache OTA emergency number database
        int otaDatabaseVersion = cacheOtaEmergencyNumberDatabase();
        mCurrentOtaDatabaseVersion = cacheOtaEmergencyNumberDatabase();

        // Use a valid database that has higher version.
        if (otaDatabaseVersion == INVALID_DATABASE_VERSION
        if (mCurrentOtaDatabaseVersion == INVALID_DATABASE_VERSION
                && assetsDatabaseVersion == INVALID_DATABASE_VERSION) {
            loge("No database available. Phone Id: " + mPhone.getPhoneId());
        } else if (assetsDatabaseVersion > otaDatabaseVersion) {
        } else if (assetsDatabaseVersion > mCurrentOtaDatabaseVersion) {
            logd("Using Asset Emergency database. Version: " + assetsDatabaseVersion);
            mCurrentDatabaseVersion = assetsDatabaseVersion;
            mEmergencyNumberListFromDatabase = updatedAssetEmergencyNumberList;
            mNormalRoutedNumbers.clear();
            mNormalRoutedNumbers = assetNormalRoutedNumbers;
        } else {
            logd("Using Ota Emergency database. Version: " + otaDatabaseVersion);
            logd("Using Ota Emergency database. Version: " + mCurrentOtaDatabaseVersion);
        }
    }

@@ -725,7 +728,8 @@ public class EmergencyNumberTracker extends Handler {
    private void updateOtaEmergencyNumberListDatabaseAndNotify() {
        logd("updateOtaEmergencyNumberListDatabaseAndNotify():"
                + " receiving Emegency Number database OTA update");
        if (cacheOtaEmergencyNumberDatabase() != INVALID_DATABASE_VERSION) {
        mCurrentOtaDatabaseVersion = cacheOtaEmergencyNumberDatabase();
        if (mCurrentOtaDatabaseVersion != INVALID_DATABASE_VERSION) {
            writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase);
            if (!DBG) {
                mEmergencyNumberListDatabaseLocalLog.log(
@@ -1009,6 +1013,10 @@ public class EmergencyNumberTracker extends Handler {
        return mCurrentDatabaseVersion;
    }

    public int getEmergencyNumberOtaDbVersion() {
        return mCurrentOtaDatabaseVersion;
    }

    private synchronized void updateEmergencyCountryIso(String countryIso) {
        mCountryIso = countryIso;
        if (!TextUtils.isEmpty(mCountryIso)) {
@@ -1360,6 +1368,21 @@ public class EmergencyNumberTracker extends Handler {
        return false;
    }

    /**
     * Captures the consolidated emergency numbers list and returns the array of
     * {@link PersistAtomsProto.EmergencyNumber}.
     */
    public PersistAtomsProto.EmergencyNumbersInfo[] getEmergencyNumbersProtoArray() {
        int otaVersion = Math.max(0, getEmergencyNumberOtaDbVersion());
        int assetVersion = Math.max(0, getEmergencyNumberDbVersion());
        boolean isDbRoutingIgnored = shouldEmergencyNumberRoutingFromDbBeIgnored();
        List<EmergencyNumber> emergencyNumberList = getEmergencyNumberList();
        logd("log emergency number list=" + emergencyNumberList + " for otaVersion=" + otaVersion
                + ", assetVersion=" + assetVersion + ", isDbRoutingIgnored=" + isDbRoutingIgnored);
        return EmergencyNumberStats.getInstance().convertEmergencyNumbersListToProto(
                emergencyNumberList, assetVersion, otaVersion, isDbRoutingIgnored);
    }

    /**
     * Dump Emergency Number List info in the tracking
     *
+173 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_EMERGENCY;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_NORMAL;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AIEC;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AMBULANCE;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MIEC;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_POLICE;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DATABASE;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DEFAULT;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_SIM;

import android.telephony.emergency.EmergencyNumber;
import android.util.SparseIntArray;

import com.android.internal.telephony.nano.PersistAtomsProto;

import java.util.ArrayList;
import java.util.List;

/**
 * EmergencyStats logs the atoms for consolidated emergency number list in framework. It also logs
 * the details of a dialed emergency number. To avoid repeated information this class stores the
 * emergency numbers list in map and verifies the information for duplicacy before logging it. Note:
 * This locally stored information will erase on process restart scenarios (like reboot, crash,
 * etc.).
 */
public class EmergencyNumberStats {

    private static final String TAG = EmergencyNumberStats.class.getSimpleName();
    private static final SparseIntArray sRoutesMap;
    private static final SparseIntArray sServiceCategoriesMap;
    private static final SparseIntArray sSourcesMap;
    private static EmergencyNumberStats sInstance;

    static {
        sRoutesMap = new SparseIntArray() {
            {
                put(EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY,
                        EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_EMERGENCY);
                put(EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL,
                        EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_NORMAL);
                put(EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN,
                        EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_UNKNOWN);
            }
        };

        sServiceCategoriesMap = new SparseIntArray() {
            {
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_POLICE);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MIEC);
                put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC,
                        EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AIEC);
            }
        };

        sSourcesMap = new SparseIntArray() {
            {
                put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
                        EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
                put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
                        EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_SIM);
                put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
                        EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DATABASE);
                put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
                        EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
                put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT,
                        EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DEFAULT);
            }
        };
    }

    private EmergencyNumberStats() {
    }

    /** Static method to provide singleton instance for EmergencyNumberStats. */
    public static EmergencyNumberStats getInstance() {
        if (sInstance == null) {
            sInstance = new EmergencyNumberStats();
        }
        return sInstance;
    }

    /**
     * It converts the {@link android.telephony.emergency.EmergencyNumber} to
     * {@link PersistAtomsProto.EmergencyNumber} for
     * logging the EmergencyNumber atoms with pulled event.
     *
     * @param emergencyNumberList android.telephony.EmergencyNumber list
     * @param assetVersion        assert version
     * @param otaVersion          ota version
     * @param isDbRoutingIgnored  flag that defines if routing is ignored through database.
     */
    public PersistAtomsProto.EmergencyNumbersInfo[] convertEmergencyNumbersListToProto(
            List<EmergencyNumber> emergencyNumberList, int assetVersion, int otaVersion,
            boolean isDbRoutingIgnored) {
        List<PersistAtomsProto.EmergencyNumbersInfo> numberProtoList = new ArrayList<>();
        for (EmergencyNumber number : emergencyNumberList) {
            numberProtoList.add(convertEmergencyNumberToProto(number, assetVersion, otaVersion,
                    isDbRoutingIgnored));
        }
        return numberProtoList.toArray(new PersistAtomsProto.EmergencyNumbersInfo[0]);
    }

    private PersistAtomsProto.EmergencyNumbersInfo convertEmergencyNumberToProto(
            EmergencyNumber number, int assetVer, int otaVer, boolean isDbRoutingIgnored) {
        String dialNumber = number.getNumber();
        PersistAtomsProto.EmergencyNumbersInfo emergencyNumber =
                new PersistAtomsProto.EmergencyNumbersInfo();
        emergencyNumber.isDbVersionIgnored = isDbRoutingIgnored;
        emergencyNumber.assetVersion = assetVer;
        emergencyNumber.otaVersion = otaVer;
        emergencyNumber.number = dialNumber;
        emergencyNumber.countryIso = number.getCountryIso();
        emergencyNumber.mnc = number.getMnc();
        emergencyNumber.route = sRoutesMap.get(number.getEmergencyCallRouting());
        emergencyNumber.urns = number.getEmergencyUrns().toArray(new String[0]);
        emergencyNumber.serviceCategories = getMappedServiceCategories(
                number.getEmergencyServiceCategories());
        emergencyNumber.sources = getMappedSources(number.getEmergencyNumberSources());
        return emergencyNumber;
    }

    private int[] getMappedServiceCategories(List<Integer> serviceCategories) {
        if (serviceCategories == null || serviceCategories.isEmpty()) {
            return null;
        }
        return serviceCategories.stream().map(sServiceCategoriesMap::get).mapToInt(
                Integer::intValue).toArray();
    }

    private int[] getMappedSources(List<Integer> sources) {
        if (sources == null || sources.isEmpty()) {
            return null;
        }
        return sources.stream().map(sSourcesMap::get).mapToInt(Integer::intValue).toArray();
    }
}
+37 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SER
import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION;
import static com.android.internal.telephony.TelephonyStatsLog.DEVICE_TELEPHONY_PROPERTIES;
import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO;
import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_LISTENER_EVENT;
@@ -57,10 +58,12 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
import com.android.internal.telephony.nano.PersistAtomsProto.EmergencyNumbersInfo;
import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
@@ -178,6 +181,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
            registerAtom(GBA_EVENT);
            registerAtom(PER_SIM_STATUS);
            registerAtom(OUTGOING_SHORT_CODE_SMS);
            registerAtom(EMERGENCY_NUMBERS_INFO);
            Rlog.d(TAG, "registered");
        } else {
            Rlog.e(TAG, "could not get StatsManager, atoms not registered");
@@ -254,6 +258,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                return pullPerSimStatus(data);
            case OUTGOING_SHORT_CODE_SMS:
                return pullOutgoingShortCodeSms(data);
            case EMERGENCY_NUMBERS_INFO:
                return pullEmergencyNumbersInfo(data);
            default:
                Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
                return StatsManager.PULL_SKIP;
@@ -760,6 +766,21 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
        }
    }

    private int pullEmergencyNumbersInfo(List<StatsEvent> data) {
        boolean isDataLogged = false;
        for (Phone phone : getPhonesIfAny()) {
            if (phone != null) {
                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
                if (tracker != null) {
                    EmergencyNumbersInfo[] numList = tracker.getEmergencyNumbersProtoArray();
                    Arrays.stream(numList).forEach(number -> data.add(buildStatsEvent(number)));
                    isDataLogged = true;
                }
            }
        }
        return isDataLogged ? StatsManager.PULL_SUCCESS : StatsManager.PULL_SKIP;
    }

    /** Registers a pulled atom ID {@code atomId}. */
    private void registerAtom(int atomId) {
        mStatsManager.setPullAtomCallback(atomId, /* metadata= */ null,
@@ -1109,6 +1130,21 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                shortCodeSms.shortCodeSmsCount);
    }

    private static StatsEvent buildStatsEvent(EmergencyNumbersInfo emergencyNumber) {
        return TelephonyStatsLog.buildStatsEvent(
                EMERGENCY_NUMBERS_INFO,
                emergencyNumber.isDbVersionIgnored,
                emergencyNumber.assetVersion,
                emergencyNumber.otaVersion,
                emergencyNumber.number,
                emergencyNumber.countryIso,
                emergencyNumber.mnc,
                emergencyNumber.route,
                emergencyNumber.urns,
                emergencyNumber.serviceCategories,
                emergencyNumber.sources);
    }

    /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
    private static Phone[] getPhonesIfAny() {
        try {