• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.annotation.Nullable;
20 import android.car.storagemonitoring.LifetimeWriteInfo;
21 import android.util.Slog;
22 
23 import com.android.car.CarLog;
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 import java.io.File;
27 import java.io.IOException;
28 import java.nio.file.Files;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 import java.util.Objects;
33 
34 /**
35  * <p>Loads lifetime write data for mounted filesystems via sysfs.</p>
36  *
37  * <p>ext4 and f2fs currently offer this information via
38  * /sys/fs/<i>type</i>/<i>partition</i>/lifetime_write_kbytes VFS entry points.</p>
39  */
40 public class SysfsLifetimeWriteInfoProvider implements LifetimeWriteInfoProvider {
41     private static final String TAG = CarLog.tagFor(SysfsLifetimeWriteInfoProvider.class);
42 
43     private static final String DEFAULT_PATH = "/sys/fs/";
44     private static final String[] KNOWN_FILESYSTEMS = new String[] {
45         "ext4",
46         "f2fs"
47     };
48     private static final String FILENAME = "lifetime_write_kbytes";
49 
50     private final File mWriteInfosPath;
51 
SysfsLifetimeWriteInfoProvider()52     public SysfsLifetimeWriteInfoProvider() {
53         this(new File(DEFAULT_PATH));
54     }
55 
56     @VisibleForTesting
SysfsLifetimeWriteInfoProvider(File writeInfosPath)57     SysfsLifetimeWriteInfoProvider(File writeInfosPath) {
58         mWriteInfosPath = writeInfosPath;
59     }
60 
61     @Nullable
tryParse(File dir)62     private LifetimeWriteInfo tryParse(File dir) {
63         File writefile = new File(dir, FILENAME);
64         if (!writefile.exists() || !writefile.isFile()) {
65             Slog.d(TAG, writefile + " not a valid source of lifetime writes");
66             return null;
67         }
68         List<String> datalines;
69         try {
70             datalines = Files.readAllLines(writefile.toPath());
71         } catch (IOException e) {
72             Slog.e(TAG, "unable to read write info from " + writefile, e);
73             return null;
74         }
75         if (datalines == null || datalines.size() != 1) {
76             Slog.e(TAG, "unable to read valid write info from " + writefile);
77             return null;
78         }
79         String data = datalines.get(0);
80         try {
81             long writtenBytes = 1024L * Long.parseLong(data);
82             if (writtenBytes < 0) {
83                 Slog.e(TAG, "file at location " + writefile + " contained a negative data amount "
84                         + data + ". Ignoring.");
85                 return null;
86             } else {
87                 return new LifetimeWriteInfo(
88                     dir.getName(),
89                     dir.getParentFile().getName(),
90                     writtenBytes);
91             }
92         } catch (NumberFormatException e) {
93             Slog.e(TAG, "unable to read valid write info from " + writefile, e);
94             return null;
95         }
96     }
97 
98     @Override
99     @NonNull
load()100     public LifetimeWriteInfo[] load() {
101         List<LifetimeWriteInfo> writeInfos = new ArrayList<>();
102 
103         for (String fstype : KNOWN_FILESYSTEMS) {
104             File fspath = new File(mWriteInfosPath, fstype);
105             if (!fspath.exists() || !fspath.isDirectory()) continue;
106             File[] files = fspath.listFiles(File::isDirectory);
107             if (files == null) {
108                 Slog.e(TAG, "there are no directories at location " + fspath.getAbsolutePath());
109                 continue;
110             }
111             Arrays.stream(files)
112                 .map(this::tryParse)
113                 .filter(Objects::nonNull)
114                 .forEach(writeInfos::add);
115         }
116 
117         return writeInfos.toArray(new LifetimeWriteInfo[0]);
118     }
119 }
120