1 /* 2 * Copyright (C) 2009 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.voicedialer; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.os.Build; 22 import android.speech.srec.WaveHeader; 23 import android.text.format.DateFormat; 24 import android.util.Config; 25 import android.util.Log; 26 27 import java.io.BufferedWriter; 28 import java.io.ByteArrayOutputStream; 29 import java.io.File; 30 import java.io.FileFilter; 31 import java.io.FileOutputStream; 32 import java.io.FileWriter; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.OutputStream; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 40 /** 41 * This class logs the inputs and results of a recognition session to 42 * the files listed below, which reside in 43 * /data/data/com.android.voicedialer/app_logdir. 44 * The files have the date encoded in the name so that they will sort in 45 * time order. The newest RecognizerLogger.MAX_FILES are kept, 46 * and the rest deleted to limit space used in the file system. 47 * <ul> 48 * <li> datename.wav - what the microphone heard. 49 * <li> datename.log - contact list, results, errors, etc. 50 * </ul> 51 */ 52 public class RecognizerLogger { 53 54 private static final String TAG = "RecognizerLogger"; 55 56 private static final String LOGDIR = "logdir"; 57 private static final String ENABLED = "enabled"; 58 59 private static final int MAX_FILES = 20; 60 61 private final String mDatedPath; 62 private final BufferedWriter mWriter; 63 64 /** 65 * Determine if logging is enabled. If the 66 * @param context needed to reference the logging directory. 67 * @return true if logging is enabled, determined by the 'enabled' file. 68 */ 69 public static boolean isEnabled(Context context) { 70 File dir = context.getDir(LOGDIR, 0); 71 File enabled = new File(dir, ENABLED); 72 return enabled.exists(); 73 } 74 75 /** 76 * Enable logging. 77 * @param context needed to reference the logging directory. 78 */ 79 public static void enable(Context context) { 80 try { 81 File dir = context.getDir(LOGDIR, 0); 82 File enabled = new File(dir, ENABLED); 83 enabled.createNewFile(); 84 } 85 catch (IOException e) { 86 Log.e(TAG, "enableLogging " + e); 87 } 88 } 89 90 /** 91 * Disable logging. 92 * @param context needed to reference the logging directory. 93 */ 94 public static void disable(Context context) { 95 try { 96 File dir = context.getDir(LOGDIR, 0); 97 File enabled = new File(dir, ENABLED); 98 enabled.delete(); 99 } 100 catch (SecurityException e) { 101 Log.e(TAG, "disableLogging " + e); 102 } 103 } 104 105 /** 106 * Constructor 107 * @param dataDir directory to contain the log files. 108 */ 109 public RecognizerLogger(Context context) throws IOException { 110 if (Config.LOGD) Log.d(TAG, "RecognizerLogger"); 111 112 // generate new root filename 113 File dir = context.getDir(LOGDIR, 0); 114 mDatedPath = dir.toString() + File.separator + "log_" + 115 DateFormat.format("yyyy_MM_dd_kk_mm_ss", 116 System.currentTimeMillis()); 117 118 // delete oldest files 119 deleteOldest(".wav"); 120 deleteOldest(".log"); 121 122 // generate new text output log file 123 mWriter = new BufferedWriter(new FileWriter(mDatedPath + ".log"), 8192); 124 mWriter.write(Build.FINGERPRINT); 125 mWriter.newLine(); 126 } 127 128 /** 129 * Write a line into the text log file. 130 */ 131 public void logLine(String msg) { 132 try { 133 mWriter.write(msg); 134 mWriter.newLine(); 135 } 136 catch (IOException e) { 137 Log.e(TAG, "logLine exception: " + e); 138 } 139 } 140 141 /** 142 * Write a header for the NBest lines into the text log file. 143 */ 144 public void logNbestHeader() { 145 logLine("Nbest *****************"); 146 } 147 148 /** 149 * Write the list of contacts into the text log file. 150 * @param contacts 151 */ 152 public void logContacts(List<VoiceContact> contacts) { 153 logLine("Contacts *****************"); 154 for (VoiceContact vc : contacts) logLine(vc.toString()); 155 try { 156 mWriter.flush(); 157 } 158 catch (IOException e) { 159 Log.e(TAG, "logContacts exception: " + e); 160 } 161 } 162 163 /** 164 * Write a list of Intents into the text log file. 165 * @param intents 166 */ 167 public void logIntents(ArrayList<Intent> intents) { 168 logLine("Intents *********************"); 169 StringBuffer sb = new StringBuffer(); 170 for (Intent intent : intents) { 171 logLine(intent.toString() + " " + RecognizerEngine.SENTENCE_EXTRA + "=" + 172 intent.getStringExtra(RecognizerEngine.SENTENCE_EXTRA)); 173 } 174 try { 175 mWriter.flush(); 176 } 177 catch (IOException e) { 178 Log.e(TAG, "logIntents exception: " + e); 179 } 180 } 181 182 /** 183 * Close the text log file. 184 * @throws IOException 185 */ 186 public void close() throws IOException { 187 mWriter.close(); 188 } 189 190 /** 191 * Delete oldest files with a given suffix, if more than MAX_FILES. 192 * @param suffix delete oldest files with this suffix. 193 */ 194 private void deleteOldest(final String suffix) { 195 FileFilter ff = new FileFilter() { 196 public boolean accept(File f) { 197 String name = f.getName(); 198 return name.startsWith("log_") && name.endsWith(suffix); 199 } 200 }; 201 File[] files = (new File(mDatedPath)).getParentFile().listFiles(ff); 202 Arrays.sort(files); 203 204 for (int i = 0; i < files.length - MAX_FILES; i++) { 205 files[i].delete(); 206 } 207 } 208 209 /** 210 * InputStream wrapper which will log the contents to a WAV file. 211 * @param inputStream 212 * @param sampleRate 213 * @return 214 */ 215 public InputStream logInputStream(final InputStream inputStream, final int sampleRate) { 216 final ByteArrayOutputStream baos = new ByteArrayOutputStream(sampleRate * 2 * 20); 217 218 return new InputStream() { 219 220 public int available() throws IOException { 221 return inputStream.available(); 222 } 223 224 public int read(byte[] b, int offset, int length) throws IOException { 225 int rtn = inputStream.read(b, offset, length); 226 if (rtn > 0) baos.write(b, offset, rtn); 227 return rtn; 228 } 229 230 public int read(byte[] b) throws IOException { 231 int rtn = inputStream.read(b); 232 if (rtn > 0) baos.write(b, 0, rtn); 233 return rtn; 234 } 235 236 public int read() throws IOException { 237 int rtn = inputStream.read(); 238 if (rtn > 0) baos.write(rtn); 239 return rtn; 240 } 241 242 public long skip(long n) throws IOException { 243 throw new UnsupportedOperationException(); 244 } 245 246 public void close() throws IOException { 247 try { 248 OutputStream out = new FileOutputStream(mDatedPath + ".wav"); 249 try { 250 byte[] pcm = baos.toByteArray(); 251 WaveHeader hdr = new WaveHeader(WaveHeader.FORMAT_PCM, 252 (short)1, sampleRate, (short)16, pcm.length); 253 hdr.write(out); 254 out.write(pcm); 255 } 256 finally { 257 out.close(); 258 } 259 } 260 finally { 261 inputStream.close(); 262 baos.close(); 263 } 264 } 265 }; 266 } 267 268 } 269