1 /*
2  * Copyright (C) 2015 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 android.voiceinteraction.service;
18 
19 import android.app.VoiceInteractor;
20 import android.app.VoiceInteractor.Prompt;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.AsyncTask;
24 import android.os.Bundle;
25 import android.service.voice.VoiceInteractionSession;
26 import android.service.voice.VoiceInteractionSession.ConfirmationRequest;
27 import android.service.voice.VoiceInteractionSession.PickOptionRequest;
28 import android.util.Log;
29 import android.voiceinteraction.common.Utils;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 
35 public class MainInteractionSession extends VoiceInteractionSession {
36     static final String TAG = "MainInteractionSession";
37 
38     Intent mStartIntent;
39     List<MyTask> mUsedTasks = new ArrayList<MyTask>();
40 
MainInteractionSession(Context context)41     MainInteractionSession(Context context) {
42         super(context);
43     }
44 
45     @Override
onCreate()46     public void onCreate() {
47         super.onCreate();
48         Intent sessionStarted = new Intent();
49         sessionStarted.setClassName("android.voiceinteraction.cts",
50                 "android.voiceinteraction.cts.VoiceInteractionTestReceiver");
51         getContext().sendBroadcast(sessionStarted);
52     }
53 
54     @Override
onDestroy()55     public void onDestroy() {
56         Log.i(TAG, "Canceling the Asynctask in onDestroy()");
57         for (MyTask t : mUsedTasks) {
58             t.cancel(true);
59         }
60         super.onDestroy();
61     }
62 
63     @Override
onShow(Bundle args, int showFlags)64     public void onShow(Bundle args, int showFlags) {
65         super.onShow(args, showFlags);
66         mStartIntent = args.getParcelable("intent");
67         if (mStartIntent != null) {
68             startVoiceActivity(mStartIntent);
69         } else if ((showFlags & SHOW_SOURCE_ACTIVITY) == SHOW_SOURCE_ACTIVITY) {
70             // Verify args
71             if (args == null
72                     || !Utils.PRIVATE_OPTIONS_VALUE.equals(
73                             args.getString(Utils.PRIVATE_OPTIONS_KEY))) {
74                 throw new IllegalArgumentException("Incorrect arguments for SHOW_SOURCE_ACTIVITY");
75             }
76         }
77     }
78 
assertPromptFromTestApp(CharSequence prompt, Bundle extras)79     void assertPromptFromTestApp(CharSequence prompt, Bundle extras) {
80         String str = prompt.toString();
81         if (str.equals(Utils.TEST_PROMPT)) {
82             Log.i(TAG, "prompt received ok from TestApp in Session");
83         } else {
84             Utils.addErrorResult(extras, "Invalid prompt received: " + str);
85         }
86     }
87 
newTask()88     synchronized MyTask newTask() {
89         MyTask t = new MyTask();
90         mUsedTasks.add(t);
91         return t;
92     }
93 
94     @Override
onGetSupportedCommands(String[] commands)95     public boolean[] onGetSupportedCommands(String[] commands) {
96         boolean[] results = new boolean[commands.length];
97         Log.i(TAG, "in onGetSupportedCommands");
98         for (int idx = 0; idx < commands.length; idx++) {
99             results[idx] = Utils.TEST_COMMAND.equals(commands[idx]);
100             Log.i(TAG, "command " + commands[idx] + ", support = " + results[idx]);
101         }
102         return results;
103     }
104 
105     @Override
onRequestConfirmation(ConfirmationRequest request)106     public void onRequestConfirmation(ConfirmationRequest request) {
107         Bundle extras = request.getExtras();
108         CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
109         Log.i(TAG, "in Session onRequestConfirmation recvd. prompt=" + prompt +
110                 ", extras=" + Utils.toBundleString(extras));
111         assertPromptFromTestApp(prompt, extras);
112         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setExtras(extras);
113         if (isTestTypeCancel(extras)) {
114             Log.i(TAG, "Sending Cancel.");
115             newTask().execute(
116                     asyncTaskArg.setTestType(Utils.TestCaseType.CONFIRMATION_REQUEST_CANCEL_TEST));
117         } else {
118             Log.i(TAG, "in Session sending sendConfirmationResult. " +
119                     Utils.toBundleString(extras));
120             newTask().execute(
121                     asyncTaskArg.setTestType(Utils.TestCaseType.CONFIRMATION_REQUEST_TEST));
122         }
123     }
124 
125     @Override
onRequestCompleteVoice(CompleteVoiceRequest request)126     public void onRequestCompleteVoice(CompleteVoiceRequest request) {
127         Bundle extras = request.getExtras();
128         CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
129         Log.i(TAG, "in Session onRequestCompleteVoice recvd. message=" +
130                 prompt + ", extras=" + Utils.toBundleString(extras));
131         assertPromptFromTestApp(prompt, extras);
132         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setExtras(extras);
133         if (isTestTypeCancel(extras)) {
134             Log.i(TAG, "Sending Cancel.");
135             newTask().execute(
136                     asyncTaskArg.setTestType(Utils.TestCaseType.COMPLETION_REQUEST_CANCEL_TEST));
137         } else {
138             Log.i(TAG, "in Session sending sendConfirmationResult. " +
139                     Utils.toBundleString(extras));
140             newTask().execute(
141                     asyncTaskArg.setTestType(Utils.TestCaseType.COMPLETION_REQUEST_TEST));
142         }
143     }
144 
145     @Override
onRequestAbortVoice(AbortVoiceRequest request)146     public void onRequestAbortVoice(AbortVoiceRequest request) {
147         Bundle extras = request.getExtras();
148         CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
149         Log.i(TAG, "in Session onRequestAbortVoice recvd. message=" +
150                 prompt + ", extras=" + Utils.toBundleString(extras));
151         assertPromptFromTestApp(prompt, extras);
152         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setExtras(extras);
153         if (isTestTypeCancel(extras)) {
154             Log.i(TAG, "Sending Cancel.");
155             newTask().execute(
156                     asyncTaskArg.setTestType(Utils.TestCaseType.ABORT_REQUEST_CANCEL_TEST));
157         } else {
158             Log.i(TAG, "in Session sending sendAbortResult. " +
159                 Utils.toBundleString(extras));
160             newTask().execute(asyncTaskArg.setTestType(Utils.TestCaseType.ABORT_REQUEST_TEST));
161         }
162     }
163 
164     @Override
onRequestCommand(CommandRequest request)165     public void onRequestCommand(CommandRequest request) {
166         Bundle extras = request.getExtras();
167         Log.i(TAG, "in Session onRequestCommand recvd. Bundle = " +
168                 Utils.toBundleString(extras));
169 
170         // Make sure that the input request has Utils.TEST_COMMAND sent by TestApp
171         String command = request.getCommand();
172         if (command.equals(Utils.TEST_COMMAND)) {
173             Log.i(TAG, "command received ok from TestApp in Session");
174         } else {
175             Utils.addErrorResult(extras, "Invalid TEST_COMMAND received: " + command);
176         }
177         // Add a field and value in the bundle to be sent to TestApp.
178         // TestApp will ensure that these are transmitted correctly.
179         extras.putString(Utils.TEST_ONCOMMAND_RESULT, Utils.TEST_ONCOMMAND_RESULT_VALUE);
180         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setExtras(extras);
181         if (isTestTypeCancel(extras)) {
182             Log.i(TAG, "Sending Cancel.");
183             newTask().execute(
184                     asyncTaskArg.setTestType(Utils.TestCaseType.COMMANDREQUEST_CANCEL_TEST));
185         } else {
186             Log.i(TAG, "in Session sending sendResult. " +
187                     Utils.toBundleString(extras) + ", string_in_bundle: " +
188                     Utils.TEST_ONCOMMAND_RESULT + " = " + Utils.TEST_ONCOMMAND_RESULT_VALUE);
189             newTask().execute(asyncTaskArg.setTestType(Utils.TestCaseType.COMMANDREQUEST_TEST));
190         }
191     }
192 
assertPickOptionsFromTestApp(VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras)193     void assertPickOptionsFromTestApp(VoiceInteractor.PickOptionRequest.Option[] options,
194             Bundle extras) {
195         if ((options.length != 2) ||
196             !options[0].getLabel().toString().equals(Utils.PICKOPTON_1) ||
197             !options[1].getLabel().toString().equals(Utils.PICKOPTON_2)) {
198             Utils.addErrorResult(extras, "Pickoptions Not received correctly in Session.");
199         } else {
200             Log.i(TAG, "Pickoptions received ok from TestApp in Session");
201         }
202     }
203 
204     @Override
onRequestPickOption(PickOptionRequest request)205     public void onRequestPickOption(PickOptionRequest request) {
206         Bundle extras = request.getExtras();
207         CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
208         Log.i(TAG, "in Session onRequestPickOption recvd. message=" +
209                 prompt + ", options = " + Utils.toOptionsString(request.getOptions()) +
210                 ", extras=" + Utils.toBundleString(extras));
211         VoiceInteractor.PickOptionRequest.Option[] picked
212             = new VoiceInteractor.PickOptionRequest.Option[1];
213         assertPromptFromTestApp(prompt, extras);
214         assertPickOptionsFromTestApp(request.getOptions(), extras);
215         picked[0] = new VoiceInteractor.PickOptionRequest.Option(Utils.PICKOPTON_3, 0);
216         AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request)
217                 .setExtras(extras)
218                 .setPickedOptions(picked);
219         if (isTestTypeCancel(extras)) {
220             Log.i(TAG, "in MainInteractionSession, Sending Cancel.");
221             newTask().execute(
222                     asyncTaskArg.setTestType(Utils.TestCaseType.PICKOPTION_REQUEST_CANCEL_TEST));
223         } else {
224             Log.i(TAG, "in MainInteractionSession sending sendPickOptionResult. " +
225                     Utils.toBundleString(extras));
226             newTask().execute(asyncTaskArg.setTestType(Utils.TestCaseType.PICKOPTION_REQUEST_TEST));
227         }
228     }
229 
isTestTypeCancel(Bundle extras)230     public static final boolean isTestTypeCancel(Bundle extras) {
231         Utils.TestCaseType testCaseType;
232         try {
233             testCaseType = Utils.TestCaseType.valueOf(extras.getString(Utils.TESTCASE_TYPE));
234         } catch (IllegalArgumentException | NullPointerException e) {
235             Log.wtf(TAG, "unexpected testCaseType value in Bundle received", e);
236             return true;
237         }
238         return testCaseType == Utils.TestCaseType.COMPLETION_REQUEST_CANCEL_TEST ||
239                 testCaseType == Utils.TestCaseType.COMMANDREQUEST_CANCEL_TEST ||
240                 testCaseType == Utils.TestCaseType.CONFIRMATION_REQUEST_CANCEL_TEST ||
241                 testCaseType == Utils.TestCaseType.PICKOPTION_REQUEST_CANCEL_TEST ||
242                 testCaseType == Utils.TestCaseType.ABORT_REQUEST_CANCEL_TEST;
243     }
244 
245     private class AsyncTaskArg {
246         ConfirmationRequest confReq;
247         CommandRequest commandReq;
248         CompleteVoiceRequest compReq;
249         AbortVoiceRequest abortReq;
250         PickOptionRequest pickReq;
251         Bundle extras;
252         VoiceInteractor.PickOptionRequest.Option[] picked;
253         Utils.TestCaseType testType;
254 
setTestType(Utils.TestCaseType t)255         AsyncTaskArg setTestType(Utils.TestCaseType t) {testType = t; return this;}
setRequest(CommandRequest r)256         AsyncTaskArg setRequest(CommandRequest r) {commandReq = r; return this;}
setRequest(ConfirmationRequest r)257         AsyncTaskArg setRequest(ConfirmationRequest r) {confReq = r; return this;}
setRequest(CompleteVoiceRequest r)258         AsyncTaskArg setRequest(CompleteVoiceRequest r) {compReq = r; return this;}
setRequest(AbortVoiceRequest r)259         AsyncTaskArg setRequest(AbortVoiceRequest r) {abortReq = r; return this;}
setRequest(PickOptionRequest r)260         AsyncTaskArg setRequest(PickOptionRequest r) {pickReq = r; return this;}
setExtras(Bundle e)261         AsyncTaskArg setExtras(Bundle e) {extras = e;  return this;}
setPickedOptions(VoiceInteractor.PickOptionRequest.Option[] p)262         AsyncTaskArg setPickedOptions(VoiceInteractor.PickOptionRequest.Option[] p) {
263             picked = p;
264             return this;
265         }
266     }
267 
268     private class MyTask extends AsyncTask<AsyncTaskArg, Void, Void> {
269         @Override
doInBackground(AsyncTaskArg... params)270         protected Void doInBackground(AsyncTaskArg... params) {
271             AsyncTaskArg arg = params[0];
272             Log.i(TAG, "in MyTask - doInBackground: requestType = " +
273                     arg.testType.toString());
274             switch (arg.testType) {
275                 case ABORT_REQUEST_CANCEL_TEST:
276                     arg.abortReq.cancel();
277                     break;
278                 case ABORT_REQUEST_TEST:
279                     arg.abortReq.sendAbortResult(arg.extras);
280                     break;
281                 case COMMANDREQUEST_CANCEL_TEST:
282                     arg.commandReq.cancel();
283                     break;
284                 case COMMANDREQUEST_TEST:
285                     Log.i(TAG, "in MyTask sendResult. " +
286                             Utils.toBundleString(arg.extras) + ", string_in_bundle: " +
287                             Utils.TEST_ONCOMMAND_RESULT + " = " +
288                             Utils.TEST_ONCOMMAND_RESULT_VALUE);
289                     arg.commandReq.sendResult(arg.extras);
290                     break;
291                 case COMPLETION_REQUEST_CANCEL_TEST:
292                     arg.compReq.cancel();
293                     break;
294                 case COMPLETION_REQUEST_TEST:
295                     arg.compReq.sendCompleteResult(arg.extras);
296                     break;
297                 case CONFIRMATION_REQUEST_CANCEL_TEST:
298                      arg.confReq.cancel();
299                      break;
300                 case CONFIRMATION_REQUEST_TEST:
301                      arg.confReq.sendConfirmationResult(true, arg.extras);
302                      break;
303                 case PICKOPTION_REQUEST_CANCEL_TEST:
304                      arg.pickReq.cancel();
305                      break;
306                 case PICKOPTION_REQUEST_TEST:
307                      StringBuilder buf = new StringBuilder();
308                      for (VoiceInteractor.PickOptionRequest.Option s : arg.picked) {
309                          buf.append("option: " + s.toString() + ", ");
310                      }
311                      Log.i(TAG, "******** Sending PickoptionResult: " +
312                              "picked: size = " + arg.picked.length +
313                              ", Options = " + buf.toString() +
314                              ", Bundle: " + Utils.toBundleString(arg.extras));
315                      arg.pickReq.sendPickOptionResult(arg.picked, arg.extras);
316                      break;
317                default:
318                    Log.i(TAG, "Doing nothing for the testcase type: " + arg.testType);
319                    break;
320             }
321             return null;
322         }
323     }
324 }
325