| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.voicedialer; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Build; |
| import android.speech.srec.WaveHeader; |
| import android.text.format.DateFormat; |
| import android.util.Log; |
| |
| import java.io.BufferedWriter; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.FileOutputStream; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * This class logs the inputs and results of a recognition session to |
| * the files listed below, which reside in |
| * /data/data/com.android.voicedialer/app_logdir. |
| * The files have the date encoded in the name so that they will sort in |
| * time order. The newest RecognizerLogger.MAX_FILES are kept, |
| * and the rest deleted to limit space used in the file system. |
| * <ul> |
| * <li> datename.wav - what the microphone heard. |
| * <li> datename.log - contact list, results, errors, etc. |
| * </ul> |
| */ |
| public class RecognizerLogger { |
| |
| private static final String TAG = "RecognizerLogger"; |
| |
| private static final String LOGDIR = "logdir"; |
| private static final String ENABLED = "enabled"; |
| |
| private static final int MAX_FILES = 20; |
| |
| private final String mDatedPath; |
| private final BufferedWriter mWriter; |
| |
| /** |
| * Determine if logging is enabled. If the |
| * @param context needed to reference the logging directory. |
| * @return true if logging is enabled, determined by the 'enabled' file. |
| */ |
| public static boolean isEnabled(Context context) { |
| File dir = context.getDir(LOGDIR, 0); |
| File enabled = new File(dir, ENABLED); |
| return enabled.exists(); |
| } |
| |
| /** |
| * Enable logging. |
| * @param context needed to reference the logging directory. |
| */ |
| public static void enable(Context context) { |
| try { |
| File dir = context.getDir(LOGDIR, 0); |
| File enabled = new File(dir, ENABLED); |
| enabled.createNewFile(); |
| } |
| catch (IOException e) { |
| Log.e(TAG, "enableLogging " + e); |
| } |
| } |
| |
| /** |
| * Disable logging. |
| * @param context needed to reference the logging directory. |
| */ |
| public static void disable(Context context) { |
| try { |
| File dir = context.getDir(LOGDIR, 0); |
| File enabled = new File(dir, ENABLED); |
| enabled.delete(); |
| } |
| catch (SecurityException e) { |
| Log.e(TAG, "disableLogging " + e); |
| } |
| } |
| |
| /** |
| * Constructor |
| * @param dataDir directory to contain the log files. |
| */ |
| public RecognizerLogger(Context context) throws IOException { |
| if (false) Log.d(TAG, "RecognizerLogger"); |
| |
| // generate new root filename |
| File dir = context.getDir(LOGDIR, 0); |
| mDatedPath = dir.toString() + File.separator + "log_" + |
| DateFormat.format("yyyy_MM_dd_kk_mm_ss", |
| System.currentTimeMillis()); |
| |
| // delete oldest files |
| deleteOldest(".wav"); |
| deleteOldest(".log"); |
| |
| // generate new text output log file |
| mWriter = new BufferedWriter(new FileWriter(mDatedPath + ".log"), 8192); |
| mWriter.write(Build.FINGERPRINT); |
| mWriter.newLine(); |
| } |
| |
| /** |
| * Write a line into the text log file. |
| */ |
| public void logLine(String msg) { |
| try { |
| mWriter.write(msg); |
| mWriter.newLine(); |
| } |
| catch (IOException e) { |
| Log.e(TAG, "logLine exception: " + e); |
| } |
| } |
| |
| /** |
| * Write a header for the NBest lines into the text log file. |
| */ |
| public void logNbestHeader() { |
| logLine("Nbest *****************"); |
| } |
| |
| /** |
| * Write the list of contacts into the text log file. |
| * @param contacts |
| */ |
| public void logContacts(List<VoiceContact> contacts) { |
| logLine("Contacts *****************"); |
| for (VoiceContact vc : contacts) logLine(vc.toString()); |
| try { |
| mWriter.flush(); |
| } |
| catch (IOException e) { |
| Log.e(TAG, "logContacts exception: " + e); |
| } |
| } |
| |
| /** |
| * Write a list of Intents into the text log file. |
| * @param intents |
| */ |
| public void logIntents(ArrayList<Intent> intents) { |
| logLine("Intents *********************"); |
| StringBuffer sb = new StringBuffer(); |
| for (Intent intent : intents) { |
| logLine(intent.toString() + " " + RecognizerEngine.SENTENCE_EXTRA + "=" + |
| intent.getStringExtra(RecognizerEngine.SENTENCE_EXTRA)); |
| } |
| try { |
| mWriter.flush(); |
| } |
| catch (IOException e) { |
| Log.e(TAG, "logIntents exception: " + e); |
| } |
| } |
| |
| /** |
| * Close the text log file. |
| * @throws IOException |
| */ |
| public void close() throws IOException { |
| mWriter.close(); |
| } |
| |
| /** |
| * Delete oldest files with a given suffix, if more than MAX_FILES. |
| * @param suffix delete oldest files with this suffix. |
| */ |
| private void deleteOldest(final String suffix) { |
| FileFilter ff = new FileFilter() { |
| public boolean accept(File f) { |
| String name = f.getName(); |
| return name.startsWith("log_") && name.endsWith(suffix); |
| } |
| }; |
| File[] files = (new File(mDatedPath)).getParentFile().listFiles(ff); |
| Arrays.sort(files); |
| |
| for (int i = 0; i < files.length - MAX_FILES; i++) { |
| files[i].delete(); |
| } |
| } |
| |
| /** |
| * InputStream wrapper which will log the contents to a WAV file. |
| * @param inputStream |
| * @param sampleRate |
| * @return |
| */ |
| public InputStream logInputStream(final InputStream inputStream, final int sampleRate) { |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(sampleRate * 2 * 20); |
| |
| return new InputStream() { |
| |
| public int available() throws IOException { |
| return inputStream.available(); |
| } |
| |
| public int read(byte[] b, int offset, int length) throws IOException { |
| int rtn = inputStream.read(b, offset, length); |
| if (rtn > 0) baos.write(b, offset, rtn); |
| return rtn; |
| } |
| |
| public int read(byte[] b) throws IOException { |
| int rtn = inputStream.read(b); |
| if (rtn > 0) baos.write(b, 0, rtn); |
| return rtn; |
| } |
| |
| public int read() throws IOException { |
| int rtn = inputStream.read(); |
| if (rtn > 0) baos.write(rtn); |
| return rtn; |
| } |
| |
| public long skip(long n) throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void close() throws IOException { |
| try { |
| OutputStream out = new FileOutputStream(mDatedPath + ".wav"); |
| try { |
| byte[] pcm = baos.toByteArray(); |
| WaveHeader hdr = new WaveHeader(WaveHeader.FORMAT_PCM, |
| (short)1, sampleRate, (short)16, pcm.length); |
| hdr.write(out); |
| out.write(pcm); |
| } |
| finally { |
| out.close(); |
| } |
| } |
| finally { |
| inputStream.close(); |
| baos.close(); |
| } |
| } |
| }; |
| } |
| |
| } |