1 /*
2  * Copyright (C) 2016 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 android.support.test.aupt;
18 
19 import android.app.ActivityManager;
20 import android.app.Instrumentation;
21 import android.os.Environment;
22 import android.os.ParcelFileDescriptor;
23 import android.os.RemoteException;
24 import android.util.Log;
25 
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileNotFoundException;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.text.SimpleDateFormat;
34 import java.util.Date;
35 import java.util.Locale;
36 
37 public class FilesystemUtil {
38     private static final String TAG = FilesystemUtil.class.getSimpleName();
39 
40     /** Save the output of a process to a file */
saveProcessOutput(Instrumentation instr, String command, File file)41     public static void saveProcessOutput(Instrumentation instr, String command, File file)
42             throws IOException {
43         Log.d(TAG, String.format("Saving command \"%s\" output into file %s",
44                 command, file.getAbsolutePath()));
45 
46         OutputStream out = new FileOutputStream(file);
47         saveProcessOutput(instr, command, out);
48         out.close();
49     }
50 
51     /** Send the output of a process to an OutputStream. */
saveProcessOutput(Instrumentation instr, String command, OutputStream out)52     public static void saveProcessOutput(Instrumentation instr, String command, OutputStream out)
53             throws IOException {
54         try {
55             // First, try to execute via our UiAutomation
56             ParcelFileDescriptor pfd = instr.getUiAutomation().executeShellCommand(command);
57             pipe(new ParcelFileDescriptor.AutoCloseInputStream(pfd), out);
58         } catch (IllegalStateException ise) {
59             // If we don't have a UiAutomation, we'll get an IllegalStatException;
60             // so try to do it via an exec()
61             Process process = Runtime.getRuntime().exec(command);
62             pipe(process.getInputStream(), out);
63 
64             // Wait for our process to finish
65             try {
66                 process.waitFor();
67             } catch (InterruptedException ie) {
68                 throw new IOException("Thread interrupted waiting for command: " + command);
69             }
70 
71             // Make sure it succeeded.
72             if (process.exitValue() != 0) {
73                 throw new IOException("Failed to save output of command: " + command);
74             }
75         }
76     }
77 
78     /** Save a bugreport to the given file */
saveBugreport(Instrumentation instr, String filename)79     public static void saveBugreport(Instrumentation instr, String filename)
80             throws IOException, InterruptedException {
81         ByteArrayOutputStream baos = new ByteArrayOutputStream();
82         String cmdline = String.format("/system/bin/sh -c /system/bin/bugreport>%s",
83                 templateToFilename(filename));
84         saveProcessOutput(instr, cmdline, baos);
85         baos.close();
86     }
87 
88     /** Save a bugreport to the given file */
saveBugreportz(Instrumentation instr)89     public static void saveBugreportz(Instrumentation instr) throws IOException {
90         try {
91             ActivityManager.getService().requestBugReport(ActivityManager.BUGREPORT_OPTION_FULL);
92         } catch (RemoteException e) {
93             throw new IOException("Could not capture bugreportz", e);
94         }
95     }
96 
97     /** Save annotated Meminfo to our default logging directory */
dumpMeminfo(Instrumentation instr, String notes)98     public static void dumpMeminfo(Instrumentation instr, String notes) {
99         long epochSeconds = System.currentTimeMillis() / 1000;
100         File outputDir = new File(Environment.getExternalStorageDirectory(), "meminfo");
101         Log.i(TAG, outputDir.toString());
102         if (!outputDir.exists()) {
103             boolean yes  = outputDir.mkdirs();
104             Log.i(TAG, yes ? "created" : "not created");
105         }
106         File outputFile = new File(outputDir, String.format("%d.txt", epochSeconds));
107         Log.i(TAG, outputFile.toString());
108         FileOutputStream fos = null;
109 
110         try {
111             fos = new FileOutputStream(outputFile);
112             fos.write(String.format("notes: %s\n\n", notes).getBytes());
113 
114             saveProcessOutput(instr, "dumpsys meminfo -c", fos);
115             fos.close();
116         } catch (FileNotFoundException e) {
117             Log.e(TAG, "exception while dumping meminfo", e);
118         } catch (IOException e) {
119             Log.e(TAG, "exception while dumping meminfo", e);
120         }
121     }
122 
123     /** Splice the date into the "%s" in a file name */
templateToFilename(String filenameTemplate)124     public static String templateToFilename(String filenameTemplate) {
125         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
126         return String.format(filenameTemplate, sdf.format(new Date()));
127     }
128 
129     /** Pipe an inputstream to an outputstream. This matches Apache's IOUtils::copy */
pipe(InputStream in, OutputStream out)130     private static void pipe(InputStream in, OutputStream out) throws IOException {
131         byte[] buffer = new byte[4096];
132         int bytesRead = 0;
133 
134         try {
135             while (bytesRead >= 0) {
136                 out.write(buffer, 0, bytesRead);
137                 bytesRead = in.read(buffer);
138             }
139         } finally {
140             in.close();
141             out.flush();
142         }
143     }
144 }
145