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