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