• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.car.telemetry.util;
18 
19 import android.annotation.NonNull;
20 import android.car.builtin.util.Slogf;
21 import android.os.PersistableBundle;
22 import android.util.AtomicFile;
23 
24 import com.android.car.CarLog;
25 
26 import com.google.protobuf.MessageLite;
27 
28 import java.io.Closeable;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.nio.file.Files;
34 import java.nio.file.Paths;
35 
36 /** Utility class for car telemetry I/O operations. */
37 public class IoUtils {
38 
39     /**
40      * Reads a {@link PersistableBundle} from the file system.
41      *
42      * @param bundleFile file location of the PersistableBundle.
43      * @return {@link PersistableBundle} stored in the given file.
44      * @throws IOException for read failure.
45      */
46     @NonNull
readBundle(@onNull File bundleFile)47     public static PersistableBundle readBundle(@NonNull File bundleFile) throws IOException {
48         AtomicFile atomicFile = new AtomicFile(bundleFile);
49         try (FileInputStream fis = atomicFile.openRead()) {
50             return PersistableBundle.readFromStream(fis);
51         }
52     }
53 
54     /**
55      * Saves a {@link PersistableBundle} to a file.
56      *
57      * @param dir      directory to save the bundle to.
58      * @param fileName file name to save the file as.
59      * @param bundle   to be saved.
60      * @throws IOException for write failure.
61      */
writeBundle( @onNull File dir, @NonNull String fileName, @NonNull PersistableBundle bundle)62     public static void writeBundle(
63             @NonNull File dir, @NonNull String fileName, @NonNull PersistableBundle bundle)
64             throws IOException {
65         writeBundle(new File(dir, fileName), bundle);
66     }
67 
68     /**
69      * Saves a {@link PersistableBundle} to a file.
70      *
71      * @param dest   file location to save the {@link PersistableBundle}.
72      * @param bundle to be saved.
73      * @throws IOException for write failure.
74      */
writeBundle(@onNull File dest, @NonNull PersistableBundle bundle)75     public static void writeBundle(@NonNull File dest, @NonNull PersistableBundle bundle)
76             throws IOException {
77         AtomicFile atomicFile = new AtomicFile(dest);
78         try (FileOutputStream fos = atomicFile.startWrite()) {
79             try {
80                 bundle.writeToStream(fos);
81                 atomicFile.finishWrite(fos);
82             } catch (IOException e) {
83                 atomicFile.failWrite(fos);
84                 throw e;
85             }
86         }
87     }
88 
89     /**
90      * Saves a protobuf message to file.
91      *
92      * @param dir directory to save the file to.
93      * @param fileName name to save the file as.
94      * @param proto to be saved.
95      * @throws IOException for write failure.
96      */
writeProto( @onNull File dir, @NonNull String fileName, @NonNull MessageLite proto)97     public static void writeProto(
98             @NonNull File dir, @NonNull String fileName, @NonNull MessageLite proto)
99             throws IOException {
100         writeProto(new File(dir, fileName), proto);
101     }
102 
103     /**
104      * Savea  protobuf message to file.
105      *
106      * @param dest  file location to save the protobuf message.
107      * @param proto to be saved.
108      * @throws IOException for write failure.
109      */
writeProto( @onNull File dest, @NonNull MessageLite proto)110     public static void writeProto(
111             @NonNull File dest, @NonNull MessageLite proto) throws IOException {
112         AtomicFile atomicFile = new AtomicFile(dest);
113         try (FileOutputStream fos = atomicFile.startWrite()) {
114             try {
115                 fos.write(proto.toByteArray());
116                 atomicFile.finishWrite(fos);
117             } catch (IOException e) {
118                 atomicFile.failWrite(fos);
119                 throw e;
120             }
121         }
122     }
123 
124     /**
125      * Deletes the file silently from the file system if it exists. Return true for success, false
126      * for failure.
127      */
deleteSilently(@onNull File directory, @NonNull String fileName)128     public static boolean deleteSilently(@NonNull File directory, @NonNull String fileName) {
129         try {
130             return Files.deleteIfExists(Paths.get(
131                     directory.getAbsolutePath(), fileName));
132         } catch (IOException e) {
133             Slogf.w(CarLog.TAG_TELEMETRY, "Failed to delete file " + fileName + " in directory "
134                     + directory.getAbsolutePath(), e);
135             // TODO(b/197153560): record failure
136         }
137         return false;
138     }
139 
140     /**
141      * Deletes all files silently from the directory. This method does not delete recursively.
142      */
deleteAllSilently(@onNull File directory)143     public static void deleteAllSilently(@NonNull File directory) {
144         File[] files = directory.listFiles();
145         if (files == null) {
146             Slogf.i(CarLog.TAG_TELEMETRY, "Skip deleting the empty dir %s", directory.getName());
147             return;
148         }
149         for (File file : files) {
150             if (!file.delete()) {
151                 Slogf.w(CarLog.TAG_TELEMETRY, "Failed to delete file " + file.getName()
152                         + " in directory " + directory.getAbsolutePath());
153             }
154         }
155     }
156 
157     /**
158      * Deletes all files in the specified directories that are stale/older than some threshold.
159      * This method does not delete recursively.
160      *
161      * @param staleThresholdMillis the threshold to classify a file as stale.
162      * @param dirs                 the directories to remove stale files from.
163      */
deleteOldFiles(long staleThresholdMillis, @NonNull File... dirs)164     public static void deleteOldFiles(long staleThresholdMillis, @NonNull File... dirs) {
165         long currTimeMs = System.currentTimeMillis();
166         for (File dir : dirs) {
167             File[] files = dir.listFiles();
168             if (files == null) {
169                 Slogf.i(CarLog.TAG_TELEMETRY, "Skip deleting the empty dir %s", dir.getName());
170                 continue;
171             }
172             for (File file : files) {
173                 // delete stale data
174                 if (file.lastModified() + staleThresholdMillis < currTimeMs) {
175                     file.delete();
176                 }
177             }
178         }
179     }
180 
181     /** Quietly closes Java Closeables, ignoring IOException. */
closeQuietly(@onNull Closeable closeable)182     public static void closeQuietly(@NonNull Closeable closeable) {
183         try {
184             closeable.close();
185         } catch (IOException e) {
186             // Ignore
187         }
188     }
189 }
190