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

Commit 3273c577 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
Merged-In: I5f4c53cbdb4796f57d75b11855093a9d9b384022
(cherry picked from commit 8de15f93)
parent 8aadc22e
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -225,6 +225,12 @@ message PersistAtoms {

    /* Timestamp of last satellite_sos_message_recommender pull. */
    optional int64 satellite_sos_message_recommender_pull_timestamp_millis = 69;

    /* 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:
@@ -628,3 +634,38 @@ message SatelliteSosMessageRecommender {
    optional int32 cellular_service_state = 4;
    optional int32 count = 5;
}

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
@@ -49,7 +49,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;
@@ -105,6 +107,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 Resources mResources = null;
    /**
     * Used for storing all specific mnc's along with the list of emergency numbers
@@ -563,20 +566,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);
        }
    }

@@ -716,7 +719,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(
@@ -1000,6 +1004,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)) {
@@ -1290,6 +1298,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;
@@ -63,10 +64,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;
@@ -194,6 +197,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
            registerAtom(SATELLITE_OUTGOING_DATAGRAM);
            registerAtom(SATELLITE_PROVISION);
            registerAtom(SATELLITE_SOS_MESSAGE_RECOMMENDER);
            registerAtom(EMERGENCY_NUMBERS_INFO);
            Rlog.d(TAG, "registered");
        } else {
            Rlog.e(TAG, "could not get StatsManager, atoms not registered");
@@ -282,6 +286,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                return pullSatelliteProvision(data);
            case SATELLITE_SOS_MESSAGE_RECOMMENDER:
                return pullSatelliteSosMessageRecommender(data);
            case EMERGENCY_NUMBERS_INFO:
                return pullEmergencyNumbersInfo(data);
            default:
                Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
                return StatsManager.PULL_SKIP;
@@ -863,6 +869,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,
@@ -1278,6 +1299,21 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                stats.count);
    }

    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 {