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