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