diff options
Diffstat (limited to 'src/com/android/mms/service/metrics/MmsMetricsCollector.java')
-rw-r--r-- | src/com/android/mms/service/metrics/MmsMetricsCollector.java | 164 |
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; + } +} |