Home | History | Annotate | Download | only in storagemonitoring
      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 
     47     public WearHistory() {}
     48 
     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 
     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 
     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 
     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 
     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 
     89     public int size() {
     90         return mWearHistory.size();
     91     }
     92 
     93     public WearEstimateRecord get(int i) {
     94         return mWearHistory.get(i);
     95     }
     96 
     97     public WearEstimateRecord getLast() {
     98         return get(size() - 1);
     99     }
    100 
    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
    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
    137     public int hashCode() {
    138         return mWearHistory.hashCode();
    139     }
    140 
    141     @Override
    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