16dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath/* 26dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Copyright (C) 2011 The Android Open Source Project 36dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * 46dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); you may not 56dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * use this file except in compliance with the License. You may obtain a copy of 66dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * the License at 76dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * 86dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 96dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * 106dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Unless required by applicable law or agreed to in writing, software 116dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 126dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 136dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * License for the specific language governing permissions and limitations under 146dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * the License. 156dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 166dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamathpackage android.speech.tts; 176dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 186dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamathimport android.os.SystemClock; 196dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamathimport android.text.TextUtils; 2067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport android.util.Log; 216dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 226dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath/** 236dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Writes data about a given speech synthesis request to the event logs. 246dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * The data that is logged includes the calling app, length of the utterance, 256dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * speech rate / pitch and the latency and overall time taken. 266dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * 276dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Note that {@link EventLogger#onStopped()} and {@link EventLogger#onError()} 2867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * might be called from any thread, but on {@link EventLogger#onAudioDataWritten()} and 296dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * {@link EventLogger#onComplete()} must be called from a single thread 306dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * (usually the audio playback thread} 316dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 326dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamathclass EventLogger { 336dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private final SynthesisRequest mRequest; 346dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private final String mServiceApp; 35492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final int mCallerUid; 36492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final int mCallerPid; 376dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private final long mReceivedTime; 386dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private long mPlaybackStartTime = -1; 396dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private volatile long mRequestProcessingStartTime = -1; 406dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private volatile long mEngineStartTime = -1; 416dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private volatile long mEngineCompleteTime = -1; 426dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 436dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private volatile boolean mError = false; 446dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private volatile boolean mStopped = false; 456dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private boolean mLogWritten = false; 466dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 47492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath EventLogger(SynthesisRequest request, int callerUid, int callerPid, String serviceApp) { 486dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mRequest = request; 49492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerUid = callerUid; 50492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerPid = callerPid; 516dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mServiceApp = serviceApp; 526dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mReceivedTime = SystemClock.elapsedRealtime(); 536dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 546dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 556dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 566dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that this request has been selected from 576dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * the processing queue for processing. Engine latency / total time 586dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * is measured from this baseline. 596dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 606dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath public void onRequestProcessingStart() { 616dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mRequestProcessingStartTime = SystemClock.elapsedRealtime(); 626dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 636dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 646dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 656dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that a chunk of data has been received from 666dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * the engine. Might be called multiple times. 676dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 686dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath public void onEngineDataReceived() { 696dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (mEngineStartTime == -1) { 706dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mEngineStartTime = SystemClock.elapsedRealtime(); 716dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 726dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 736dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 746dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 756dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that the engine has finished processing data. 766dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Will be called exactly once. 776dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 786dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath public void onEngineComplete() { 796dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mEngineCompleteTime = SystemClock.elapsedRealtime(); 806dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 816dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 826dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 836dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that audio playback has started for some section 846dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * of the synthesis. This is normally some amount of time after the engine 8567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * has synthesized data and varies depending on utterances and 866dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * other audio currently in the queue. 876dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 8867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath public void onAudioDataWritten() { 896dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath // For now, keep track of only the first chunk of audio 906dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath // that was played. 916dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (mPlaybackStartTime == -1) { 926dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mPlaybackStartTime = SystemClock.elapsedRealtime(); 936dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 946dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 956dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 966dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 976dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that the current synthesis was stopped. 986dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Latency numbers are not reported for stopped syntheses. 996dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 1006dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath public void onStopped() { 1016dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mStopped = false; 1026dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1036dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1046dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 1056dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that the current synthesis resulted in 1066dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * an error. This is logged using {@link EventLogTags#writeTtsSpeakFailure}. 1076dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 1086dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath public void onError() { 1096dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mError = true; 1106dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1116dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1126dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 1136dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Notifies the logger that the current synthesis has completed. 1146dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * All available data is not logged. 1156dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 1166dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath public void onWriteData() { 1176dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (mLogWritten) { 1186dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath return; 1196dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } else { 1206dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mLogWritten = true; 1216dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1226dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1236dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath long completionTime = SystemClock.elapsedRealtime(); 12467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // onAudioDataWritten() should normally always be called if an 1256dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath // error does not occur. 1266dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (mError || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) { 127492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath EventLogTags.writeTtsSpeakFailure(mServiceApp, mCallerUid, mCallerPid, 1286dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath getUtteranceLength(), getLocaleString(), 1296dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mRequest.getSpeechRate(), mRequest.getPitch()); 1306dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath return; 1316dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1326dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1336dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath // We don't report stopped syntheses because their overall 1346dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath // total time spent will be innacurate (will not correlate with 1356dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath // the length of the utterance). 1366dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (mStopped) { 1376dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath return; 1386dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1396dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1406dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath final long audioLatency = mPlaybackStartTime - mReceivedTime; 1416dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath final long engineLatency = mEngineStartTime - mRequestProcessingStartTime; 1426dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime; 14367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath 144492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath EventLogTags.writeTtsSpeakSuccess(mServiceApp, mCallerUid, mCallerPid, 1456dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath getUtteranceLength(), getLocaleString(), 1466dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mRequest.getSpeechRate(), mRequest.getPitch(), 1476dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath engineLatency, engineTotal, audioLatency); 1486dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1496dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1506dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 1516dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * @return the length of the utterance for the given synthesis, 0 1526dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * if the utterance was {@code null}. 1536dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 1546dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private int getUtteranceLength() { 1556dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath final String utterance = mRequest.getText(); 1566dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath return utterance == null ? 0 : utterance.length(); 1576dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1586dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1596dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath /** 1606dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * Returns a formatted locale string from the synthesis params of the 1616dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath * form lang-country-variant. 1626dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath */ 1636dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private String getLocaleString() { 1646dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath StringBuilder sb = new StringBuilder(mRequest.getLanguage()); 1656dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (!TextUtils.isEmpty(mRequest.getCountry())) { 1666dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath sb.append('-'); 1676dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath sb.append(mRequest.getCountry()); 1686dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1696dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath if (!TextUtils.isEmpty(mRequest.getVariant())) { 1706dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath sb.append('-'); 1716dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath sb.append(mRequest.getVariant()); 1726dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1736dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1746dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1756dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath return sb.toString(); 1766dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath } 1776dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 1786dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath} 179