1 /* 2 * Copyright (C) 2022 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.settings.fuelgauge.batteryusage.db; 18 19 import android.content.ContentValues; 20 21 import androidx.room.Entity; 22 import androidx.room.PrimaryKey; 23 24 import com.android.settings.fuelgauge.BatteryUtils; 25 import com.android.settings.fuelgauge.batteryusage.BatteryInformation; 26 import com.android.settings.fuelgauge.batteryusage.ConvertUtils; 27 28 import com.google.errorprone.annotations.CanIgnoreReturnValue; 29 30 import java.util.Locale; 31 32 /** A {@link Entity} class to save battery states snapshot into database. */ 33 @Entity 34 public class BatteryState { 35 @PrimaryKey(autoGenerate = true) 36 private long mId; 37 38 // Records the app relative information. 39 public final long uid; 40 public final long userId; 41 public final String packageName; 42 public final long timestamp; 43 public final int consumerType; 44 public final boolean isFullChargeCycleStart; 45 public final String batteryInformation; 46 /** 47 * This field is filled only when build type is "userdebug". 48 * For now, Java Proto Lite is recommended by the Android team as the more lightweight solution 49 * designed specifically for mobile apps to process protobuf. 50 * However, converting protobuf to string through Java Proto Lite needs to parse it into a bytes 51 * field first, which leads to the strings saved in our database are encoded and hard to 52 * understand. 53 * To make it easier to debug in our daily development, this field is added. 54 * It will not be filled for the real users. 55 */ 56 public final String batteryInformationDebug; 57 BatteryState( long uid, long userId, String packageName, long timestamp, int consumerType, boolean isFullChargeCycleStart, String batteryInformation, String batteryInformationDebug)58 public BatteryState( 59 long uid, 60 long userId, 61 String packageName, 62 long timestamp, 63 int consumerType, 64 boolean isFullChargeCycleStart, 65 String batteryInformation, 66 String batteryInformationDebug) { 67 // Records the app relative information. 68 this.uid = uid; 69 this.userId = userId; 70 this.packageName = packageName; 71 this.timestamp = timestamp; 72 this.consumerType = consumerType; 73 this.isFullChargeCycleStart = isFullChargeCycleStart; 74 this.batteryInformation = batteryInformation; 75 this.batteryInformationDebug = batteryInformationDebug; 76 } 77 78 /** Sets the auto-generated content ID. */ setId(long id)79 public void setId(long id) { 80 this.mId = id; 81 } 82 83 /** Gets the auto-generated content ID. */ getId()84 public long getId() { 85 return mId; 86 } 87 88 @Override toString()89 public String toString() { 90 final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(timestamp); 91 final BatteryInformation batteryInformationInstance = 92 BatteryUtils.parseProtoFromString( 93 batteryInformation, BatteryInformation.getDefaultInstance()); 94 final StringBuilder builder = new StringBuilder() 95 .append("\nBatteryState{") 96 .append(String.format(Locale.US, 97 "\n\tpackage=%s|uid=%d|userId=%d", packageName, uid, userId)) 98 .append(String.format(Locale.US, "\n\ttimestamp=%s|consumer=%d|isStart=%b", 99 recordAtDateTime, consumerType, isFullChargeCycleStart)) 100 .append(String.format(Locale.US, "\n\tbatteryInfo=")) 101 .append(batteryInformationInstance.toString()); 102 return builder.toString(); 103 } 104 105 106 /** Creates new {@link BatteryState} from {@link ContentValues}. */ create(ContentValues contentValues)107 public static BatteryState create(ContentValues contentValues) { 108 Builder builder = BatteryState.newBuilder(); 109 if (contentValues.containsKey("uid")) { 110 builder.setUid(contentValues.getAsLong("uid")); 111 } 112 if (contentValues.containsKey("userId")) { 113 builder.setUserId(contentValues.getAsLong("userId")); 114 } 115 if (contentValues.containsKey("packageName")) { 116 builder.setPackageName(contentValues.getAsString("packageName")); 117 } 118 if (contentValues.containsKey("timestamp")) { 119 builder.setTimestamp(contentValues.getAsLong("timestamp")); 120 } 121 if (contentValues.containsKey("consumerType")) { 122 builder.setConsumerType(contentValues.getAsInteger("consumerType")); 123 } 124 if (contentValues.containsKey("isFullChargeCycleStart")) { 125 builder.setIsFullChargeCycleStart( 126 contentValues.getAsBoolean("isFullChargeCycleStart")); 127 } 128 if (contentValues.containsKey("batteryInformation")) { 129 builder.setBatteryInformation(contentValues.getAsString("batteryInformation")); 130 } 131 if (contentValues.containsKey("batteryInformationDebug")) { 132 builder.setBatteryInformationDebug( 133 contentValues.getAsString("batteryInformationDebug")); 134 } 135 return builder.build(); 136 } 137 138 /** Creates a new {@link Builder} instance. */ newBuilder()139 public static Builder newBuilder() { 140 return new Builder(); 141 } 142 143 /** A convenience builder class to improve readability. */ 144 public static class Builder { 145 private long mUid; 146 private long mUserId; 147 private String mPackageName; 148 private long mTimestamp; 149 private int mConsumerType; 150 private boolean mIsFullChargeCycleStart; 151 private String mBatteryInformation; 152 private String mBatteryInformationDebug; 153 154 /** Sets the uid. */ 155 @CanIgnoreReturnValue setUid(long uid)156 public Builder setUid(long uid) { 157 this.mUid = uid; 158 return this; 159 } 160 161 /** Sets the user ID. */ 162 @CanIgnoreReturnValue setUserId(long userId)163 public Builder setUserId(long userId) { 164 this.mUserId = userId; 165 return this; 166 } 167 168 /** Sets the package name. */ 169 @CanIgnoreReturnValue setPackageName(String packageName)170 public Builder setPackageName(String packageName) { 171 this.mPackageName = packageName; 172 return this; 173 } 174 175 /** Sets the timestamp. */ 176 @CanIgnoreReturnValue setTimestamp(long timestamp)177 public Builder setTimestamp(long timestamp) { 178 this.mTimestamp = timestamp; 179 return this; 180 } 181 182 /** Sets the consumer type. */ 183 @CanIgnoreReturnValue setConsumerType(int consumerType)184 public Builder setConsumerType(int consumerType) { 185 this.mConsumerType = consumerType; 186 return this; 187 } 188 189 /** Sets whether is the full charge cycle start. */ 190 @CanIgnoreReturnValue setIsFullChargeCycleStart(boolean isFullChargeCycleStart)191 public Builder setIsFullChargeCycleStart(boolean isFullChargeCycleStart) { 192 this.mIsFullChargeCycleStart = isFullChargeCycleStart; 193 return this; 194 } 195 196 /** Sets the battery information. */ 197 @CanIgnoreReturnValue setBatteryInformation(String batteryInformation)198 public Builder setBatteryInformation(String batteryInformation) { 199 this.mBatteryInformation = batteryInformation; 200 return this; 201 } 202 203 /** Sets the battery information debug string. */ 204 @CanIgnoreReturnValue setBatteryInformationDebug(String batteryInformationDebug)205 public Builder setBatteryInformationDebug(String batteryInformationDebug) { 206 this.mBatteryInformationDebug = batteryInformationDebug; 207 return this; 208 } 209 210 /** Builds the BatteryState. */ build()211 public BatteryState build() { 212 return new BatteryState( 213 mUid, 214 mUserId, 215 mPackageName, 216 mTimestamp, 217 mConsumerType, 218 mIsFullChargeCycleStart, 219 mBatteryInformation, 220 mBatteryInformationDebug); 221 } 222 Builder()223 private Builder() {} 224 } 225 } 226