1b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata/* 2b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Copyright (C) 2017 The Android Open Source Project 3b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * 4b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Licensed under the Apache License, Version 2.0 (the "License"); 5b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * you may not use this file except in compliance with the License. 6b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * You may obtain a copy of the License at 7b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * 8b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * http://www.apache.org/licenses/LICENSE-2.0 9b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * 10b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Unless required by applicable law or agreed to in writing, software 11b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * distributed under the License is distributed on an "AS IS" BASIS, 12b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * See the License for the specific language governing permissions and 14b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * limitations under the License. 15b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata */ 16b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granatapackage com.android.car.storagemonitoring; 17b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 18b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport android.annotation.NonNull; 19b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport android.annotation.Nullable; 20b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport android.util.Log; 21b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport com.android.car.CarLog; 22b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport com.android.internal.annotations.VisibleForTesting; 23b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.io.File; 24b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.io.IOException; 25b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.util.List; 26b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.util.Optional; 27b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.util.Scanner; 28b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.util.regex.MatchResult; 29b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granataimport java.util.regex.Pattern; 30b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 31b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata/** 32b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Loads wear information from the UFS sysfs entry points. 33b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * sysfs exposes UFS lifetime data in /sys/devices/soc/624000.ufshc/health 34b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * The first line of the file contains the UFS version 35b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Subsequent lines contains individual information points in the format: 36b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Health Descriptor[Byte offset 0x%d]: %31s = 0x%hx 37b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata * Of these we care about the key values bPreEOLInfo and bDeviceLifeTimeEstA bDeviceLifeTimeEstB 38b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata */ 39b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granatapublic class UfsWearInformationProvider implements WearInformationProvider { 40b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata private static File DEFAULT_FILE = 41b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata new File("/sys/devices/soc/624000.ufshc/health"); 42b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 43b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata private File mFile; 44b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 45b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata public UfsWearInformationProvider() { 46b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata this(DEFAULT_FILE); 47b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 48b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 49b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata @VisibleForTesting 50b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata public UfsWearInformationProvider(@NonNull File file) { 51b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata mFile = file; 52b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 53b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 54b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata @Nullable 55b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata @Override 56b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata public WearInformation load() { 57b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata List<String> lifetimeData; 58b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata try { 59b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata lifetimeData = java.nio.file.Files.readAllLines(mFile.toPath()); 60b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } catch (IOException e) { 61b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Log.w(CarLog.TAG_STORAGE, "error reading " + mFile, e); 62b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata return null; 63b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 64b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata if (lifetimeData == null || lifetimeData.size() < 4) { 65b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata return null; 66b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 67b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 68b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Pattern infoPattern = Pattern.compile( 69b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata "Health Descriptor\\[Byte offset 0x\\d+\\]: (\\w+) = 0x([0-9a-fA-F]+)"); 70b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 71b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Optional<Integer> lifetimeA = Optional.empty(); 72b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Optional<Integer> lifetimeB = Optional.empty(); 73b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Optional<Integer> eol = Optional.empty(); 74b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 75b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata for(String lifetimeInfo : lifetimeData) { 76b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Scanner scanner = new Scanner(lifetimeInfo); 77b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata if (null == scanner.findInLine(infoPattern)) { 78b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata continue; 79b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 80b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata MatchResult match = scanner.match(); 81b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata if (match.groupCount() != 2) { 82b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata continue; 83b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 84b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata String name = match.group(1); 85b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata String value = "0x" + match.group(2); 86b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata try { 87b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata switch (name) { 88b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata case "bPreEOLInfo": 89b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata eol = Optional.of(Integer.decode(value)); 90b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata break; 91b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata case "bDeviceLifeTimeEstA": 92b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata lifetimeA = Optional.of(Integer.decode(value)); 93b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata break; 94b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata case "bDeviceLifeTimeEstB": 95b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata lifetimeB = Optional.of(Integer.decode(value)); 96b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata break; 97b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 98b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } catch (NumberFormatException e) { 99b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata Log.w(CarLog.TAG_STORAGE, 100b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata "trying to decode key " + name + " value " + value + " didn't parse properly", e); 101b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 102b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 103b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 104b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata if (!lifetimeA.isPresent() || !lifetimeB.isPresent() || !eol.isPresent()) { 105b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata return null; 106b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 107b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata 108b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata return new WearInformation(convertLifetime(lifetimeA.get()), 109b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata convertLifetime(lifetimeB.get()), 110b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata adjustEol(eol.get())); 111b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata } 112b2c48c3eefcc8825a0138f5405af6d64e91791d2Enrico Granata} 113