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