1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.telecom.metrics; 18 19 import android.annotation.NonNull; 20 import android.app.StatsManager; 21 import android.content.Context; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.telecom.Log; 26 import android.util.StatsEvent; 27 28 import androidx.annotation.VisibleForTesting; 29 30 import com.android.server.telecom.nano.PulledAtomsClass.PulledAtoms; 31 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.nio.file.Files; 35 import java.nio.file.NoSuchFileException; 36 import java.util.List; 37 38 public abstract class TelecomPulledAtom extends Handler { 39 /** 40 * Min interval to persist the data. 41 */ 42 protected static final int DELAY_FOR_PERSISTENT_MILLIS = 30000; 43 protected static final int EVENT_SUB_BASE = 1000; 44 private static final String TAG = TelecomPulledAtom.class.getSimpleName(); 45 private static final long MIN_PULL_INTERVAL_MILLIS = 23L * 60 * 60 * 1000; 46 private static final int EVENT_SAVE = 1; 47 protected final Context mContext; 48 protected final boolean mIsTestMode; 49 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) 50 public PulledAtoms mPulledAtoms; 51 protected long mLastPulledTimestamps; 52 TelecomPulledAtom(@onNull Context context, @NonNull Looper looper, boolean isTestMode)53 protected TelecomPulledAtom(@NonNull Context context, @NonNull Looper looper, 54 boolean isTestMode) { 55 super(looper); 56 mContext = context; 57 mIsTestMode = isTestMode; 58 mPulledAtoms = loadAtomsFromFile(); 59 onLoad(); 60 } 61 pull(final List<StatsEvent> data)62 public synchronized int pull(final List<StatsEvent> data) { 63 if (!mIsTestMode) { 64 long cur = System.currentTimeMillis(); 65 if (cur - mLastPulledTimestamps < MIN_PULL_INTERVAL_MILLIS) { 66 return StatsManager.PULL_SKIP; 67 } 68 mLastPulledTimestamps = cur; 69 } 70 return onPull(data); 71 } 72 73 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) getTag()74 abstract public int getTag(); 75 76 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) onPull(List<StatsEvent> data)77 public abstract int onPull(List<StatsEvent> data); 78 onLoad()79 protected abstract void onLoad(); 80 81 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) onAggregate()82 public abstract void onAggregate(); 83 flush()84 public void flush() { 85 save(0); 86 } 87 getFileName()88 protected abstract String getFileName(); 89 loadAtomsFromFile()90 private synchronized PulledAtoms loadAtomsFromFile() { 91 if (!mIsTestMode) { 92 try { 93 return PulledAtoms.parseFrom( 94 Files.readAllBytes(mContext.getFileStreamPath(getFileName()).toPath())); 95 } catch (NoSuchFileException e) { 96 Log.e(TAG, e, "the atom file not found"); 97 } catch (IOException | NullPointerException e) { 98 Log.e(TAG, e, "cannot load/parse the atom file"); 99 } 100 } 101 return makeNewPulledAtoms(); 102 } 103 clearAtoms()104 protected synchronized void clearAtoms() { 105 mPulledAtoms = makeNewPulledAtoms(); 106 } 107 onSave()108 private synchronized void onSave() { 109 if (!mIsTestMode) { 110 try (FileOutputStream stream = mContext.openFileOutput(getFileName(), 111 Context.MODE_PRIVATE)) { 112 Log.d(TAG, "save " + getTag()); 113 stream.write(PulledAtoms.toByteArray(mPulledAtoms)); 114 } catch (IOException e) { 115 Log.e(TAG, e, "cannot save the atom to file"); 116 } catch (UnsupportedOperationException e) { 117 Log.e(TAG, e, "cannot open the file"); 118 } 119 } 120 } 121 makeNewPulledAtoms()122 private PulledAtoms makeNewPulledAtoms() { 123 return new PulledAtoms(); 124 } 125 126 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) save(int delayMillis)127 public void save(int delayMillis) { 128 if (delayMillis > 0) { 129 if (!hasMessages(EVENT_SAVE)) { 130 sendMessageDelayed(obtainMessage(EVENT_SAVE), delayMillis); 131 } 132 } else { 133 onSave(); 134 } 135 } 136 137 @Override handleMessage(Message msg)138 public void handleMessage(Message msg) { 139 if (msg.what == EVENT_SAVE) { 140 onSave(); 141 } 142 } 143 } 144