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 
17 package com.android.cts.storageapp;
18 
19 import android.content.Context;
20 import android.system.Os;
21 import android.system.OsConstants;
22 import android.system.StructUtsname;
23 import android.util.Log;
24 
25 import junit.framework.AssertionFailedError;
26 
27 import java.io.BufferedReader;
28 import java.io.ByteArrayOutputStream;
29 import java.io.File;
30 import java.io.FileDescriptor;
31 import java.io.FileOutputStream;
32 import java.io.FileReader;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.util.Arrays;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 
40 public class Utils {
41     public static final String TAG = "StorageApp";
42 
43     public static final String PKG_A = "com.android.cts.storageapp_a";
44     public static final String PKG_B = "com.android.cts.storageapp_b";
45 
46     // You will pry my kibibytes from my cold dead hands! But to make test
47     // results easier to debug, we'll use kilobytes...
48     public static final long KB_IN_BYTES = 1000;
49     public static final long MB_IN_BYTES = KB_IN_BYTES * 1000;
50     public static final long GB_IN_BYTES = MB_IN_BYTES * 1000;
51 
52     public static final long DATA_INT = (2 + 3 + 5 + 13 + 17 + 19 + 23) * MB_IN_BYTES;
53     public static final long DATA_EXT = (7 + 11) * MB_IN_BYTES;
54     public static final long DATA_ALL = DATA_INT + DATA_EXT; // 100MB
55 
56     public static final long CACHE_INT = (3 + 5 + 17 + 19 + 23) * MB_IN_BYTES;
57     public static final long CACHE_EXT = (11) * MB_IN_BYTES;
58     public static final long CACHE_ALL = CACHE_INT + CACHE_EXT; // 78MB
59 
60     public static final long CODE_ALL = 29 * MB_IN_BYTES;
61 
useSpace(Context c)62     public static void useSpace(Context c) throws Exception {
63         // We use prime numbers for all values so that we can easily identify
64         // which file(s) are missing from broken test results.
65         useWrite(makeUniqueFile(c.getFilesDir()), 2 * MB_IN_BYTES);
66         useWrite(makeUniqueFile(c.getCodeCacheDir()), 3 * MB_IN_BYTES);
67         useWrite(makeUniqueFile(c.getCacheDir()), 5 * MB_IN_BYTES);
68         useWrite(makeUniqueFile(c.getExternalFilesDir("meow")), 7 * MB_IN_BYTES);
69         useWrite(makeUniqueFile(c.getExternalCacheDir()), 11 * MB_IN_BYTES);
70 
71         useFallocate(makeUniqueFile(c.getFilesDir()), 13 * MB_IN_BYTES);
72         useFallocate(makeUniqueFile(c.getCodeCacheDir()), 17 * MB_IN_BYTES);
73         useFallocate(makeUniqueFile(c.getCacheDir()), 19 * MB_IN_BYTES);
74         final File subdir = makeUniqueFile(c.getCacheDir());
75         Os.mkdir(subdir.getAbsolutePath(), 0700);
76         useFallocate(makeUniqueFile(subdir), 23 * MB_IN_BYTES);
77 
78         useWrite(makeUniqueFile(c.getObbDir()), 29 * MB_IN_BYTES);
79     }
80 
assertAtLeast(long expected, long actual)81     public static void assertAtLeast(long expected, long actual) {
82         if (actual < expected) {
83             throw new AssertionFailedError("Expected at least " + expected + " but was " + actual
84                     + " [" + android.os.Process.myUserHandle() + "]");
85         }
86     }
87 
assertMostlyEquals(long expected, long actual)88     public static void assertMostlyEquals(long expected, long actual) {
89         assertMostlyEquals(expected, actual, 500 * KB_IN_BYTES);
90     }
91 
assertMostlyEquals(long expected, long actual, long delta)92     public static void assertMostlyEquals(long expected, long actual, long delta) {
93         if (Math.abs(expected - actual) > delta) {
94             throw new AssertionFailedError("Expected roughly " + expected + " but was " + actual
95                     + " [" + android.os.Process.myUserHandle() + "]");
96         }
97     }
98 
makeUniqueFile(File dir)99     public static File makeUniqueFile(File dir) {
100         return new File(dir, Long.toString(System.nanoTime()));
101     }
102 
useWrite(File file, long size)103     public static File useWrite(File file, long size) throws Exception {
104         try (FileOutputStream os = new FileOutputStream(file)) {
105             final byte[] buf = new byte[1024];
106             while (size > 0) {
107                 os.write(buf, 0, (int) Math.min(buf.length, size));
108                 size -= buf.length;
109             }
110         }
111         return file;
112     }
113 
useFallocate(File file, long length, long time)114     public static File useFallocate(File file, long length, long time) throws Exception {
115         final File res = useFallocate(file, length);
116         file.setLastModified(time);
117         return res;
118     }
119 
useFallocate(File file, long length)120     public static File useFallocate(File file, long length) throws Exception {
121         final FileDescriptor fd = Os.open(file.getAbsolutePath(),
122                 OsConstants.O_CREAT | OsConstants.O_RDWR | OsConstants.O_TRUNC, 0700);
123         try {
124             Os.posix_fallocate(fd, 0, length);
125         } finally {
126             Os.close(fd);
127         }
128         return file;
129     }
130 
getSizeManual(File dir)131     public static long getSizeManual(File dir) throws Exception {
132         return getSizeManual(dir, false);
133     }
134 
getSizeManual(File dir, boolean excludeObb)135     public static long getSizeManual(File dir, boolean excludeObb) throws Exception {
136         long size = getAllocatedSize(dir);
137         for (File f : dir.listFiles()) {
138             if (f.isDirectory()) {
139                 if (excludeObb && f.getName().equalsIgnoreCase("obb")
140                         && f.getParentFile().getName().equalsIgnoreCase("Android")) {
141                     Log.d(TAG, "Ignoring OBB directory " + f);
142                 } else {
143                     size += getSizeManual(f, excludeObb);
144                 }
145             } else {
146                 size += getAllocatedSize(f);
147             }
148         }
149         return size;
150     }
151 
getAllocatedSize(File f)152     private static long getAllocatedSize(File f) throws Exception {
153         return Os.lstat(f.getAbsolutePath()).st_blocks * 512;
154     }
155 
deleteContents(File dir)156     public static boolean deleteContents(File dir) {
157         File[] files = dir.listFiles();
158         boolean success = true;
159         if (files != null) {
160             for (File file : files) {
161                 if (file.isDirectory()) {
162                     success &= deleteContents(file);
163                 }
164                 if (!file.delete()) {
165                     success = false;
166                 }
167             }
168         }
169         return success;
170     }
171 
shouldHaveQuota(StructUtsname uname)172     public static boolean shouldHaveQuota(StructUtsname uname) throws Exception {
173         try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) {
174             String line;
175             while ((line = br.readLine()) != null) {
176                 final String[] fields = line.split(" ");
177                 final String target = fields[1];
178                 final String format = fields[2];
179 
180                 if (target.equals("/data") && !format.equals("ext4")) {
181                     Log.d(TAG, "Assuming no quota support because /data is " + format);
182                     return false;
183                 }
184             }
185         }
186 
187         final Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)").matcher(uname.release);
188         if (!matcher.find()) {
189             throw new IllegalStateException("Failed to parse version: " + uname.release);
190         }
191         final int major = Integer.parseInt(matcher.group(1));
192         final int minor = Integer.parseInt(matcher.group(2));
193         return (major > 3 || (major == 3 && minor >= 18));
194     }
195 
logCommand(String... cmd)196     public static void logCommand(String... cmd) throws Exception {
197         final Process proc = new ProcessBuilder(cmd).redirectErrorStream(true).start();
198 
199         final ByteArrayOutputStream buf = new ByteArrayOutputStream();
200         copy(proc.getInputStream(), buf);
201         final int res = proc.waitFor();
202 
203         Log.d(TAG, Arrays.toString(cmd) + " result " + res + ":");
204         Log.d(TAG, buf.toString());
205     }
206 
207     /** Shamelessly lifted from libcore.io.Streams */
copy(InputStream in, OutputStream out)208     public static int copy(InputStream in, OutputStream out) throws IOException {
209         int total = 0;
210         byte[] buffer = new byte[8192];
211         int c;
212         while ((c = in.read(buffer)) != -1) {
213             total += c;
214             out.write(buffer, 0, c);
215         }
216         return total;
217     }
218 }
219