/*
 * 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.safetycenter.logging;

import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_PRIMARY_ACTION_CLICKED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_SECONDARY_ACTION_CLICKED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_DISMISSED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_POSTED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__ISSUE_STATE__ACTIVE;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__NAVIGATION_SOURCE__SOURCE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SENSOR__SENSOR_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_UNSPECIFIED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__VIEW_TYPE__VIEW_TYPE_NOTIFICATION;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__COMPLETE_GET_NEW_DATA;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__COMPLETE_RESCAN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__INLINE_ACTION;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__SINGLE_SOURCE_GET_NEW_DATA;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__SINGLE_SOURCE_RESCAN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__ERROR;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__SUCCESS;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__AUTOMATIC;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__SOURCE_UPDATED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_UNSPECIFIED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__DATA_PROVIDED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__NO_DATA_PROVIDED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__REFRESH_ERROR;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__REFRESH_TIMEOUT;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_CLEARED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_ERROR;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_STATE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__UPDATE_TYPE__REFRESH_RESPONSE;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__UPDATE_TYPE__SELF_INITIATED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__UPDATE_TYPE__UPDATE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED;
import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY;
import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.safetycenter.SafetyCenterManager;
import android.safetycenter.SafetyCenterManager.RefreshRequestType;
import android.safetycenter.SafetyCenterStatus;
import android.safetycenter.SafetyEvent;
import android.safetycenter.SafetySourceData;
import android.util.Log;
import android.util.StatsEvent;

import androidx.annotation.Nullable;

import com.android.permission.PermissionStatsLog;
import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.UserProfileGroup.ProfileType;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;

/**
 * Marshalls and writes statsd atoms. Contains implementation details of how atom parameters are
 * encoded and provides a better-typed interface for other classes to call.
 *
 * @hide
 */
public final class SafetyCenterStatsdLogger {

    private static final String TAG = "SafetyCenterStatsdLog";
    private static final long UNSET_SOURCE_ID = 0;
    private static final long UNSET_ISSUE_TYPE_ID = 0;
    private static final long UNSET_SESSION_ID = 0;
    private static final long UNSET_SOURCE_GROUP_ID = 0;
    private static final long UNSET_REFRESH_REASON = 0L;
    private static final boolean UNSET_DATA_CHANGED = false;
    private static final long UNSET_LAST_UPDATED_ELAPSED_TIME_MILLIS = 0L;

    /**
     * The different results for a system event reported by Safety Center.
     *
     * @hide
     */
    @IntDef(
            prefix = {"SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__"},
            value = {
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__SUCCESS,
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__ERROR,
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SystemEventResult {}

    /**
     * The different results for a system event reported by Safety Center.
     *
     * @hide
     */
    @IntDef(
            prefix = {"SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__"},
            value = {
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_STATE_UNKNOWN,
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__DATA_PROVIDED,
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__NO_DATA_PROVIDED,
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__REFRESH_TIMEOUT,
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__REFRESH_ERROR,
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_ERROR,
                SAFETY_SOURCE_STATE_COLLECTED__SOURCE_STATE__SOURCE_CLEARED
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SourceState {}

    /**
     * Creates a {@link PermissionStatsLog#SAFETY_STATE} {@link StatsEvent} with the given
     * parameters.
     */
    static StatsEvent createSafetyStateEvent(
            @SafetyCenterStatus.OverallSeverityLevel int severityLevel,
            long openIssueCount,
            long dismissedIssueCount) {
        return PermissionStatsLog.buildStatsEvent(
                SAFETY_STATE,
                toSafetyStateOverallSeverityLevel(severityLevel),
                openIssueCount,
                dismissedIssueCount);
    }

    /** Writes a {@link PermissionStatsLog#SAFETY_SOURCE_STATE_COLLECTED} atom. */
    public static void writeSafetySourceStateCollected(
            String sourceId,
            @ProfileType int profileType,
            @Nullable @SafetySourceData.SeverityLevel Integer sourceSeverityLevel,
            long openIssuesCount,
            long dismissedIssuesCount,
            long duplicateFilteredOutIssuesCount,
            @SourceState int sourceState,
            @Nullable SafetyEvent safetyEvent,
            @Nullable @SafetyCenterManager.RefreshReason Integer refreshReason,
            boolean dataChanged,
            @Nullable @ElapsedRealtimeLong Long lastUpdatedElapsedTimeMillis) {
        if (!SafetyCenterFlags.getAllowStatsdLogging()) {
            return;
        }
        int collectionType =
                safetyEvent != null
                        ? SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__SOURCE_UPDATED
                        : SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__AUTOMATIC;
        PermissionStatsLog.write(
                SAFETY_SOURCE_STATE_COLLECTED,
                idStringToLong(sourceId),
                toSourceStateCollectedProfileType(profileType),
                toSafetySourceStateCollectedSeverityLevel(sourceSeverityLevel),
                openIssuesCount,
                dismissedIssuesCount,
                duplicateFilteredOutIssuesCount,
                sourceState,
                collectionType,
                toSafetySourceStateCollectedCollectionType(safetyEvent),
                refreshReason != null ? refreshReason : UNSET_REFRESH_REASON,
                dataChanged,
                lastUpdatedElapsedTimeMillis != null
                        ? lastUpdatedElapsedTimeMillis
                        : UNSET_LAST_UPDATED_ELAPSED_TIME_MILLIS);
    }

    /**
     * Writes a {@link PermissionStatsLog#SAFETY_CENTER_SYSTEM_EVENT_REPORTED} atom of type {@code
     * SINGLE_SOURCE_RESCAN} or {@code SINGLE_SOURCE_GET_DATA}.
     */
    public static void writeSourceRefreshSystemEvent(
            @RefreshRequestType int refreshType,
            String sourceId,
            @ProfileType int profileType,
            Duration duration,
            @SystemEventResult int result,
            long refreshReason,
            boolean dataChanged) {
        if (!SafetyCenterFlags.getAllowStatsdLogging()) {
            return;
        }
        PermissionStatsLog.write(
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
                toSourceRefreshEventType(refreshType),
                idStringToLong(sourceId),
                toSystemEventProfileType(profileType),
                UNSET_ISSUE_TYPE_ID,
                duration.toMillis(),
                result,
                refreshReason,
                dataChanged);
    }

    /**
     * Writes a {@link PermissionStatsLog#SAFETY_CENTER_SYSTEM_EVENT_REPORTED} atom of type {@code
     * COMPLETE_RESCAN} or {@code COMPLETE_GET_DATA}.
     */
    public static void writeWholeRefreshSystemEvent(
            @RefreshRequestType int refreshType,
            Duration duration,
            @SystemEventResult int result,
            long refreshReason,
            boolean dataChanged) {
        if (!SafetyCenterFlags.getAllowStatsdLogging()) {
            return;
        }
        PermissionStatsLog.write(
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
                toWholeRefreshEventType(refreshType),
                UNSET_SOURCE_ID,
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN,
                UNSET_ISSUE_TYPE_ID,
                duration.toMillis(),
                result,
                refreshReason,
                dataChanged);
    }

    /**
     * Writes a {@link PermissionStatsLog#SAFETY_CENTER_SYSTEM_EVENT_REPORTED} atom of type {@code
     * INLINE_ACTION}.
     */
    public static void writeInlineActionSystemEvent(
            String sourceId,
            @ProfileType int profileType,
            @Nullable String issueTypeId,
            Duration duration,
            @SystemEventResult int result) {
        if (!SafetyCenterFlags.getAllowStatsdLogging()) {
            return;
        }
        PermissionStatsLog.write(
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
                SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__INLINE_ACTION,
                idStringToLong(sourceId),
                toSystemEventProfileType(profileType),
                issueTypeId == null ? UNSET_ISSUE_TYPE_ID : idStringToLong(issueTypeId),
                duration.toMillis(),
                result,
                UNSET_REFRESH_REASON,
                UNSET_DATA_CHANGED);
    }

    /**
     * Writes a {@link PermissionStatsLog#SAFETY_CENTER_INTERACTION_REPORTED} atom with the action
     * {@code NOTIFICATION_POSTED}.
     */
    public static void writeNotificationPostedEvent(
            String sourceId,
            @ProfileType int profileType,
            String issueTypeId,
            @SafetySourceData.SeverityLevel int sourceSeverityLevel) {
        writeNotificationInteractionReportedEvent(
                SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_POSTED,
                sourceId,
                profileType,
                issueTypeId,
                sourceSeverityLevel);
    }

    /**
     * Writes a {@link PermissionStatsLog#SAFETY_CENTER_INTERACTION_REPORTED} atom with the action
     * {@code NOTIFICATION_DISMISSED}.
     */
    public static void writeNotificationDismissedEvent(
            String sourceId,
            @ProfileType int profileType,
            String issueTypeId,
            @SafetySourceData.SeverityLevel int sourceSeverityLevel) {
        writeNotificationInteractionReportedEvent(
                SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_DISMISSED,
                sourceId,
                profileType,
                issueTypeId,
                sourceSeverityLevel);
    }

    /**
     * Writes a {@link PermissionStatsLog#SAFETY_CENTER_INTERACTION_REPORTED} atom with the action
     * {@code ISSUE_PRIMARY_ACTION_CLICKED} or {@code ISSUE_SECONDARY_ACTION_CLICKED}.
     */
    public static void writeNotificationActionClickedEvent(
            String sourceId,
            @ProfileType int profileType,
            String issueTypeId,
            @SafetySourceData.SeverityLevel int sourceSeverityLevel,
            boolean isPrimaryAction) {
        int action =
                isPrimaryAction
                        ? SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_PRIMARY_ACTION_CLICKED
                        : SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_SECONDARY_ACTION_CLICKED;
        writeNotificationInteractionReportedEvent(
                action, sourceId, profileType, issueTypeId, sourceSeverityLevel);
    }

    private static void writeNotificationInteractionReportedEvent(
            int interactionReportedAction,
            String sourceId,
            @ProfileType int profileType,
            String issueTypeId,
            @SafetySourceData.SeverityLevel int sourceSeverityLevel) {
        if (!SafetyCenterFlags.getAllowStatsdLogging()) {
            return;
        }
        PermissionStatsLog.write(
                SAFETY_CENTER_INTERACTION_REPORTED,
                UNSET_SESSION_ID,
                interactionReportedAction,
                SAFETY_CENTER_INTERACTION_REPORTED__VIEW_TYPE__VIEW_TYPE_NOTIFICATION,
                SAFETY_CENTER_INTERACTION_REPORTED__NAVIGATION_SOURCE__SOURCE_UNKNOWN,
                toInteractionReportedSeverityLevel(sourceSeverityLevel),
                idStringToLong(sourceId),
                toInteractionReportedProfileType(profileType),
                idStringToLong(issueTypeId),
                SAFETY_CENTER_INTERACTION_REPORTED__SENSOR__SENSOR_UNKNOWN,
                UNSET_SOURCE_GROUP_ID,
                SAFETY_CENTER_INTERACTION_REPORTED__ISSUE_STATE__ACTIVE);
    }

    /**
     * Returns a {@link SystemEventResult} based on whether the given operation was {@code
     * successful}.
     */
    @SystemEventResult
    public static int toSystemEventResult(boolean success) {
        return success
                ? SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__SUCCESS
                : SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__ERROR;
    }

    private static int toSourceRefreshEventType(@RefreshRequestType int refreshType) {
        switch (refreshType) {
            case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__SINGLE_SOURCE_GET_NEW_DATA;
            case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__SINGLE_SOURCE_RESCAN;
        }
        Log.w(TAG, "Unexpected SafetyCenterManager.RefreshRequestType: " + refreshType);
        return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_UNKNOWN;
    }

    private static int toWholeRefreshEventType(@RefreshRequestType int refreshType) {
        switch (refreshType) {
            case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__COMPLETE_GET_NEW_DATA;
            case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__COMPLETE_RESCAN;
        }
        Log.w(TAG, "Unexpected SafetyCenterManager.RefreshRequestType: " + refreshType);
        return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_UNKNOWN;
    }

    private static int toSourceStateCollectedProfileType(@ProfileType int profileType) {
        switch (profileType) {
            case PROFILE_TYPE_PRIMARY:
                return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
            case PROFILE_TYPE_MANAGED:
                return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
            case PROFILE_TYPE_PRIVATE:
                return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
        }
        Log.w(TAG, "state collect arg requested for unknown profile type " + profileType);
        return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
    }

    private static int toSystemEventProfileType(@ProfileType int profileType) {
        switch (profileType) {
            case PROFILE_TYPE_PRIMARY:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
            case PROFILE_TYPE_MANAGED:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
            case PROFILE_TYPE_PRIVATE:
                return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
        }
        Log.w(TAG, "system event arg requested for unknown profile type " + profileType);
        return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
    }

    private static int toInteractionReportedProfileType(@ProfileType int profileType) {
        switch (profileType) {
            case PROFILE_TYPE_PRIMARY:
                return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
            case PROFILE_TYPE_MANAGED:
                return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
            case PROFILE_TYPE_PRIVATE:
                return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
        }
        Log.w(TAG, "interaction enum requested for unknown profile type " + profileType);
        return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
    }

    /**
     * Converts a {@link String} ID (e.g. a Safety Source ID) to a {@code long} suitable for logging
     * to statsd.
     */
    private static long idStringToLong(String id) {
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            Log.w(TAG, "Couldn't encode safety source id: " + id, e);
            return 0;
        }
        messageDigest.update(id.getBytes());
        return new BigInteger(messageDigest.digest()).longValue();
    }

    private static int toSafetyStateOverallSeverityLevel(
            @SafetyCenterStatus.OverallSeverityLevel int severityLevel) {
        switch (severityLevel) {
            case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN:
                return SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
            case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK:
                return SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
            case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION:
                return SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
            case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING:
                return SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
        }
        Log.w(TAG, "Unexpected SafetyCenterStatus.OverallSeverityLevel: " + severityLevel);
        return SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
    }

    private static int toSafetySourceStateCollectedSeverityLevel(
            @Nullable @SafetySourceData.SeverityLevel Integer safetySourceSeverityLevel) {
        if (safetySourceSeverityLevel == null) {
            return SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
        }
        switch (safetySourceSeverityLevel) {
            case SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED:
                return SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_UNSPECIFIED;
            case SafetySourceData.SEVERITY_LEVEL_INFORMATION:
                return SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
            case SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION:
                return SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
            case SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING:
                return SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
        }
        Log.w(TAG, "Unexpected SafetySourceData.SeverityLevel: " + safetySourceSeverityLevel);
        return SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
    }

    private static int toInteractionReportedSeverityLevel(
            @SafetySourceData.SeverityLevel int severityLevel) {
        switch (severityLevel) {
            case SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED:
                return SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_UNSPECIFIED;
            case SafetySourceData.SEVERITY_LEVEL_INFORMATION:
                return SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
            case SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION:
                return SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
            case SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING:
                return SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
        }
        Log.w(TAG, "Unexpected SafetySourceData.SeverityLevel: " + severityLevel);
        return SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
    }

    private static int toSafetySourceStateCollectedCollectionType(
            @Nullable SafetyEvent safetyEvent) {
        if (safetyEvent == null) {
            return SAFETY_SOURCE_STATE_COLLECTED__UPDATE_TYPE__UPDATE_TYPE_UNKNOWN;
        }
        if (safetyEvent.getType() == SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED) {
            return SAFETY_SOURCE_STATE_COLLECTED__UPDATE_TYPE__REFRESH_RESPONSE;
        } else {
            return SAFETY_SOURCE_STATE_COLLECTED__UPDATE_TYPE__SELF_INITIATED;
        }
    }
}