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 package com.android.car.storagemonitoring; 18 19 import android.annotation.NonNull; 20 import android.car.storagemonitoring.WearEstimate; 21 import android.car.storagemonitoring.WearEstimateChange; 22 import android.util.JsonWriter; 23 24 import org.json.JSONException; 25 import org.json.JSONObject; 26 27 import java.io.IOException; 28 import java.time.Instant; 29 import java.util.Objects; 30 31 /** 32 * This class represents a wear estimate record as stored by CarStorageMonitoringService. 33 * 34 * Because it is meant to map 1:1 to on-disk records, it is not directly convertible to a 35 * WearEstimateChange because it does not include information about "acceptable degradation". 36 */ 37 public class WearEstimateRecord { 38 39 private final WearEstimate mOldWearEstimate; 40 private final WearEstimate mNewWearEstimate; 41 private final long mTotalCarServiceUptime; 42 private final Instant mUnixTimestamp; 43 WearEstimateRecord(@onNull WearEstimate oldWearEstimate, @NonNull WearEstimate newWearEstimate, long totalCarServiceUptime, @NonNull Instant unixTimestamp)44 public WearEstimateRecord(@NonNull WearEstimate oldWearEstimate, 45 @NonNull WearEstimate newWearEstimate, 46 long totalCarServiceUptime, 47 @NonNull Instant unixTimestamp) { 48 mOldWearEstimate = Objects.requireNonNull(oldWearEstimate); 49 mNewWearEstimate = Objects.requireNonNull(newWearEstimate); 50 mTotalCarServiceUptime = totalCarServiceUptime; 51 long unixEpochSeconds = Objects.requireNonNull(unixTimestamp).getEpochSecond(); 52 mUnixTimestamp = Instant.ofEpochSecond(unixEpochSeconds); 53 } 54 WearEstimateRecord(@onNull JSONObject json)55 WearEstimateRecord(@NonNull JSONObject json) throws JSONException { 56 mOldWearEstimate = new WearEstimate(json.getJSONObject("oldWearEstimate")); 57 mNewWearEstimate = new WearEstimate(json.getJSONObject("newWearEstimate")); 58 mTotalCarServiceUptime = json.getLong("totalCarServiceUptime"); 59 long unixEpochSeconds = Instant.ofEpochMilli(json.getLong("unixTimestamp")) 60 .getEpochSecond(); 61 mUnixTimestamp = Instant.ofEpochSecond(unixEpochSeconds); 62 } 63 writeToJson(@onNull JsonWriter jsonWriter)64 void writeToJson(@NonNull JsonWriter jsonWriter) throws IOException { 65 jsonWriter.beginObject(); 66 jsonWriter.name("oldWearEstimate"); mOldWearEstimate.writeToJson(jsonWriter); 67 jsonWriter.name("newWearEstimate"); mNewWearEstimate.writeToJson(jsonWriter); 68 jsonWriter.name("totalCarServiceUptime").value(mTotalCarServiceUptime); 69 jsonWriter.name("unixTimestamp").value(mUnixTimestamp.toEpochMilli()); 70 jsonWriter.endObject(); 71 } 72 getOldWearEstimate()73 public WearEstimate getOldWearEstimate() { 74 return mOldWearEstimate; 75 } 76 getNewWearEstimate()77 public WearEstimate getNewWearEstimate() { 78 return mNewWearEstimate; 79 } 80 getTotalCarServiceUptime()81 public long getTotalCarServiceUptime() { 82 return mTotalCarServiceUptime; 83 } 84 getUnixTimestamp()85 public Instant getUnixTimestamp() { 86 return mUnixTimestamp; 87 } 88 toWearEstimateChange(boolean isAcceptableDegradation)89 WearEstimateChange toWearEstimateChange(boolean isAcceptableDegradation) { 90 return new WearEstimateChange(mOldWearEstimate, 91 mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp, isAcceptableDegradation); 92 } 93 94 @Override equals(Object other)95 public boolean equals(Object other) { 96 if (other instanceof WearEstimateRecord) { 97 WearEstimateRecord wer = (WearEstimateRecord)other; 98 if (!wer.mOldWearEstimate.equals(mOldWearEstimate)) return false; 99 if (!wer.mNewWearEstimate.equals(mNewWearEstimate)) return false; 100 if (wer.mTotalCarServiceUptime != mTotalCarServiceUptime) return false; 101 return wer.mUnixTimestamp.equals(mUnixTimestamp); 102 } 103 return false; 104 } 105 106 /** 107 * Checks whether this record tracks the same change as the provided estimate. 108 * That means the two objects have the same values for: 109 * <ul> 110 * <li>old wear indicators</li> 111 * <li>new wear indicators</li> 112 * <li>uptime at event</li> 113 * </ul> 114 */ isSameAs(@onNull WearEstimateChange wearEstimateChange)115 public boolean isSameAs(@NonNull WearEstimateChange wearEstimateChange) { 116 if (!mOldWearEstimate.equals(wearEstimateChange.oldEstimate)) return false; 117 if (!mNewWearEstimate.equals(wearEstimateChange.newEstimate)) return false; 118 return (mTotalCarServiceUptime == wearEstimateChange.uptimeAtChange); 119 } 120 121 @Override hashCode()122 public int hashCode() { 123 return Objects.hash(mOldWearEstimate, 124 mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp); 125 } 126 127 @Override toString()128 public String toString() { 129 return String.format("WearEstimateRecord {" + 130 "mOldWearEstimate = %s, " + 131 "mNewWearEstimate = %s, " + 132 "mTotalCarServiceUptime = %d, " + 133 "mUnixTimestamp = %s}", 134 mOldWearEstimate, mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp); 135 } 136 137 public static final class Builder { 138 private WearEstimate mOldWearEstimate = null; 139 private WearEstimate mNewWearEstimate = null; 140 private long mTotalCarServiceUptime = -1; 141 private Instant mUnixTimestamp = null; 142 Builder()143 private Builder() {} 144 newBuilder()145 public static Builder newBuilder() { 146 return new Builder(); 147 } 148 fromWearEstimate(@onNull WearEstimate wearEstimate)149 public Builder fromWearEstimate(@NonNull WearEstimate wearEstimate) { 150 mOldWearEstimate = Objects.requireNonNull(wearEstimate); 151 return this; 152 } 153 toWearEstimate(@onNull WearEstimate wearEstimate)154 public Builder toWearEstimate(@NonNull WearEstimate wearEstimate) { 155 mNewWearEstimate = Objects.requireNonNull(wearEstimate); 156 return this; 157 } 158 atUptime(long uptime)159 public Builder atUptime(long uptime) { 160 if (uptime < 0) { 161 throw new IllegalArgumentException("uptime must be >= 0"); 162 } 163 mTotalCarServiceUptime = uptime; 164 return this; 165 } 166 atTimestamp(@onNull Instant now)167 public Builder atTimestamp(@NonNull Instant now) { 168 mUnixTimestamp = Objects.requireNonNull(now); 169 return this; 170 } 171 build()172 public WearEstimateRecord build() { 173 if (mOldWearEstimate == null || mNewWearEstimate == null || 174 mTotalCarServiceUptime < 0 || mUnixTimestamp == null) { 175 throw new IllegalStateException("malformed builder state"); 176 } 177 return new WearEstimateRecord( 178 mOldWearEstimate, mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp); 179 } 180 } 181 } 182