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