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 package com.android.car.storagemonitoring;
17 
18 import android.annotation.NonNull;
19 import android.car.storagemonitoring.WearEstimateChange;
20 import android.util.JsonWriter;
21 import com.android.car.CarStorageMonitoringService;
22 import com.android.car.R;
23 import java.io.File;
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.time.Duration;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.function.BiPredicate;
32 import org.json.JSONArray;
33 import org.json.JSONException;
34 import org.json.JSONObject;
35 
36 /**
37  * This class represents the entire history of flash wear changes as tracked
38  * by CarStorageMonitoringService. It is a set of WearEstimateRecords.
39  *
40  * This is convertible to a list of WearEstimateChanges given policy about what constitutes
41  * acceptable or not acceptable degradation across change events. This policy is subject to
42  * modifications across OS versions, and as such it is not suitable for permanent storage.
43  */
44 public class WearHistory {
45     private final List<WearEstimateRecord> mWearHistory = new ArrayList<>();
46 
WearHistory()47     public WearHistory() {}
48 
WearHistory(@onNull JSONObject jsonObject)49     WearHistory(@NonNull JSONObject jsonObject) throws JSONException {
50         final JSONArray wearHistory = jsonObject.getJSONArray("wearHistory");
51         for (int i = 0; i < wearHistory.length(); ++i) {
52             JSONObject wearRecordJson = wearHistory.getJSONObject(i);
53             WearEstimateRecord wearRecord = new WearEstimateRecord(wearRecordJson);
54             add(wearRecord);
55         }
56     }
57 
fromRecords(@onNull WearEstimateRecord... records)58     public static WearHistory fromRecords(@NonNull WearEstimateRecord... records) {
59         WearHistory wearHistory = new WearHistory();
60         Arrays.stream(records).forEach(wearHistory::add);
61         return wearHistory;
62     }
63 
fromJson(@onNull File in)64     public static WearHistory fromJson(@NonNull File in) throws IOException, JSONException {
65         JSONObject jsonObject = new JSONObject(new String(Files.readAllBytes(in.toPath())));
66         return new WearHistory(jsonObject);
67     }
68 
writeToJson(@onNull JsonWriter out)69     public void writeToJson(@NonNull JsonWriter out) throws IOException {
70         out.beginObject();
71         out.name("wearHistory").beginArray();
72         for (WearEstimateRecord wearRecord : mWearHistory) {
73             wearRecord.writeToJson(out);
74         }
75         out.endArray();
76         out.endObject();
77     }
78 
add(@onNull WearEstimateRecord record)79     public boolean add(@NonNull WearEstimateRecord record) {
80         if (record != null && mWearHistory.add(record)) {
81             mWearHistory.sort((WearEstimateRecord o1, WearEstimateRecord o2) ->
82                 Long.valueOf(o1.getTotalCarServiceUptime()).compareTo(
83                     o2.getTotalCarServiceUptime()));
84             return true;
85         }
86         return false;
87     }
88 
size()89     public int size() {
90         return mWearHistory.size();
91     }
92 
get(int i)93     public WearEstimateRecord get(int i) {
94         return mWearHistory.get(i);
95     }
96 
getLast()97     public WearEstimateRecord getLast() {
98         return get(size() - 1);
99     }
100 
toWearEstimateChanges( long acceptableHoursPerOnePercentFlashWear)101     public List<WearEstimateChange> toWearEstimateChanges(
102             long acceptableHoursPerOnePercentFlashWear) {
103         // current technology allows us to detect wear in 10% increments
104         final int WEAR_PERCENTAGE_INCREMENT = 10;
105         final long acceptableWearRate = WEAR_PERCENTAGE_INCREMENT *
106                 Duration.ofHours(acceptableHoursPerOnePercentFlashWear).toMillis();
107         final int numRecords = size();
108 
109         if (numRecords == 0) return Collections.emptyList();
110 
111         List<WearEstimateChange> result = new ArrayList<>();
112         result.add(get(0).toWearEstimateChange(true));
113 
114         for (int i = 1; i < numRecords; ++i) {
115             WearEstimateRecord previousRecord = get(i - 1);
116             WearEstimateRecord currentRecord = get(i);
117             final long timeForChange =
118                     currentRecord.getTotalCarServiceUptime() -
119                             previousRecord.getTotalCarServiceUptime();
120             final boolean isAcceptableDegradation = timeForChange >= acceptableWearRate;
121             result.add(currentRecord.toWearEstimateChange(isAcceptableDegradation));
122         }
123 
124         return Collections.unmodifiableList(result);
125     }
126 
127     @Override
equals(Object other)128     public boolean equals(Object other) {
129         if (other instanceof WearHistory) {
130             WearHistory wi = (WearHistory)other;
131             return wi.mWearHistory.equals(mWearHistory);
132         }
133         return false;
134     }
135 
136     @Override
hashCode()137     public int hashCode() {
138         return mWearHistory.hashCode();
139     }
140 
141     @Override
toString()142     public String toString() {
143         return mWearHistory.stream().map(WearEstimateRecord::toString).reduce(
144             "WearHistory[size = " + size() + "] -> ",
145             (String s, String t) -> s + ", " + t);
146     }
147 }
148