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

Commit 92abd165 authored by Hyundo Moon's avatar Hyundo Moon
Browse files

Add ContentProfileErrorReportUtils

Bug: 317002318
Bug: 294797589
Test: atest ContentProfileErrorReportUtilsTest
Change-Id: Ic1152c844c2ce02bde4d2f4f2bc3a74649db92b4
parent 455b5402
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.bluetooth.content_profiles;

import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.os.SystemClock;
import android.util.Log;

import com.android.bluetooth.BluetoothStatsLog;
import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;

/**
 * Utility method to report exceptions and error/warn logs in content profiles. Will be no-op if
 * {@link Flags#contentProfilesErrorsMetrics()} is not enabled.
 */
public class ContentProfileErrorReportUtils {
    private static final String TAG = ContentProfileErrorReportUtils.class.getSimpleName();

    /* Minimum period between two error reports */
    @VisibleForTesting static final long MIN_PERIOD_BETWEEN_TWO_ERROR_REPORTS_MILLIS = 1_000;

    /* Whether reporting is enabled by flags */
    @VisibleForTesting static boolean sEnabled = Flags.contentProfilesErrorsMetrics();

    @VisibleForTesting static long sLastReportTime = 0;

    /**
     * Report error by writing BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED atom. A
     * report will be skipped if not enough time has passed from the last report.
     *
     * @param profile One of: {@link BluetoothProfile#PBAP}, {@link BluetoothProfile#MAP}, {@link
     *     BluetoothProfile#OPP}
     * @param fileNameEnum File name enum which is declared in {@link BluetoothProtoEnums}
     * @param type One of the following: {@link
     *     BluetoothStatsLog#BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION}, {@link
     *     BluetoothStatsLog#BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR}, {@link
     *     BluetoothStatsLog#BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_WARN}
     * @param tag A tag which represents the code location of this error. The values are managed per
     *     each java file.
     */
    public static synchronized void report(int profile, int fileNameEnum, int type, int tag) {
        if (!sEnabled) {
            return;
        }

        if (isTooFrequentReport()) {
            Log.w(
                    TAG,
                    "Skipping reporting this error to prevent flooding."
                            + " fileNameEnum="
                            + fileNameEnum
                            + ", tag="
                            + tag);
            return;
        }

        BluetoothStatsLog.write(
                BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED,
                profile,
                fileNameEnum,
                type,
                tag);
        sLastReportTime = SystemClock.uptimeMillis();
    }

    private static boolean isTooFrequentReport() {
        return SystemClock.uptimeMillis() - sLastReportTime
                < MIN_PERIOD_BETWEEN_TWO_ERROR_REPORTS_MILLIS;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ java_defaults {
        "androidx.test.ext.truth",
        "androidx.test.rules",
        "androidx.test.uiautomator_uiautomator",
        "flag-junit",
        "framework-bluetooth-pre-jarjar",
        "frameworks-base-testutils",
        "gson",
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.bluetooth.content_profiles;

import static com.google.common.truth.Truth.assertThat;

import android.os.SystemClock;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.flags.Flags;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class ContentProfileErrorReportUtilsTest {

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Test
    public void noOpWhenFlagIsDisabled() {
        mSetFlagsRule.disableFlags(Flags.FLAG_CONTENT_PROFILES_ERRORS_METRICS);
        long previousReportTimeMillis = ContentProfileErrorReportUtils.sLastReportTime;

        ContentProfileErrorReportUtils.report(0, 0, 0, 0);

        // The last report time should not be changed.
        assertThat(ContentProfileErrorReportUtils.sLastReportTime)
                .isEqualTo(previousReportTimeMillis);
    }

    @Test
    public void tooFrequentErrorReportIsSkipped() {
        mSetFlagsRule.enableFlags(Flags.FLAG_CONTENT_PROFILES_ERRORS_METRICS);
        // Set the last report time to the current time.
        long lastReportTimeMillisToSet = SystemClock.uptimeMillis();
        ContentProfileErrorReportUtils.sLastReportTime = lastReportTimeMillisToSet;

        ContentProfileErrorReportUtils.report(0, 0, 0, 0);

        // The last report time should not be changed.
        assertThat(ContentProfileErrorReportUtils.sLastReportTime)
                .isEqualTo(lastReportTimeMillisToSet);
    }

    @Test
    public void successfulReport() {
        mSetFlagsRule.enableFlags(Flags.FLAG_CONTENT_PROFILES_ERRORS_METRICS);
        // Set the last report time to much earlier than the current time.
        long lastReportTimeMillisToSet =
                SystemClock.uptimeMillis()
                        - (ContentProfileErrorReportUtils
                                        .MIN_PERIOD_BETWEEN_TWO_ERROR_REPORTS_MILLIS
                                * 2);
        ContentProfileErrorReportUtils.sLastReportTime = lastReportTimeMillisToSet;

        ContentProfileErrorReportUtils.report(0, 0, 0, 0);

        // After the successful report, the last report time should be changed.
        assertThat(ContentProfileErrorReportUtils.sLastReportTime)
                .isGreaterThan(lastReportTimeMillisToSet);
    }
}