1/* 2 * Copyright (C) 2017 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#define LOG_TAG "MetricsSummarizer" 18#include <utils/Log.h> 19 20#include <stdlib.h> 21#include <stdint.h> 22#include <inttypes.h> 23 24#include <utils/threads.h> 25#include <utils/Errors.h> 26#include <utils/KeyedVector.h> 27#include <utils/String8.h> 28#include <utils/List.h> 29 30#include <media/IMediaAnalyticsService.h> 31 32#include "MetricsSummarizer.h" 33 34 35namespace android { 36 37#define DEBUG_SORT 0 38#define DEBUG_QUEUE 0 39 40 41MetricsSummarizer::MetricsSummarizer(const char *key) 42 : mIgnorables(NULL) 43{ 44 ALOGV("MetricsSummarizer::MetricsSummarizer"); 45 46 if (key == NULL) { 47 mKey = key; 48 } else { 49 mKey = strdup(key); 50 } 51 52 mSummaries = new List<MediaAnalyticsItem *>(); 53} 54 55MetricsSummarizer::~MetricsSummarizer() 56{ 57 ALOGV("MetricsSummarizer::~MetricsSummarizer"); 58 if (mKey) { 59 free((void *)mKey); 60 mKey = NULL; 61 } 62 63 // clear the list of items we have saved 64 while (mSummaries->size() > 0) { 65 MediaAnalyticsItem * oitem = *(mSummaries->begin()); 66 if (DEBUG_QUEUE) { 67 ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "", 68 oitem->getKey().c_str(), oitem->getSessionID(), 69 oitem->getTimestamp()); 70 } 71 mSummaries->erase(mSummaries->begin()); 72 delete oitem; 73 } 74} 75 76// so we know what summarizer we were using 77const char *MetricsSummarizer::getKey() { 78 const char *value = mKey; 79 if (value == NULL) { 80 value = "unknown"; 81 } 82 return value; 83} 84 85// should the record be given to this summarizer 86bool MetricsSummarizer::isMine(MediaAnalyticsItem &item) 87{ 88 if (mKey == NULL) 89 return true; 90 AString itemKey = item.getKey(); 91 if (strcmp(mKey, itemKey.c_str()) != 0) { 92 return false; 93 } 94 return true; 95} 96 97AString MetricsSummarizer::dumpSummary(int &slot) 98{ 99 return dumpSummary(slot, NULL); 100} 101 102AString MetricsSummarizer::dumpSummary(int &slot, const char *only) 103{ 104 AString value = ""; 105 106 List<MediaAnalyticsItem *>::iterator it = mSummaries->begin(); 107 if (it != mSummaries->end()) { 108 char buf[16]; // enough for "#####: " 109 for (; it != mSummaries->end(); it++) { 110 if (only != NULL && strcmp(only, (*it)->getKey().c_str()) != 0) { 111 continue; 112 } 113 AString entry = (*it)->toString(); 114 snprintf(buf, sizeof(buf), "%5d: ", slot); 115 value.append(buf); 116 value.append(entry.c_str()); 117 value.append("\n"); 118 slot++; 119 } 120 } 121 return value; 122} 123 124void MetricsSummarizer::setIgnorables(const char **ignorables) { 125 mIgnorables = ignorables; 126} 127 128const char **MetricsSummarizer::getIgnorables() { 129 return mIgnorables; 130} 131 132void MetricsSummarizer::handleRecord(MediaAnalyticsItem *item) { 133 134 ALOGV("MetricsSummarizer::handleRecord() for %s", 135 item == NULL ? "<nothing>" : item->toString().c_str()); 136 137 if (item == NULL) { 138 return; 139 } 140 141 List<MediaAnalyticsItem *>::iterator it = mSummaries->begin(); 142 for (; it != mSummaries->end(); it++) { 143 bool good = sameAttributes((*it), item, getIgnorables()); 144 ALOGV("Match against %s says %d", 145 (*it)->toString().c_str(), good); 146 if (good) 147 break; 148 } 149 if (it == mSummaries->end()) { 150 ALOGV("save new record"); 151 item = item->dup(); 152 if (item == NULL) { 153 ALOGE("unable to save MediaMetrics record"); 154 } 155 sortProps(item); 156 item->setInt32("aggregated",1); 157 mSummaries->push_back(item); 158 } else { 159 ALOGV("increment existing record"); 160 (*it)->addInt32("aggregated",1); 161 mergeRecord(*(*it), *item); 162 } 163} 164 165void MetricsSummarizer::mergeRecord(MediaAnalyticsItem &/*have*/, MediaAnalyticsItem &/*item*/) { 166 // default is no further massaging. 167 ALOGV("MetricsSummarizer::mergeRecord() [default]"); 168 return; 169} 170 171 172// 173// Comparators 174// 175 176// testing that all of 'single' is in 'summ' 177// and that the values match. 178// 'summ' may have extra fields. 179// 'ignorable' is a set of things that we don't worry about matching up 180// (usually time- or count-based values we'll sum elsewhere) 181bool MetricsSummarizer::sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) { 182 183 if (single == NULL || summ == NULL) { 184 return false; 185 } 186 ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str()); 187 ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str()); 188 189 // this can be made better. 190 for(size_t i=0;i<single->mPropCount;i++) { 191 MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]); 192 const char *attrName = prop1->mName; 193 ALOGV("compare on attr '%s'", attrName); 194 195 // is it something we should ignore 196 if (ignorable != NULL) { 197 const char **ig = ignorable; 198 while (*ig) { 199 if (strcmp(*ig, attrName) == 0) { 200 break; 201 } 202 ig++; 203 } 204 if (*ig) { 205 ALOGV("we don't mind that it has attr '%s'", attrName); 206 continue; 207 } 208 } 209 210 MediaAnalyticsItem::Prop *prop2 = summ->findProp(attrName); 211 if (prop2 == NULL) { 212 ALOGV("summ doesn't have this attr"); 213 return false; 214 } 215 if (prop1->mType != prop2->mType) { 216 ALOGV("mismatched attr types"); 217 return false; 218 } 219 switch (prop1->mType) { 220 case MediaAnalyticsItem::kTypeInt32: 221 if (prop1->u.int32Value != prop2->u.int32Value) 222 return false; 223 break; 224 case MediaAnalyticsItem::kTypeInt64: 225 if (prop1->u.int64Value != prop2->u.int64Value) 226 return false; 227 break; 228 case MediaAnalyticsItem::kTypeDouble: 229 // XXX: watch out for floating point comparisons! 230 if (prop1->u.doubleValue != prop2->u.doubleValue) 231 return false; 232 break; 233 case MediaAnalyticsItem::kTypeCString: 234 if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0) 235 return false; 236 break; 237 case MediaAnalyticsItem::kTypeRate: 238 if (prop1->u.rate.count != prop2->u.rate.count) 239 return false; 240 if (prop1->u.rate.duration != prop2->u.rate.duration) 241 return false; 242 break; 243 default: 244 return false; 245 } 246 } 247 248 return true; 249} 250 251bool MetricsSummarizer::sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) { 252 253 // verify same user 254 if (summ->mPid != single->mPid) 255 return false; 256 257 // and finally do the more expensive validation of the attributes 258 return sameAttributes(summ, single, ignorable); 259} 260 261int MetricsSummarizer::PropSorter(const void *a, const void *b) { 262 MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a; 263 MediaAnalyticsItem::Prop *bi = (MediaAnalyticsItem::Prop *)b; 264 return strcmp(ai->mName, bi->mName); 265} 266 267// we sort in the summaries so that it looks pretty in the dumpsys 268void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) { 269 if (item->mPropCount != 0) { 270 if (DEBUG_SORT) { 271 ALOGD("sortProps(pre): %s", item->toString().c_str()); 272 } 273 qsort(item->mProps, item->mPropCount, 274 sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter); 275 if (DEBUG_SORT) { 276 ALOGD("sortProps(pst): %s", item->toString().c_str()); 277 } 278 } 279} 280 281} // namespace android 282