1 /*
2  * Copyright (C) 2011 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.verifier;
18 
19 import android.app.AlertDialog;
20 import android.content.Context;
21 import android.os.AsyncTask;
22 import android.os.Build;
23 import android.os.Environment;
24 import android.os.FileUtils;
25 import android.os.ParcelFileDescriptor;
26 import android.util.Log;
27 
28 import com.android.compatibility.common.util.FileUtil;
29 import com.android.compatibility.common.util.IInvocationResult;
30 import com.android.compatibility.common.util.ResultHandler;
31 import com.android.compatibility.common.util.ZipUtil;
32 
33 import org.xmlpull.v1.XmlPullParserException;
34 
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.nio.file.Paths;
42 import java.nio.file.StandardCopyOption;
43 import java.text.SimpleDateFormat;
44 import java.util.Date;
45 import java.util.Locale;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
48 
49 /**
50  * Background task to generate a report and save it to external storage.
51  */
52 class ReportExporter extends AsyncTask<Void, Void, String> {
53     private static final String TAG = ReportExporter.class.getSimpleName();
54     private static final boolean DEBUG = true;
55 
56     public static final String REPORT_DIRECTORY = "VerifierReports";
57     public static final String LOGS_DIRECTORY = "ReportLogFiles";
58 
59     private static final Logger LOG = Logger.getLogger(ReportExporter.class.getName());
60     private static final String COMMAND_LINE_ARGS = "";
61     private static final String LOG_URL = null;
62     private static final String REFERENCE_URL = null;
63     private static final String SUITE_NAME_METADATA_KEY = "SuiteName";
64     private static final String SUITE_PLAN = "verifier";
65     private static final String SUITE_BUILD = "0";
66     private static final String ZIP_EXTENSION = ".zip";
67     private final long START_MS = System.currentTimeMillis();
68     private final long END_MS = START_MS;
69     private final Context mContext;
70     private final TestListAdapter mAdapter;
71 
ReportExporter(Context context, TestListAdapter adapter)72     ReportExporter(Context context, TestListAdapter adapter) {
73         this.mContext = context;
74         this.mAdapter = adapter;
75     }
76 
77     //
78     // Copy any ReportLog files created by XTS-Verifier tests into the temp report directory
79     // so that they will get ZIPped into the transmitted file.
80     //
copyReportFiles(File tempDir)81     private void copyReportFiles(File tempDir) {
82         if (DEBUG) {
83             Log.d(TAG, "copyReportFiles(" + tempDir.getAbsolutePath() + ")");
84         }
85 
86         File reportLogFolder =
87                 new File(Environment.getExternalStorageDirectory().getAbsolutePath()
88                         + File.separator
89                         + LOGS_DIRECTORY);
90 
91         copyFilesRecursively(reportLogFolder, tempDir);
92     }
93 
copyFilesRecursively(File source, File destFolder)94     private void copyFilesRecursively(File source, File destFolder) {
95         File[] files = source.listFiles();
96 
97         if (files == null) {
98             return;
99         }
100 
101         for (File file : files) {
102             Path src = Paths.get(file.getAbsolutePath());
103             Path dest = Paths.get(
104                     destFolder.getAbsolutePath()
105                             + File.separator
106                             + file.getName());
107             try {
108                 Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
109             } catch (IOException ex) {
110                 LOG.log(Level.WARNING, "Error copying ReportLog file. IOException: " + ex);
111             }
112             if (file.isDirectory()) {
113                 copyFilesRecursively(file, dest.toFile());
114             }
115         }
116     }
117 
118 
119     @Override
doInBackground(Void... params)120     protected String doInBackground(Void... params) {
121         if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
122             LOG.log(Level.WARNING, "External storage is not writable.");
123             return mContext.getString(R.string.no_storage);
124         }
125         IInvocationResult result;
126         try {
127             TestResultsReport report = new TestResultsReport(mContext, mAdapter);
128             result = report.generateResult();
129         } catch (Exception e) {
130             LOG.log(Level.WARNING, "Couldn't create test results report", e);
131             return mContext.getString(R.string.test_results_error);
132         }
133         // create a directory for XTS Verifier reports
134         File externalStorageDirectory = Environment.getExternalStorageDirectory();
135         File verifierReportsDir = new File(externalStorageDirectory, REPORT_DIRECTORY);
136         verifierReportsDir.mkdirs();
137 
138         String suiteName = Version.getMetadata(mContext, SUITE_NAME_METADATA_KEY);
139         // create a temporary directory for this particular report
140         File tempDir = new File(verifierReportsDir, getReportName(suiteName));
141         tempDir.mkdirs();
142 
143         // Pull in any ReportLogs
144         copyReportFiles(tempDir);
145 
146         // create a File object for a report ZIP file
147         File reportZipFile = new File(
148                 verifierReportsDir, getReportName(suiteName) + ZIP_EXTENSION);
149 
150         try {
151             // Serialize the report
152             String versionName = Version.getVersionName(mContext);
153             ResultHandler.writeResults(suiteName, versionName, SUITE_PLAN, SUITE_BUILD,
154                     result, tempDir, START_MS, END_MS, REFERENCE_URL, LOG_URL,
155                     COMMAND_LINE_ARGS, null);
156 
157             // copy formatting files to the temporary report directory
158             copyFormattingFiles(tempDir);
159 
160             // create a compressed ZIP file containing the temporary report directory
161             ZipUtil.createZip(tempDir, reportZipFile);
162         } catch (IOException | XmlPullParserException e) {
163             LOG.log(Level.WARNING, "I/O exception writing report to storage.", e);
164             return mContext.getString(R.string.no_storage_io_parser_exception);
165         } finally {
166             // delete the temporary directory and its files made for the report
167             FileUtil.recursiveDelete(tempDir);
168         }
169         saveReportOnInternalStorage(reportZipFile);
170         return mContext.getString(R.string.report_saved, reportZipFile.getPath());
171     }
172 
saveReportOnInternalStorage(File reportZipFile)173     private void saveReportOnInternalStorage(File reportZipFile) {
174         if (DEBUG) {
175             Log.d(TAG, "---- saveReportOnInternalStorage(" + reportZipFile.getAbsolutePath() + ")");
176         }
177         try {
178             ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
179                     reportZipFile, ParcelFileDescriptor.MODE_READ_ONLY);
180             InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
181 
182             File verifierDir = mContext.getDir(REPORT_DIRECTORY, Context.MODE_PRIVATE);
183             File verifierReport = new File(verifierDir, reportZipFile.getName());
184             FileOutputStream fos = new FileOutputStream(verifierReport);
185 
186             FileUtils.copy(is, fos);
187         } catch (Exception e) {
188             LOG.log(Level.WARNING, "I/O exception writing report to internal storage.", e);
189         }
190     }
191 
192     /**
193      * Copy the XML formatting files stored in the assets directory to the result output.
194      *
195      * @param resultsDir
196      */
copyFormattingFiles(File resultsDir)197     private void copyFormattingFiles(File resultsDir) {
198         for (String resultFileName : ResultHandler.RESULT_RESOURCES) {
199             InputStream rawStream = null;
200             try {
201                 rawStream = mContext.getAssets().open(
202                         String.format("report/%s", resultFileName));
203             } catch (IOException e) {
204                 LOG.log(Level.WARNING, "Failed to load " + resultFileName + " from assets.");
205             }
206             if (rawStream != null) {
207                 File resultFile = new File(resultsDir, resultFileName);
208                 try {
209                     FileUtil.writeToFile(rawStream, resultFile);
210                 } catch (IOException e) {
211                     LOG.log(Level.WARNING, "Failed to write " + resultFileName + " to a file.");
212                 }
213             }
214         }
215     }
216 
getReportName(String suiteName)217     private String getReportName(String suiteName) {
218         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss", Locale.ENGLISH);
219         String date = dateFormat.format(new Date());
220         return String.format("%s-%s-%s-%s-%s-%s",
221                 date, suiteName, Build.MANUFACTURER, Build.PRODUCT, Build.DEVICE, Build.ID);
222     }
223 
224     @Override
onPostExecute(String result)225     protected void onPostExecute(String result) {
226         new AlertDialog.Builder(mContext)
227                 .setMessage(result)
228                 .setPositiveButton(android.R.string.ok, null)
229                 .show();
230     }
231 }
232