github.com/google/battery-historian@v0.0.0-20170519220231-d2356ba4fd5f/checkindelta/checkin_normalize.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package checkindelta 16 17 import ( 18 "errors" 19 "math" 20 "reflect" 21 "strings" 22 23 "github.com/golang/protobuf/proto" 24 "github.com/google/battery-historian/checkinparse" 25 bspb "github.com/google/battery-historian/pb/batterystats_proto" 26 ) 27 28 // roundToTwoDecimal rounds off floats to 2 decimal places. 29 func roundToTwoDecimal(val float64) float64 { 30 var roundedVal float64 31 if val > 0 { 32 roundedVal = math.Floor((val * 100) + .5) 33 } else { 34 roundedVal = math.Ceil((val * 100) - .5) 35 } 36 return roundedVal / 100 37 } 38 39 // NormalizeStats takes in a proto and normalizes it by converting 40 // any absolute value to value/TotalTime. 41 func NormalizeStats(p *bspb.BatteryStats) (*bspb.BatteryStats, error) { 42 totalTimeHour := roundToTwoDecimal(float64((p.GetSystem().GetBattery().GetBatteryRealtimeMsec()) / (3600 * 1000))) 43 44 if totalTimeHour == 0 { 45 return nil, errors.New("battery real time cannot be 0") 46 } 47 normApp := &bspb.BatteryStats{ 48 ReportVersion: p.ReportVersion, 49 AggregationType: p.AggregationType, 50 } 51 52 // Normalize the app data 53 for _, a1 := range p.GetApp() { 54 a := normalizeApp(a1, totalTimeHour) 55 normApp.App = append(normApp.App, a) 56 } 57 58 // Normalize system data 59 s1 := p.GetSystem() 60 s := &bspb.BatteryStats_System{} 61 62 if norm := normalizeMessage(s1.GetBattery(), totalTimeHour); norm != nil { 63 s.Battery = norm.(*bspb.BatteryStats_System_Battery) 64 } 65 if norm := normalizeMessage(s1.GetBatteryDischarge(), totalTimeHour); norm != nil { 66 s.BatteryDischarge = norm.(*bspb.BatteryStats_System_BatteryDischarge) 67 } 68 s.BatteryLevel = s1.GetBatteryLevel() 69 if norm := normalizeRepeatedMessage(s1.GetBluetoothState(), totalTimeHour); !norm.IsNil() { 70 s.BluetoothState = norm.Interface().([]*bspb.BatteryStats_System_BluetoothState) 71 } 72 if norm := normalizeRepeatedMessage(s1.GetDataConnection(), totalTimeHour); !norm.IsNil() { 73 s.DataConnection = norm.Interface().([]*bspb.BatteryStats_System_DataConnection) 74 } 75 if norm := normalizeMessage(s1.GetGlobalNetwork(), totalTimeHour); norm != nil { 76 s.GlobalNetwork = norm.(*bspb.BatteryStats_System_GlobalNetwork) 77 } 78 if norm := normalizeRepeatedMessage(s1.GetKernelWakelock(), totalTimeHour); !norm.IsNil() { 79 s.KernelWakelock = norm.Interface().([]*bspb.BatteryStats_System_KernelWakelock) 80 } 81 if norm := normalizeMessage(s1.GetMisc(), totalTimeHour); norm != nil { 82 s.Misc = norm.(*bspb.BatteryStats_System_Misc) 83 } 84 if norm := normalizeRepeatedMessage(s1.GetPowerUseItem(), totalTimeHour); !norm.IsNil() { 85 s.PowerUseItem = norm.Interface().([]*bspb.BatteryStats_System_PowerUseItem) 86 } 87 if norm := normalizeMessage(s1.GetPowerUseSummary(), totalTimeHour); norm != nil { 88 s.PowerUseSummary = norm.(*bspb.BatteryStats_System_PowerUseSummary) 89 } 90 if norm := normalizeRepeatedMessage(s1.GetScreenBrightness(), totalTimeHour); !norm.IsNil() { 91 s.ScreenBrightness = norm.Interface().([]*bspb.BatteryStats_System_ScreenBrightness) 92 } 93 if norm := normalizeMessage(s1.GetSignalScanningTime(), totalTimeHour); norm != nil { 94 s.SignalScanningTime = norm.(*bspb.BatteryStats_System_SignalScanningTime) 95 } 96 if norm := normalizeRepeatedMessage(s1.GetSignalStrength(), totalTimeHour); !norm.IsNil() { 97 s.SignalStrength = norm.Interface().([]*bspb.BatteryStats_System_SignalStrength) 98 } 99 if norm := normalizeRepeatedMessage(s1.GetWakeupReason(), totalTimeHour); !norm.IsNil() { 100 s.WakeupReason = norm.Interface().([]*bspb.BatteryStats_System_WakeupReason) 101 } 102 if norm := normalizeRepeatedMessage(s1.GetWifiState(), totalTimeHour); !norm.IsNil() { 103 s.WifiState = norm.Interface().([]*bspb.BatteryStats_System_WifiState) 104 } 105 if norm := normalizeRepeatedMessage(s1.GetWifiSupplicantState(), totalTimeHour); !norm.IsNil() { 106 s.WifiSupplicantState = norm.Interface().([]*bspb.BatteryStats_System_WifiSupplicantState) 107 } 108 p.System = s 109 p.App = normApp.App 110 return p, nil 111 } 112 113 // Normalize app data 114 func normalizeApp(a *bspb.BatteryStats_App, totalTimeHour float64) *bspb.BatteryStats_App { 115 if a == nil { 116 return nil 117 } 118 res := proto.Clone(a).(*bspb.BatteryStats_App) 119 120 if norm := normalizeMessage(a.GetForeground(), totalTimeHour); norm != nil { 121 res.Foreground = norm.(*bspb.BatteryStats_App_Foreground) 122 } 123 if norm := normalizeAppApk(a.GetApk(), totalTimeHour); norm != nil { 124 res.Apk = norm 125 } 126 normalizeAppChildren(res.GetChild(), totalTimeHour) 127 if norm := normalizeMessage(a.GetNetwork(), totalTimeHour); norm != nil { 128 res.Network = norm.(*bspb.BatteryStats_App_Network) 129 } 130 if norm := normalizeMessage(a.GetPowerUseItem(), totalTimeHour); norm != nil { 131 res.PowerUseItem = norm.(*bspb.BatteryStats_App_PowerUseItem) 132 } 133 if norm := normalizeRepeatedMessage(a.GetProcess(), totalTimeHour).Interface(); norm != nil { 134 res.Process = norm.([]*bspb.BatteryStats_App_Process) 135 } 136 if norm := normalizeRepeatedMessage(a.GetSensor(), totalTimeHour).Interface(); norm != nil { 137 res.Sensor = norm.([]*bspb.BatteryStats_App_Sensor) 138 } 139 if norm := normalizeMessage(a.GetStateTime(), totalTimeHour); norm != nil { 140 res.StateTime = norm.(*bspb.BatteryStats_App_StateTime) 141 } 142 if norm := normalizeMessage(a.GetVibrator(), totalTimeHour); norm != nil { 143 res.Vibrator = norm.(*bspb.BatteryStats_App_Vibrator) 144 } 145 if norm := normalizeRepeatedMessage(a.GetWakelock(), totalTimeHour).Interface(); norm != nil { 146 res.Wakelock = norm.([]*bspb.BatteryStats_App_Wakelock) 147 } 148 if norm := normalizeRepeatedMessage(a.GetWakeupAlarm(), totalTimeHour).Interface(); norm != nil { 149 res.WakeupAlarm = norm.([]*bspb.BatteryStats_App_WakeupAlarm) 150 } 151 if norm := normalizeMessage(a.GetWifi(), totalTimeHour); norm != nil { 152 res.Wifi = norm.(*bspb.BatteryStats_App_Wifi) 153 } 154 if norm := normalizeRepeatedMessage(a.GetUserActivity(), totalTimeHour).Interface(); norm != nil { 155 res.UserActivity = norm.([]*bspb.BatteryStats_App_UserActivity) 156 } 157 if norm := normalizeRepeatedMessage(a.GetScheduledJob(), totalTimeHour).Interface(); norm != nil { 158 res.ScheduledJob = norm.([]*bspb.BatteryStats_App_ScheduledJob) 159 } 160 return res 161 } 162 163 // normalizeAppApk normalizes values in the "apk" section of App. 164 func normalizeAppApk(p *bspb.BatteryStats_App_Apk, totalTimeHour float64) *bspb.BatteryStats_App_Apk { 165 norm := &bspb.BatteryStats_App_Apk{} 166 167 // there's only one wakeups value per apk 168 norm.Wakeups = proto.Float32(float32(roundToTwoDecimal(float64(p.GetWakeups()) / totalTimeHour))) 169 norm.Service = normalizeRepeatedMessage(p.GetService(), totalTimeHour).Interface().([]*bspb.BatteryStats_App_Apk_Service) 170 return norm 171 } 172 173 // normalizeAppChildren normalizes values in the "child" section of App. 174 func normalizeAppChildren(children []*bspb.BatteryStats_App_Child, totalTimeHour float64) { 175 for _, c := range children { 176 c.Apk = normalizeAppApk(c.GetApk(), totalTimeHour) 177 } 178 } 179 180 // normalizeRepeatedMessage loops over a repeated message value and calls normalizeMessage 181 // for each if the message. 182 func normalizeRepeatedMessage(list interface{}, totalTimeHour float64) reflect.Value { 183 l := genericListOrDie(list) 184 out := reflect.MakeSlice(l.Type(), 0, l.Len()) 185 for i := 0; i < l.Len(); i++ { 186 item := l.Index(i) 187 norm := normalizeMessage(item.Interface().(proto.Message), totalTimeHour) 188 out = reflect.Append(out, reflect.ValueOf(norm)) 189 } 190 if out.Len() == 0 { 191 return reflect.Zero(l.Type()) 192 } 193 return out 194 } 195 196 // normalizeMessage extracts the struct within the message and calls normalizeStruct 197 // to normalize the value contained. 198 func normalizeMessage(p proto.Message, totalTimeHour float64) proto.Message { 199 in := reflect.ValueOf(p) 200 if in.IsNil() { 201 return nil 202 } 203 out := reflect.New(in.Type().Elem()) 204 normalizeStruct(out.Elem(), in.Elem(), totalTimeHour, checkinparse.BatteryStatsIDMap[reflect.TypeOf(p)]) 205 return out.Interface().(proto.Message) 206 } 207 208 // normalizeStruct traverses a struct value and normalizes each field 209 func normalizeStruct(out, in reflect.Value, totalTimeHour float64, ids map[int]bool) { 210 for i := 0; i < in.NumField(); i++ { 211 if f := in.Type().Field(i); strings.HasPrefix(f.Name, "XXX_") || f.PkgPath != "" { 212 continue // skip XXX_ and unexported fields 213 } 214 fieldPtrV := in.Field(i) 215 if fieldPtrV.IsNil() { 216 continue 217 } 218 normV := reflect.New(fieldPtrV.Type().Elem()) 219 normalizeValue(normV.Elem(), fieldPtrV.Elem(), in.String(), totalTimeHour, ids[i]) 220 out.Field(i).Set(normV) 221 } 222 } 223 224 // normalizeValue normalizes numerical values. 225 func normalizeValue(out, in reflect.Value, section string, totalTimeHour float64, id bool) { 226 switch in.Kind() { 227 case reflect.Float32, reflect.Float64: 228 if id { 229 out.Set(in) 230 } else { 231 out.SetFloat(roundToTwoDecimal(in.Float() / totalTimeHour)) 232 } 233 case reflect.Int32, reflect.Int64: 234 if id { 235 out.Set(in) 236 } else { 237 // Some rounding error is okay for the integer fields. 238 out.SetInt(int64(float64(in.Int()) / totalTimeHour)) 239 } 240 case reflect.String: 241 if !id { 242 reportWarningf("Tried to normalize a string for %s", section) 243 } 244 out.Set(in) 245 default: 246 reportWarningf("Normalizing %s type in %s not supported", in.Kind().String(), section) 247 } 248 }