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