summaryrefslogtreecommitdiff
path: root/src/com/android/mms/service/metrics/MmsMetricsCollector.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/mms/service/metrics/MmsMetricsCollector.java')
-rw-r--r--src/com/android/mms/service/metrics/MmsMetricsCollector.java164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/com/android/mms/service/metrics/MmsMetricsCollector.java b/src/com/android/mms/service/metrics/MmsMetricsCollector.java
new file mode 100644
index 0000000..1bec4ae
--- /dev/null
+++ b/src/com/android/mms/service/metrics/MmsMetricsCollector.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 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.mms.service.metrics;
+
+import static com.android.mms.MmsStatsLog.INCOMING_MMS;
+import static com.android.mms.MmsStatsLog.OUTGOING_MMS;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.Log;
+import android.util.StatsEvent;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.mms.IncomingMms;
+import com.android.mms.MmsStatsLog;
+import com.android.mms.OutgoingMms;
+import com.android.internal.util.ConcurrentUtils;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implements statsd pullers for Mms.
+ *
+ * <p>This class registers pullers to statsd, which will be called once a day to obtain mms
+ * statistics that cannot be sent to statsd in real time.
+ */
+public class MmsMetricsCollector implements StatsManager.StatsPullAtomCallback {
+ private static final String TAG = MmsMetricsCollector.class.getSimpleName();
+ /** Disables various restrictions to ease debugging during development. */
+ private static final boolean DBG = false; // STOPSHIP if true
+ private static final long MILLIS_PER_HOUR = Duration.ofHours(1).toMillis();
+ private static final long MILLIS_PER_SECOND = Duration.ofSeconds(1).toMillis();
+ /**
+ * Sets atom pull cool down to 23 hours to help enforcing privacy requirement.
+ *
+ * <p>Applies to certain atoms. The interval of 23 hours leaves some margin for pull operations
+ * that occur once a day.
+ */
+ private static final long MIN_COOLDOWN_MILLIS =
+ DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
+ private static final StatsManager.PullAtomMetadata POLICY_PULL_DAILY =
+ new StatsManager.PullAtomMetadata.Builder()
+ .setCoolDownMillis(MIN_COOLDOWN_MILLIS)
+ .build();
+ private final PersistMmsAtomsStorage mStorage;
+ private final StatsManager mStatsManager;
+
+
+ public MmsMetricsCollector(Context context) {
+ this(context, new PersistMmsAtomsStorage(context));
+ }
+
+ @VisibleForTesting
+ public MmsMetricsCollector(Context context, PersistMmsAtomsStorage storage) {
+ mStorage = storage;
+ mStatsManager = context.getSystemService(StatsManager.class);
+ if (mStatsManager != null) {
+ registerAtom(INCOMING_MMS, POLICY_PULL_DAILY);
+ registerAtom(OUTGOING_MMS, POLICY_PULL_DAILY);
+ Log.d(TAG, "[MmsMetricsCollector]: registered atoms");
+ } else {
+ Log.e(TAG, "[MmsMetricsCollector]: could not get StatsManager, "
+ + "atoms not registered");
+ }
+ }
+
+ private static StatsEvent buildStatsEvent(IncomingMms mms) {
+ return MmsStatsLog.buildStatsEvent(
+ INCOMING_MMS,
+ mms.getRat(),
+ mms.getResult(),
+ mms.getRoaming(),
+ mms.getSimSlotIndex(),
+ mms.getIsMultiSim(),
+ mms.getIsEsim(),
+ mms.getCarrierId(),
+ mms.getAvgIntervalMillis(),
+ mms.getMmsCount(),
+ mms.getRetryId(),
+ mms.getHandledByCarrierApp());
+ }
+
+ private static StatsEvent buildStatsEvent(OutgoingMms mms) {
+ return MmsStatsLog.buildStatsEvent(
+ OUTGOING_MMS,
+ mms.getRat(),
+ mms.getResult(),
+ mms.getRoaming(),
+ mms.getSimSlotIndex(),
+ mms.getIsMultiSim(),
+ mms.getIsEsim(),
+ mms.getCarrierId(),
+ mms.getAvgIntervalMillis(),
+ mms.getMmsCount(),
+ mms.getIsFromDefaultApp(),
+ mms.getRetryId(),
+ mms.getHandledByCarrierApp());
+ }
+
+ @Override
+ public int onPullAtom(int atomTag, List<StatsEvent> data) {
+ switch (atomTag) {
+ case INCOMING_MMS:
+ return pullIncomingMms(data);
+ case OUTGOING_MMS:
+ return pullOutgoingMms(data);
+ default:
+ Log.e(TAG, String.format("unexpected atom ID %d", atomTag));
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullIncomingMms(List<StatsEvent> data) {
+ List<IncomingMms> incomingMmsList = mStorage.getIncomingMms(MIN_COOLDOWN_MILLIS);
+ if (incomingMmsList != null) {
+ // MMS List is already shuffled when MMS were inserted.
+ incomingMmsList.forEach(mms -> data.add(buildStatsEvent(mms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Log.w(TAG, "INCOMING_MMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullOutgoingMms(List<StatsEvent> data) {
+ List<OutgoingMms> outgoingMmsList = mStorage.getOutgoingMms(MIN_COOLDOWN_MILLIS);
+ if (outgoingMmsList != null) {
+ // MMS List is already shuffled when MMS were inserted.
+ outgoingMmsList.forEach(mms -> data.add(buildStatsEvent(mms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Log.w(TAG, "OUTGOING_MMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
+ private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) {
+ mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this);
+ }
+
+ /** Returns the {@link PersistMmsAtomsStorage} backing the puller. */
+ public PersistMmsAtomsStorage getAtomsStorage() {
+ return mStorage;
+ }
+}