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.Arrays; 32 import java.util.Collections; 33 import java.util.List; 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 Arrays.stream(records).forEach(wearHistory::add); 60 return wearHistory; 61 } 62 fromJson(@onNull File in)63 public static WearHistory fromJson(@NonNull File in) throws IOException, JSONException { 64 JSONObject jsonObject = new JSONObject(new String(Files.readAllBytes(in.toPath()))); 65 return new WearHistory(jsonObject); 66 } 67 writeToJson(@onNull JsonWriter out)68 public void writeToJson(@NonNull JsonWriter out) throws IOException { 69 out.beginObject(); 70 out.name("wearHistory").beginArray(); 71 for (WearEstimateRecord wearRecord : mWearHistory) { 72 wearRecord.writeToJson(out); 73 } 74 out.endArray(); 75 out.endObject(); 76 } 77 add(@onNull WearEstimateRecord record)78 public boolean add(@NonNull WearEstimateRecord record) { 79 if (record != null && mWearHistory.add(record)) { 80 mWearHistory.sort((WearEstimateRecord o1, WearEstimateRecord o2) -> 81 Long.valueOf(o1.getTotalCarServiceUptime()).compareTo( 82 o2.getTotalCarServiceUptime())); 83 return true; 84 } 85 return false; 86 } 87 size()88 public int size() { 89 return mWearHistory.size(); 90 } 91 get(int i)92 public WearEstimateRecord get(int i) { 93 return mWearHistory.get(i); 94 } 95 getLast()96 public WearEstimateRecord getLast() { 97 return get(size() - 1); 98 } 99 toWearEstimateChanges( long acceptableHoursPerOnePercentFlashWear)100 public List<WearEstimateChange> toWearEstimateChanges( 101 long acceptableHoursPerOnePercentFlashWear) { 102 // current technology allows us to detect wear in 10% increments 103 final int WEAR_PERCENTAGE_INCREMENT = 10; 104 final long acceptableWearRate = WEAR_PERCENTAGE_INCREMENT * 105 Duration.ofHours(acceptableHoursPerOnePercentFlashWear).toMillis(); 106 final int numRecords = size(); 107 108 if (numRecords == 0) return Collections.emptyList(); 109 110 List<WearEstimateChange> result = new ArrayList<>(); 111 result.add(get(0).toWearEstimateChange(true)); 112 113 for (int i = 1; i < numRecords; ++i) { 114 WearEstimateRecord previousRecord = get(i - 1); 115 WearEstimateRecord currentRecord = get(i); 116 final long timeForChange = 117 currentRecord.getTotalCarServiceUptime() - 118 previousRecord.getTotalCarServiceUptime(); 119 final boolean isAcceptableDegradation = timeForChange >= acceptableWearRate; 120 result.add(currentRecord.toWearEstimateChange(isAcceptableDegradation)); 121 } 122 123 return Collections.unmodifiableList(result); 124 } 125 126 @Override equals(Object other)127 public boolean equals(Object other) { 128 if (other instanceof WearHistory) { 129 WearHistory wi = (WearHistory)other; 130 return wi.mWearHistory.equals(mWearHistory); 131 } 132 return false; 133 } 134 135 @Override hashCode()136 public int hashCode() { 137 return mWearHistory.hashCode(); 138 } 139 140 @Override toString()141 public String toString() { 142 return mWearHistory.stream().map(WearEstimateRecord::toString).reduce( 143 "WearHistory[size = " + size() + "] -> ", 144 (String s, String t) -> s + ", " + t); 145 } 146 } 147