1 /* 2 * Copyright (C) 2023 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.telephony.statslib; 18 19 import android.content.Context; 20 import android.os.Handler; 21 import android.os.HandlerThread; 22 import android.os.Looper; 23 import android.os.Message; 24 import android.util.Log; 25 import android.util.StatsEvent; 26 import android.util.StatsLog; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 /** Statslib class */ 31 public class StatsLib { 32 33 private static final String LOG_TAG = StatsLib.class.getSimpleName(); 34 private static final boolean DBG = true; 35 private static final int DEFAULT_FREQUENCY_WRITING_PUSHED_ATOM_IN_MILLS = 50; 36 37 private final StatsLibPulledAtomCallback mStatsLibPulledAtomCallback; 38 private final WritePushedAtomHandler mHandler; 39 40 /** Default constructor. */ StatsLib(Context context)41 public StatsLib(Context context) { 42 mStatsLibPulledAtomCallback = new StatsLibPulledAtomCallback(context); 43 log("created StatsLib."); 44 HandlerThread handlerThread = new HandlerThread("StatsLibStorage"); 45 handlerThread.start(); 46 mHandler = new WritePushedAtomHandler(handlerThread.getLooper()); 47 } 48 49 @VisibleForTesting StatsLib(StatsLibPulledAtomCallback cb)50 protected StatsLib(StatsLibPulledAtomCallback cb) { 51 mStatsLibPulledAtomCallback = cb; 52 log("created StatsLib."); 53 HandlerThread handlerThread = new HandlerThread("StatsLibStorage"); 54 handlerThread.start(); 55 mHandler = new WritePushedAtomHandler(handlerThread.getLooper()); 56 } 57 58 /** 59 * Registers the target atom to be pulled. 60 * 61 * @param statsId the pulled atom tag to register to take data from. 62 */ registerPulledAtomCallback(int statsId)63 public void registerPulledAtomCallback(int statsId) { 64 mStatsLibPulledAtomCallback.registerAtom(statsId); 65 } 66 67 /** 68 * Registers the target atom to be pulled. 69 * 70 * @param statsId The tag of the atom for this puller callback. 71 * @param callback callback to be registered. 72 */ registerPulledAtomCallback(int statsId, PulledCallback callback)73 public void registerPulledAtomCallback(int statsId, PulledCallback callback) { 74 mStatsLibPulledAtomCallback.registerAtom(statsId, callback); 75 } 76 77 /** 78 * checks whether stats id was already registered or not. 79 * 80 * @param statsId The tag of the atom for this puller callback. 81 * @return true already registered. 82 */ isRegisteredPulledAtomCallback(int statsId)83 public boolean isRegisteredPulledAtomCallback(int statsId) { 84 return mStatsLibPulledAtomCallback.isRegisteredAtom(statsId); 85 } 86 87 /** 88 * Unregisters the target atom being pulled. 89 * 90 * @param statsId The tag of the atom to remove callback and tag 91 */ unregisterPulledAtomCallback(int statsId)92 public void unregisterPulledAtomCallback(int statsId) { 93 if (isRegisteredPulledAtomCallback(statsId)) { 94 mStatsLibPulledAtomCallback.unregisterAtom(statsId); 95 } 96 } 97 98 /** 99 * Write the pushed atoms 100 * 101 * @param pushed AtomsPushed 102 */ write(AtomsPushed pushed)103 public void write(AtomsPushed pushed) { 104 if (pushed == null) { 105 loge("writePushedAtoms: pushed is null"); 106 return; 107 } 108 mHandler.sendMessage(Message.obtain(mHandler, 0, pushed)); 109 } 110 onWritePushedAtom(AtomsPushed pushed)111 protected void onWritePushedAtom(AtomsPushed pushed) { 112 final StatsEvent.Builder builder = StatsEvent.newBuilder(); 113 builder.setAtomId(pushed.getStatsId()); 114 pushed.build(builder); 115 builder.usePooledBuffer(); 116 StatsLog.write(builder.build()); 117 log("writePushedAtoms: pushed=" + pushed); 118 119 append(pushed); 120 } 121 122 private class WritePushedAtomHandler extends Handler { 123 /** 124 * Use the provided {@link Looper} instead of the default one. 125 * 126 * @param looper The looper, must not be null. 127 */ WritePushedAtomHandler(Looper looper)128 WritePushedAtomHandler(Looper looper) { 129 super(looper); 130 } 131 132 @Override handleMessage(Message message)133 public void handleMessage(Message message) { 134 try { 135 AtomsPushed pushed = (AtomsPushed) message.obj; 136 onWritePushedAtom(pushed); 137 /* Atom logging frequency should not exceed once per 10 milliseconds (i.e. 138 * consecutive atom calls should be at least 10 milliseconds apart). This ensures 139 * that our logging socket is not spammed so that the socket does not drop data. If 140 * your logging line might trigger frequently, we suggest putting a guardrail to 141 * check that at least 1 second has passed since the last atom push. 142 */ 143 Thread.sleep(DEFAULT_FREQUENCY_WRITING_PUSHED_ATOM_IN_MILLS); 144 } catch (InterruptedException | ClassCastException e) { 145 loge("WritePushedAtomHandler, e:" + e); 146 } 147 } 148 } 149 150 /** 151 * Append the Pushed atoms 152 * 153 * @param pushed AtomsPushed 154 */ append(AtomsPushed pushed)155 private void append(AtomsPushed pushed) { 156 StatsLibStorage storage = getStorage(); 157 if (storage == null) { 158 loge("appendPushedAtoms: storage is null"); 159 return; 160 } 161 storage.appendPushedAtoms(pushed); 162 log("appendPushedAtoms: pushed=" + pushed); 163 } 164 165 /** 166 * Append the Pulled atoms 167 * 168 * @param pulled AtomsPulled 169 */ append(AtomsPulled pulled)170 public void append(AtomsPulled pulled) { 171 if (pulled == null) { 172 return; 173 } 174 StatsLibStorage storage = getStorage(); 175 if (storage == null) { 176 loge("appendPulledAtoms: storage is null"); 177 return; 178 } 179 if (!isRegisteredPulledAtomCallback(pulled.getStatsId())) { 180 registerPulledAtomCallback(pulled.getStatsId()); 181 } 182 storage.appendPulledAtoms(pulled); 183 log("appendPulledAtoms: pulled=" + pulled); 184 } 185 getStorage()186 private StatsLibStorage getStorage() { 187 if (mStatsLibPulledAtomCallback == null) { 188 return null; 189 } 190 return mStatsLibPulledAtomCallback.getStatsLibStorage(); 191 } 192 log(String s)193 private void log(String s) { 194 if (DBG) Log.d(LOG_TAG, s); 195 } 196 loge(String s)197 private void loge(String s) { 198 Log.e(LOG_TAG, s); 199 } 200 } 201