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