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  }