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.content.Context; 21 import android.content.Intent; 22 import android.content.pm.LauncherApps; 23 import android.os.AsyncTask; 24 import android.os.Bundle; 25 import android.service.voice.VoiceInteractionSession; 26 import android.util.Log; 27 import android.voiceinteraction.common.Utils; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 32 public class MainInteractionSession extends VoiceInteractionSession { 33 static final String TAG = "MainInteractionSession"; 34 35 Intent mStartIntent; 36 List<MyTask> mUsedTasks = new ArrayList<MyTask>(); 37 MainInteractionSession(Context context)38 MainInteractionSession(Context context) { 39 super(context); 40 } 41 42 @Override onCreate()43 public void onCreate() { 44 super.onCreate(); 45 Intent sessionStarted = new Intent(); 46 sessionStarted.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 47 if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) { 48 sessionStarted.putExtra("error", "Does not have shortcut permission"); 49 } 50 sessionStarted.setClassName("android.voiceinteraction.cts", 51 "android.voiceinteraction.cts.VoiceInteractionTestReceiver"); 52 Log.i(TAG, "onCreate(): broadcast intent=" + sessionStarted); 53 getContext().sendBroadcast(sessionStarted); 54 } 55 56 @Override onDestroy()57 public void onDestroy() { 58 Log.i(TAG, "Canceling the Asynctask in onDestroy()"); 59 for (MyTask t : mUsedTasks) { 60 t.cancel(true); 61 } 62 super.onDestroy(); 63 } 64 65 @Override onShow(Bundle args, int showFlags)66 public void onShow(Bundle args, int showFlags) { 67 if (args == null) { 68 Log.e(TAG, "onshow() received null args"); 69 return; 70 } 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 assertPromptFromTestApp(CharSequence prompt, Bundle extras)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 newTask()93 synchronized MyTask newTask() { 94 MyTask t = new MyTask(); 95 mUsedTasks.add(t); 96 return t; 97 } 98 99 @Override onGetSupportedCommands(String[] commands)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 onRequestConfirmation(ConfirmationRequest request)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 onRequestCompleteVoice(CompleteVoiceRequest request)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 onRequestAbortVoice(AbortVoiceRequest request)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 onRequestCommand(CommandRequest request)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 assertPickOptionsFromTestApp(VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras)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 onRequestPickOption(PickOptionRequest request)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 isTestTypeCancel(Bundle extras)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 setTestType(Utils.TestCaseType t)260 AsyncTaskArg setTestType(Utils.TestCaseType t) {testType = t; return this;} setRequest(CommandRequest r)261 AsyncTaskArg setRequest(CommandRequest r) {commandReq = r; return this;} setRequest(ConfirmationRequest r)262 AsyncTaskArg setRequest(ConfirmationRequest r) {confReq = r; return this;} setRequest(CompleteVoiceRequest r)263 AsyncTaskArg setRequest(CompleteVoiceRequest r) {compReq = r; return this;} setRequest(AbortVoiceRequest r)264 AsyncTaskArg setRequest(AbortVoiceRequest r) {abortReq = r; return this;} setRequest(PickOptionRequest r)265 AsyncTaskArg setRequest(PickOptionRequest r) {pickReq = r; return this;} setExtras(Bundle e)266 AsyncTaskArg setExtras(Bundle e) {extras = e; return this;} setPickedOptions(VoiceInteractor.PickOptionRequest.Option[] p)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 doInBackground(AsyncTaskArg... params)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