001package com.aispeech.dui.dds.agent; 002 003import android.annotation.SuppressLint; 004import android.content.BroadcastReceiver; 005import android.content.Context; 006import android.content.Intent; 007import android.content.IntentFilter; 008import android.net.ConnectivityManager; 009import android.net.NetworkInfo; 010import android.os.Build; 011import android.os.Handler; 012import android.os.HandlerThread; 013import android.os.Message; 014import android.os.SystemClock; 015import android.telephony.TelephonyManager; 016import android.text.TextUtils; 017 018import com.aispeech.dds.libprocessor.ProcessorConfig; 019import com.aispeech.dds.libprocessor.ProcessorJsonBuilder; 020import com.aispeech.dui.BaseNode; 021import com.aispeech.dui.BusClient; 022import com.aispeech.dui.dds.DDS; 023import com.aispeech.dui.dds.DDSConfig; 024import com.aispeech.dui.dds.DDSMode; 025import com.aispeech.dui.dds.agent.tts.TTSEngine; 026import com.aispeech.dui.dds.agent.tts.TTSRequestListener; 027import com.aispeech.dui.dds.agent.vprint.VprintEngine; 028import com.aispeech.dui.dds.agent.wakeup.WakeupEngine; 029import com.aispeech.dui.dds.auth.AccessTokenManager; 030import com.aispeech.dui.dds.auth.AuthInfo; 031import com.aispeech.dui.dds.auth.AuthType; 032import com.aispeech.dui.dds.exceptions.DDSNotInitCompleteException; 033import com.aispeech.dui.dds.nodes.PlayerNode; 034import com.aispeech.dui.dds.nodes.RecorderExNode; 035import com.aispeech.dui.dds.nodes.TtsExNode; 036import com.aispeech.dui.dds.utils.DeviceUtil; 037import com.aispeech.dui.dds.utils.FileUtils; 038import com.aispeech.dui.dds.utils.NetworkUtils; 039import com.aispeech.dui.dds.utils.URLUtils; 040import com.aispeech.dui.dsk.duiwidget.CommandObserver; 041import com.aispeech.dui.dsk.duiwidget.DuiWidget; 042import com.aispeech.dui.dsk.duiwidget.NativeApiObserver; 043import com.aispeech.dui.dsk.duiwidget.NativeCommandAsyncObserver; 044import com.aispeech.dui.dsk.duiwidget.NativeCommandObserver; 045import com.aispeech.dui.manager.AIJavaException; 046import com.aispeech.dui.manager.AILog; 047import com.aispeech.dui.manager.BusManager; 048import com.aispeech.dui.manager.ThreadName; 049import com.aispeech.dui.oauth.OAuthSdk; 050import com.aispeech.dui.oauth.TokenListener; 051import com.aispeech.dui.oauth.TokenResult; 052import com.aispeech.libbase.AIGsonUtil; 053import com.aispeech.libbase.StringUtil; 054import com.aispeech.libbase.UUIDUtil; 055import com.aispeech.libbase.bussiness.DDSConstant; 056import com.aispeech.libbase.bussiness.JSONObjectUtil; 057 058import com.aispeech.libcomm.abslite.impl.InterceptorImpl; 059import com.aispeech.libcomm.business.CommonUtil; 060import com.aispeech.libcomm.business.CostTimeUtil; 061import com.aispeech.libcomm.business.LocalKeys; 062import com.aispeech.libcomm.business.LocalKeysUtil; 063import com.aispeech.libcomm.business.NodeName; 064import com.aispeech.libcomm.business.call.AuthCallUtil; 065import com.aispeech.libcomm.business.call.ProcessorCallUtil; 066import com.aispeech.libcomm.business.config.PlayerConfig; 067import com.aispeech.libcomm.business.topic.AgentTopicUtil; 068import com.aispeech.libcomm.business.topic.DmsTopicUtil; 069import com.aispeech.libcomm.business.topic.PlayerTopicUtil; 070import com.aispeech.libcomm.business.topic.ProcessorTopicUtil; 071import com.aispeech.libcomm.business.topic.RecorderTopicUtil; 072import com.aispeech.libcomm.business.topic.SysTopicUtil; 073import com.aispeech.libcomm.business.topic.Topic; 074import com.aispeech.libcomm.business.topic.UploadTopicUtil; 075import com.aispeech.libcomm.business.topic.VoipTopicUtil; 076import com.aispeech.libcomm.business.topic.WakeupTopicUtil; 077import com.aispeech.libcomm.callback.CallbackManager; 078import com.aispeech.libcomm.callback.OnDmTaskCallback; 079 080import org.json.JSONArray; 081import org.json.JSONException; 082import org.json.JSONObject; 083 084import java.io.File; 085import java.io.IOException; 086import java.util.ArrayList; 087import java.util.HashMap; 088import java.util.Arrays; 089import java.util.Iterator; 090import java.util.List; 091import java.util.Map; 092import java.util.Set; 093import java.util.concurrent.ConcurrentSkipListSet; 094import java.util.concurrent.CountDownLatch; 095import java.util.concurrent.FutureTask; 096import java.util.concurrent.TimeUnit; 097 098import static com.aispeech.libbase.bussiness.DDSConstant.Auth.KEY_AUTH_CODE; 099import static com.aispeech.libbase.bussiness.DDSConstant.Auth.KEY_CLIENT_ID; 100import static com.aispeech.libbase.bussiness.DDSConstant.Auth.KEY_CODE_VERIFIER; 101import static com.aispeech.libbase.bussiness.DDSConstant.Auth.KEY_REDIRECT_URI; 102import static com.aispeech.libbase.bussiness.DDSConstant.Auth.KEY_USER_ID; 103import static com.aispeech.libcomm.business.topic.Topic.Player.TOPIC_PLAYER_AUDIOFOCUS_CALLBACK; 104 105public class Agent extends BaseNode implements ReferenceCountMap.OnReferenceCountChangeListener { 106 107 public static final String TAG = "Agent"; 108 109 private ReferenceCountMap<NativeApiObserver> nativeApiObservers; 110 111 private ReferenceCountMap<CommandObserver> commandObservers; 112 113 private ReferenceCountMap<NativeCommandObserver> mNativeCommandObservers; 114 private Object mNativeCommandLock = new Object(); 115 116 private ReferenceCountMap<MessageObserver> messageObservers; 117 118 private Set<String> subscribedTopics = new ConcurrentSkipListSet<>(); 119 120 private Context context; 121 122 private final Object topicsLock = new Object(); 123 124 private ConnectionChangeReceiver connectionChangeReceiver; 125 126 private Handler mHandler; 127 128 private DMCallback mDMCallback; 129 private DMTaskCallback mDMTaskCallback; 130 131 private AudioFocusCallback mAudioFocusCallback; 132 private DMWkupRetCallback mDmWkupRetCallback; 133 134 private HandlerThread mHandlerThread; 135 private Handler mHandler2; 136 //区分于阻塞线程,规避因为线程阻塞导致的dds逻辑运行异常问题 137 private HandlerThread mHandlerThreadAsyn; 138 private Handler mHandlerAsyn; 139 private CountDownLatch mNativeCmdCountDownLatch; 140 private Map<String, JSONObject> mNativeCmdResultMap = new HashMap<>(); 141 142 public enum DuplexMode { 143 HALF_DUPLEX, 144 FULL_DUPLEX 145 } 146 147 public enum RecorderMode { 148 PCM, SBC, OPUS 149 } 150 151 private String[] mTopicList = new String[]{ 152 Topic.Processor.TaskCallback.TOPIC_NAME, 153 Topic.Dla.SetAuthCode.TOPIC_NAME, 154 Topic.Processor.WkupCallback.TOPIC_NAME 155 }; 156 157 private String[] mCallList = new String[]{ 158 ProcessorCallUtil.CALL_GET_DM_OPERATION, 159 Topic.Agent.CALL_PLAYER_AUDIO_FOCUS_NOTIFY.TOPIC_NAME 160 }; 161 162 /** 163 * 构造方法,开发者可以不关注该构造方法, 164 * 而是可以通过{@link com.aispeech.dui.dds.DDS#getAgent}获取Agent实例 165 * 166 * @param context context 167 */ 168 public Agent(Context context) { 169 this.context = context; 170 nativeApiObservers = new ReferenceCountMap<>(); 171 commandObservers = new ReferenceCountMap<>(); 172 mNativeCommandObservers = new ReferenceCountMap<>(); 173 messageObservers = new ReferenceCountMap<>(); 174 nativeApiObservers.setListener(this); 175 commandObservers.setListener(this); 176 mNativeCommandObservers.setListener(this); 177 messageObservers.setListener(this); 178 ASREngine.getInstance(); 179 WakeupEngine.getInstance(); 180 TTSEngine.getInstance(); 181 initThread(); 182 initThreadAsyn(); 183 } 184 185 private void initThread() { 186 mHandlerThread = new HandlerThread(ThreadName.AGENT); 187 mHandlerThread.start(); 188 mHandler2 = new Handler(mHandlerThread.getLooper()) { 189 @Override 190 public void handleMessage(Message msg) { 191 super.handleMessage(msg); 192 } 193 }; 194 } 195 196 private void initThreadAsyn() { 197 mHandlerThreadAsyn = new HandlerThread(ThreadName.AGENT + "asyn"); 198 mHandlerThreadAsyn.start(); 199 mHandlerAsyn = new Handler(mHandlerThreadAsyn.getLooper()); 200 } 201 202 private void asyncExecuteNoBlocking(Runnable runnable) { 203 if (mHandlerThreadAsyn != null && !mHandlerThreadAsyn.isAlive()) { 204 mHandlerThreadAsyn.start(); 205 } 206 if (mHandlerAsyn != null) { 207 mHandlerAsyn.post(runnable); 208 } 209 } 210 211 private void syncExecute(FutureTask futureTask) { 212 try { 213 if (mHandlerThread != null && !mHandlerThread.isAlive()) { 214 mHandlerThread.start(); 215 } 216 if (mHandler2 != null) { 217 mHandler2.post(futureTask); 218 } 219 futureTask.get(); 220 } catch (Exception e) { 221 AIJavaException.printException(e); 222 } 223 } 224 225 private void asyncExecute(Runnable runnable) { 226 if (mHandlerThread != null && !mHandlerThread.isAlive()) { 227 mHandlerThread.start(); 228 } 229 if (mHandler2 != null) { 230 mHandler2.post(runnable); 231 } 232 } 233 234 @SuppressLint("WrongConstant") 235 @Override 236 public void onCreate() { 237 super.onCreate(); 238 boolean disableInnerNetworkCheck = PlayerConfig.getInstance().getBooleanConfig("DISABLE_INNER_NETWORK_CHECK", false); 239 AILog.d(TAG, "disableInnerNetworkCheck =", disableInnerNetworkCheck); 240 if(!disableInnerNetworkCheck) { 241 connectionChangeReceiver = new ConnectionChangeReceiver(); 242 IntentFilter filter = new IntentFilter(); 243 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 244 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 245 context.registerReceiver(connectionChangeReceiver, filter, Context.RECEIVER_EXPORTED); 246 } else { 247 context.registerReceiver(connectionChangeReceiver, filter); 248 } 249 250 } 251 } 252 253 254 @Override 255 public void onJoin() { 256 synchronized (topicsLock) { 257 for (String topic : subscribedTopics) { 258 bc.subscribe(topic); 259 } 260 bc.subscribe(mTopicList); 261 bc.subscribeCall(mCallList); 262 } 263 uploadData(); 264 super.onJoin(); 265 } 266 267 @Override 268 public void onExit() { 269 //just for dm callback 270 if (bc != null) { 271 bc.unsubscribe(mTopicList); 272 bc.unsubscribeCall(mCallList); 273 } 274 super.onExit(); 275 } 276 277 @Override 278 public void onDestroy() { 279 super.onDestroy(); 280 mHandler = null; 281 mHandler2 = null; 282 283 try { 284 if (context != null && connectionChangeReceiver != null) { 285 context.unregisterReceiver(connectionChangeReceiver); 286 } 287 } catch (Exception e) { 288 } 289 if (TTSEngine.getInstanceSnapshot() != null) { 290 TTSEngine.getInstanceSnapshot().destroy(); 291 } 292 if (ASREngine.getInstanceSnapshot() != null) { 293 ASREngine.getInstanceSnapshot().destroy(); 294 } 295 if (WakeupEngine.getInstanceSnapshot() != null) { 296 WakeupEngine.getInstanceSnapshot().destroy(); 297 } 298 if (VprintEngine.getInstanceSnapshot() != null) { 299 VprintEngine.getInstanceSnapshot().destroy(); 300 } 301 302 if (mHandlerThread != null) { 303 mHandlerThread.quit(); 304 mHandlerThread = null; 305 } 306 if (mHandlerThreadAsyn != null) { 307 mHandlerThreadAsyn.quit(); 308 mHandlerThreadAsyn = null; 309 } 310 if (subscribedTopics != null) { 311 subscribedTopics.clear(); 312 } 313 } 314 315 @Override 316 public String getName() { 317 return TAG; 318 } 319 320 @Override 321 public String getAddress() { 322 return DDS.BUS_SERVER_ADDR; 323 } 324 325 @Override 326 public BusClient.RPCResult onCall(String url, Object... args) throws Exception { 327 if (url.equals(ProcessorCallUtil.CALL_GET_DM_OPERATION)) { 328 if (args.length > 0) { 329 JSONObject dmJsonObject = new JSONObject(args[0].toString()); 330 if (mDMCallback != null) { 331 AILog.userO(TAG, "DMCallback.onDMResult result =", dmJsonObject.toString()); 332 JSONObject dmOperationJson = mDMCallback.onDMResult(dmJsonObject); 333 AILog.userI(TAG, "DMCallback.onDMResult input =", (dmOperationJson != null ? dmOperationJson.toString() : "null")); 334 if (dmOperationJson != null) { 335 return new BusClient.RPCResult(dmOperationJson.toString()); 336 } 337 } else { 338 return new BusClient.RPCResult(dmJsonObject.toString()); 339 } 340 } 341 } else if (TextUtils.equals(url, Topic.Agent.CALL_PLAYER_AUDIO_FOCUS_NOTIFY.TOPIC_NAME)) { 342 if (args.length > 0) { 343 JSONObject audioFocusNotify = new JSONObject(args[0].toString()); 344 if (mAudioFocusCallback != null) { 345 String action = audioFocusNotify.optString("action"); 346 AILog.userO(TAG, "AudioFocusCallback result =", action); 347 if (TextUtils.equals(action, "request")) { 348 mAudioFocusCallback.onRequest(); 349 } else if (TextUtils.equals(action, "abandon")) { 350 mAudioFocusCallback.onAbandon(); 351 } 352 } else { 353 AILog.w(TAG, "url : " + url + " mAudioFocusCallback is null"); 354 } 355 } 356 } 357 return null; 358 } 359 360 private void handleCost(String topic, String result) { 361 try { 362 if (TextUtils.equals(topic, "context.input.text")) { 363 JSONObject dataObj = new JSONObject(result); 364 String innerTopic = dataObj.optString("topic"); 365 if (TextUtils.equals("asr.speech.result", innerTopic)) { 366 CostTimeUtil.getInstance().mCloudAsrCost.agentSendInputTextResult(result); 367 } else { 368 CostTimeUtil.getInstance().mCloudAsrCost.agentSendInputVarResult(result); 369 } 370 } else if (TextUtils.equals(topic, "context.output.text")) { 371 CostTimeUtil.getInstance().mCloudAsrCost.agentSendOutputTextResult(result); 372 } 373 } catch (Exception e) { 374 AIJavaException.printException(e); 375 } 376 } 377 378 public void feedbackNativeCommandResult(String nativeCommand, JSONObject resultObj) { 379 if (mNativeCmdResultMap.containsKey(nativeCommand)) { 380 mNativeCmdResultMap.put(nativeCommand, resultObj); 381 if (mNativeCmdCountDownLatch != null) { 382 mNativeCmdCountDownLatch.countDown(); 383 mNativeCmdCountDownLatch = null; 384 } 385 setNativeState(false); 386 } 387 } 388 389 private JSONObject doNativeCommandCall(final NativeCommandObserver observer, final String topic, final String paramStr) { 390 setNativeState(true); 391 if (observer instanceof NativeCommandAsyncObserver) { 392 // 异步实现方法 393 mNativeCmdCountDownLatch = new CountDownLatch(1); 394 mNativeCmdResultMap.put(topic, new JSONObject()); 395 ((NativeCommandAsyncObserver) observer).onNativeCommandAsync(topic, paramStr); 396 try { 397 mNativeCmdCountDownLatch.await(10, TimeUnit.SECONDS); 398 } catch (Exception e) { 399 } 400 JSONObject result = JSONObjectUtil.deepCopy(mNativeCmdResultMap.get(topic)); 401 mNativeCmdResultMap.remove(topic); 402 setNativeState(false); 403 return result; 404 } else { 405 // 同步实现方法 406 JSONObject nativeResult = observer.onNativeCommandCall(topic, paramStr); 407 setNativeState(false); 408 return nativeResult; 409 } 410 } 411 412 private void setNativeState(boolean enable) { 413 if (PlayerConfig.getInstance().getBooleanConfig(DDSConfig.K_DROP_AUDIO_WHEN_NATIVE)) { 414 try { 415 publishSticky(Topic.Dms.UpdateNativeState.TOPIC_NAME, 416 new JSONObject().put(Topic.Dms.UpdateNativeState.ENABLE, enable)); 417 } catch (JSONException e) { 418 e.printStackTrace(); 419 } 420 } 421 } 422 423 private void doNativeCommand(JSONObject dmJsonObject) { 424 try { 425 JSONObject commandObj = dmJsonObject.optJSONObject("command"); 426 AILog.d(TAG, "doNativeCommand = " + commandObj.toString()); 427 String topic = commandObj.optString("api").replaceFirst(CommandObserver.COMMAND_SCHEME, ""); 428 JSONObject param = commandObj.optJSONObject("param"); 429 String paramStr = param != null ? param.toString() : "{}"; 430 ArrayList<NativeCommandObserver> nativeObservers = mNativeCommandObservers.get(topic); 431 if (nativeObservers != null && nativeObservers.size() > 0) { 432 for (int i = 0; i < nativeObservers.size(); i++) { 433 NativeCommandObserver observer = i < nativeObservers.size() ? nativeObservers.get(i) : null; 434 if (observer == null) { 435 break; 436 } 437 AILog.userO(TAG, "doNativeCommand onNativeCommandCall topic =", topic, ", paramStr =", paramStr); 438 JSONObject resultObj = doNativeCommandCall(observer, topic.replaceFirst(CommandObserver.COMMAND_SCHEME, ""), paramStr); 439 AILog.userI(TAG, "doNativeCommand onNativeCommandCall topic =", topic, ", resultObj =", (resultObj != null ? resultObj.toString() : "null")); 440 if (resultObj != null) { 441 // onNativeCommandCall方法返回不为null,就认为处理了 NativeCommand 442 dmJsonObject.remove("command"); 443 String nlg = resultObj.optString("nlg"); 444 boolean shouldEndSession = resultObj.optBoolean("shouldEndSession"); 445 JSONObject customNativeCommandResult = resultObj.optJSONObject("customNativeCommandResult"); 446 if (!TextUtils.isEmpty(nlg)) { 447 CommonUtil.changeTaskNlg(dmJsonObject, nlg); 448 } 449 if (shouldEndSession) { 450 CommonUtil.changeTaskShouldEndSession(dmJsonObject, true); 451 } 452 if (customNativeCommandResult != null) { 453 dmJsonObject.put("customNativeCommandResult", customNativeCommandResult); 454 } 455 } 456 } 457 } 458 ProcessorTopicUtil.publishTaskOperation(bc, dmJsonObject); 459 } catch (Exception e) { 460 e.printStackTrace(); 461 } 462 } 463 464 private void doInspire(JSONObject dmJsonObject) { 465 try { 466 JSONArray inspireArr = dmJsonObject.optJSONArray("inspire"); 467 JSONObject inspireSpeakObj = dmJsonObject.optJSONObject("inspireSpeak"); 468 AILog.d(TAG, "doInspire = " + inspireArr.toString()); 469 JSONObject inspireObj = JSONObjectUtil.create() 470 .put("inspire", inspireArr) 471 .put("speak", inspireSpeakObj) 472 .put("sessionId",dmJsonObject.optString("sessionId")) 473 .build(); 474 String topic = Topic.Sys.MSG_SYS_NATIVE_INSPIRE; 475 ArrayList<NativeCommandObserver> nativeObservers = mNativeCommandObservers.get(topic); 476 boolean isCustomDeal = false;// 判断用户是否已处理此消息 477 if (nativeObservers != null && nativeObservers.size() > 0) { 478 for (int i = 0; i < nativeObservers.size(); i++) { 479 NativeCommandObserver observer = nativeObservers.get(i); 480 if (observer == null) { 481 break; 482 } 483 isCustomDeal = true; 484 AILog.userO(TAG, "doInspire onNativeCommandCall topic =", topic, ", paramStr =", inspireObj.toString()); 485 JSONObject resultObj = doNativeCommandCall(observer, topic, inspireObj.toString()); 486 AILog.userI(TAG, "doInspire onNativeCommandCall topic =", topic, ", resultObj =", (resultObj != null ? resultObj.toString() : "null")); 487 if (resultObj != null) { 488 String nlg = resultObj.optString("nlg"); 489 JSONObject selectInspireItemObj = resultObj.optJSONObject("selectInspireItem"); 490 if (!TextUtils.isEmpty(nlg)) { 491 CommonUtil.changeTaskNlg(dmJsonObject, nlg); 492 } 493 if (selectInspireItemObj != null) { 494 CommonUtil.changeTaskInspireItem(dmJsonObject, selectInspireItemObj); 495 } 496 // 一句话多意图V2 使用数据拦截返回的结果 497 if (ProcessorConfig.ENABLE_MULTIINTENT_SEQUENCE) { 498 ProcessorTopicUtil.publishTaskOperation(bc, dmJsonObject.put("inspire", resultObj.optJSONArray("inspire"))); 499 return; 500 } 501 } 502 } 503 } 504 if (isCustomDeal && !ProcessorConfig.ENABLE_MULTIINTENT_SEQUENCE) { 505 dmJsonObject.remove("inspire"); 506 } 507 ProcessorTopicUtil.publishTaskOperation(bc, dmJsonObject); 508 } catch (Exception e) { 509 e.printStackTrace(); 510 } 511 } 512 513 private void doTaskCallback(JSONObject dmJsonObject) { 514 try { 515 DMTaskCallback.Type type = DMTaskCallback.Type.getTypeByVal(dmJsonObject.optString("from")); 516 if (mDMTaskCallback != null) { 517 AILog.userO(TAG, "doTaskCallback result =", dmJsonObject.toString()); 518 InterceptorImpl.doDMTaskCallbackBegin(dmJsonObject); 519 JSONObject dmTaskOperationJson = mDMTaskCallback.onDMTaskResult(dmJsonObject, type); 520 InterceptorImpl.doDMTaskCallbackEnd(dmTaskOperationJson); 521 AILog.userI(TAG, "doTaskCallback input =", (dmTaskOperationJson != null ? dmTaskOperationJson.toString() : "null")); 522 if (dmTaskOperationJson != null) { 523 dmTaskOperationJson.put("watchId", dmJsonObject.optString("watchId", "")); 524 } 525 ProcessorTopicUtil.publishTaskOperation(bc, dmTaskOperationJson != null ? dmTaskOperationJson : dmJsonObject); 526 } else { 527 //return raw obj 528 ProcessorTopicUtil.publishTaskOperation(bc, dmJsonObject); 529 } 530 } catch (Exception e) { 531 e.printStackTrace(); 532 } 533 } 534 535 private void onInnerThreadMessage(String topic, Object... parts) throws Exception { 536 AILog.d(TAG, "onInnerThreadMessage topic = " + topic); 537 if (topic.startsWith(CommandObserver.COMMAND_SCHEME)) { 538 ArrayList<CommandObserver> observers = commandObservers.get(topic); 539 if (observers != null && observers.size() > 0) { 540 for (int i = 0; i < observers.size(); i++) { 541 CommandObserver observer = i < observers.size() ? observers.get(i) : null; 542 if (observer == null) { 543 break; 544 } 545 try { 546 AILog.userO(TAG, "observer.command topic =", topic, ", params = ", ((parts != null && parts.length > 0) ? parts[0].toString() : "null")); 547 observer.onCall(topic.replaceFirst(CommandObserver.COMMAND_SCHEME, ""), (parts != null && parts.length > 0) ? parts[0].toString() : null); 548 AILog.d(TAG, "observer.onCall topic = " + topic + ", end..."); 549 } catch (Exception e) { 550 throw new BusClient.BusClientImplementException(e); 551 } 552 } 553 } 554 } else if (topic.startsWith(NativeApiObserver.NATIVE_API_SCHEME)) { 555 ArrayList<NativeApiObserver> observers = nativeApiObservers.get(topic); 556 if (observers != null) { 557 for (int i = 0; i < observers.size(); i++) { 558 NativeApiObserver observer = i < observers.size() ? observers.get(i) : null; 559 if (observer == null) { 560 break; 561 } 562 try { 563 AILog.userO(TAG, "observer.nativeApi topic =", topic, ", params = ", ((parts != null && parts.length > 0) ? parts[0].toString() : "null")); 564 observer.onQuery(topic.replaceFirst(NativeApiObserver.NATIVE_API_SCHEME, ""), (parts != null && parts.length > 0) ? parts[0].toString() : null); 565 } catch (Exception e) { 566 throw new BusClient.BusClientImplementException(e); 567 } 568 } 569 } 570 } else if (topic.equals(Topic.Processor.TaskCallback.TOPIC_NAME)) { 571 JSONObject dmJsonObject = (JSONObject) parts[0]; 572 String watchType = dmJsonObject.optString("watchType"); 573 if (TextUtils.equals(watchType, DDSConstant.DialogTaskWatchType.COMMAND_TASK)) { 574 doNativeCommand(dmJsonObject); 575 } else if (TextUtils.equals(watchType, DDSConstant.DialogTaskWatchType.COMM_TASK)) { 576 doTaskCallback(dmJsonObject); 577 } else if (TextUtils.equals(watchType, DDSConstant.DialogTaskWatchType.INSPIRE_TASK)) { 578 doInspire(dmJsonObject); 579 } 580 } else if (topic.equals(Topic.Dla.SetAuthCode.TOPIC_NAME)) { 581 if (bc != null) { 582 bc.removeSticky(topic); 583 } 584 parseAuthBean(parts[0].toString()); 585 } else if (topic.equals(Topic.Processor.WkupCallback.TOPIC_NAME)) { 586 JSONObject wkupRetJsonObject = (JSONObject) parts[0]; 587 if (mDmWkupRetCallback != null) { 588 AILog.userO(TAG, "onDMWkupRetCallback result =", wkupRetJsonObject.toString()); 589 JSONObject wkupRetOperationJson = mDmWkupRetCallback.onDMWkupRetCallback(wkupRetJsonObject); 590 AILog.userI(TAG, "onDMWkupRetCallback input =", (wkupRetOperationJson != null ? wkupRetOperationJson.toString() : "null")); 591 if (wkupRetOperationJson != null) { 592 wkupRetOperationJson.put("watchId", wkupRetJsonObject.optString("watchId", "")); 593 } 594 ProcessorTopicUtil.publishWkupOperationRet(bc, wkupRetOperationJson != null ? wkupRetOperationJson : wkupRetJsonObject); 595 } else { 596 //return raw obj 597 AILog.d(TAG, "return raw obj publishWkupOperationRet wkupRetJsonObject"); 598 ProcessorTopicUtil.publishWkupOperationRet(bc, wkupRetJsonObject); 599 } 600 } else { 601 ArrayList<MessageObserver> observers = messageObservers.get(topic); 602 try { 603 if (observers != null) { 604 Object obj = null; 605 if (parts != null && parts.length > 0) { 606 obj = parts[0]; 607 } 608 AILog.d(TAG, "observer.onMessage topic = " + topic); 609 String result = obj != null ? obj.toString() : null; 610 611 for (int i = 0; i < observers.size(); i++) { 612 MessageObserver observer = i < observers.size() ? observers.get(i) : null; 613 if (observer == null) { 614 break; 615 } 616 handleCost(topic, result); 617 observer.onMessage(topic, result); 618 AILog.d(TAG, "observer.onMessage topic = " + topic + ", end..."); 619 } 620 } 621 } catch (Exception e) { 622 AIJavaException.printException(e); 623 } 624 } 625 } 626 627 @Override 628 public void onMessage(final String topic, final Object... parts) throws Exception { 629 super.onMessage(topic, parts); 630 Runnable runnable = new Runnable() { 631 @Override 632 public void run() { 633 try { 634 onInnerThreadMessage(topic, parts); 635 } catch (Exception e) { 636 AIJavaException.printException(e); 637 } 638 } 639 }; 640 if (isTopicNoBlocking(topic)) { 641 asyncExecuteNoBlocking(runnable); 642 } else { 643 asyncExecute(runnable); 644 } 645 } 646 647 public boolean isTopicNoBlocking(String topic) { 648 return topic.equals(Topic.Processor.WkupCallback.TOPIC_NAME); 649 } 650 651 /** 652 * 用于DLA模块内部进行setAuthCode操作 653 */ 654 private void parseAuthBean(String data) throws JSONException, DDSNotInitCompleteException { 655 JSONObject authBeanJson = new JSONObject(data); 656 AuthInfo authInfo = new AuthInfo(); 657 authInfo.setClientId(authBeanJson.optString(KEY_CLIENT_ID)); 658 authInfo.setAuthCode(authBeanJson.optString(KEY_AUTH_CODE)); 659 authInfo.setUserId(authBeanJson.optString(KEY_USER_ID)); 660 authInfo.setCodeVerifier(authBeanJson.optString(KEY_CODE_VERIFIER)); 661 authInfo.setRedirectUri(authBeanJson.optString(KEY_REDIRECT_URI)); 662 setAuthCode(authInfo, new TokenListener() { 663 @Override 664 public void onSuccess(TokenResult tokenResult) { 665 AILog.d(TAG, "DLA setAuthCode successful, result is -> " + tokenResult.toString()); 666 } 667 668 @Override 669 public void onError(int errId, String errMsg) { 670 String errInfo = String.format("errId=%s, errMsg=%s", errId, errMsg); 671 AILog.e(TAG, "DLA setAuthCode failed, errInfo -> " + errInfo); 672 } 673 }); 674 } 675 676 /** 677 * 订阅command 678 * 679 * @param commands commands数组 680 * @param commandObserver 当订阅的command被触发了,会通知到commandObserver 681 */ 682 public void subscribe(final String[] commands, final CommandObserver commandObserver) { 683 AILog.userI(TAG, "subscribe commands =", (commands != null ? Arrays.toString(commands) : "null")); 684 Runnable runnable = new Runnable() { 685 @Override 686 public void run() { 687 for (String command : commands) { 688 subscribeInner(command, commandObserver); 689 } 690 } 691 }; 692 asyncExecute(runnable); 693 } 694 695 /** 696 * 订阅nativeCommand, 此command支持带返回值 697 * 698 * @param nativeCommands commands数组 699 * @param nativeCommandObserver 当订阅的command被触发了,会通知到commandObserver 700 */ 701 public void subscribeSync(final String[] nativeCommands, final NativeCommandObserver nativeCommandObserver) { 702 AILog.userI(TAG, "subscribeSync nativeCommands =", (nativeCommands != null ? Arrays.toString(nativeCommands) : "null")); 703 synchronized (mNativeCommandLock) { 704 for (String command : nativeCommands) { 705 mNativeCommandObservers.put(command, nativeCommandObserver); 706 } 707 } 708 } 709 710 /** 711 * 注销NativeCommandObserver. 注销后,这个observer将不再会接受到command的消息 712 * 713 * @param observer observer 714 */ 715 public void unSubscribeSync(final NativeCommandObserver observer) { 716 AILog.userI(TAG, "unSubscribeSync NativeCommandObserver"); 717 synchronized (mNativeCommandLock) { 718 mNativeCommandObservers.remove(observer); 719 } 720 } 721 722 /** 723 * 订阅command 724 * 725 * @param commands commands数组 726 * @param commandObserver 当订阅的command被触发了,会通知到commandObserver 727 */ 728 public void subscribeSync(final String[] commands, final CommandObserver commandObserver) { 729 AILog.userI(TAG, "subscribeSync commands =", (commands != null ? Arrays.toString(commands) : "null")); 730 final Object lock = new Object(); 731 synchronized (lock) { 732 Runnable runnable = new Runnable() { 733 @Override 734 public void run() { 735 synchronized (lock) { 736 for (String command : commands) { 737 subscribeInner(command, commandObserver); 738 } 739 lock.notifyAll(); 740 } 741 } 742 }; 743 asyncExecute(runnable); 744 try { 745 lock.wait(); 746 } catch (Exception e) { 747 AIJavaException.printException(e); 748 } 749 } 750 } 751 752 753 /** 754 * 订阅command 755 * 756 * @param command 单个commad 757 * @param commandObserver 当订阅的command被触发了,会通知到commandObserver 758 */ 759 public void subscribe(final String command, final CommandObserver commandObserver) { 760 AILog.userI(TAG, "subscribe command =", command); 761 Runnable runnable = new Runnable() { 762 @Override 763 public void run() { 764 subscribeInner(command, commandObserver); 765 } 766 }; 767 asyncExecute(runnable); 768 } 769 770 /** 771 * 订阅command 772 */ 773 private void subscribeInner(String command, CommandObserver commandObserver) { 774 String topic = CommandObserver.COMMAND_SCHEME + command; 775 commandObservers.put(topic, commandObserver); 776 } 777 778 /** 779 * 订阅nativeApi 780 * 781 * @param nativeApis nativeApi数组 782 * @param nativeApiObserver 当订阅的cnativeApi被触发了,会通知到nativeApiObserver 783 */ 784 public void subscribe(final String[] nativeApis, final NativeApiObserver nativeApiObserver) { 785 AILog.userI(TAG, "subscribe nativeApis =", (nativeApis != null ? Arrays.toString(nativeApis) : "null")); 786 Runnable runnable = new Runnable() { 787 @Override 788 public void run() { 789 for (String nativeApi : nativeApis) { 790 subscribeInner(nativeApi, nativeApiObserver); 791 } 792 } 793 }; 794 asyncExecute(runnable); 795 } 796 797 /** 798 * 订阅nativeApi 799 * 800 * @param nativeApis nativeApi数组 801 * @param nativeApiObserver 当订阅的cnativeApi被触发了,会通知到nativeApiObserver 802 */ 803 public void subscribeSync(final String[] nativeApis, final NativeApiObserver nativeApiObserver) { 804 AILog.userI(TAG, "subscribeSync nativeApis =", (nativeApis != null ? Arrays.toString(nativeApis) : "null")); 805 final Object lock = new Object(); 806 synchronized (lock) { 807 Runnable runnable = new Runnable() { 808 @Override 809 public void run() { 810 synchronized (lock) { 811 for (String nativeApi : nativeApis) { 812 subscribeInner(nativeApi, nativeApiObserver); 813 } 814 lock.notifyAll(); 815 } 816 } 817 }; 818 asyncExecute(runnable); 819 try { 820 lock.wait(); 821 } catch (Exception e) { 822 AIJavaException.printException(e); 823 } 824 } 825 } 826 827 /** 828 * 订阅nativeApi 829 * 830 * @param nativeApi 单个的nativeApi 831 * @param nativeApiObserver 当订阅的cnativeApi被触发了,会通知到nativeApiObserver 832 */ 833 public void subscribe(final String nativeApi, final NativeApiObserver nativeApiObserver) { 834 AILog.userI(TAG, "subscribe nativeApi =", nativeApi); 835 Runnable runnable = new Runnable() { 836 @Override 837 public void run() { 838 subscribeInner(nativeApi, nativeApiObserver); 839 } 840 }; 841 asyncExecute(runnable); 842 } 843 844 /** 845 * 订阅nativeApi 846 */ 847 private void subscribeInner(String nativeApi, NativeApiObserver nativeApiObserver) { 848 String topic = NativeApiObserver.NATIVE_API_SCHEME + nativeApi; 849 nativeApiObservers.put(topic, nativeApiObserver); 850 } 851 852 853 /** 854 * 订阅messages 855 * 856 * @param messages message数组 857 * @param messageObserver 当订阅的messages被触发了,会通知到messageObserver 858 */ 859 public void subscribe(final String[] messages, final MessageObserver messageObserver) { 860 AILog.userI(TAG, "subscribe messages =", (messages != null ? Arrays.toString(messages) : "null")); 861 Runnable runnable = new Runnable() { 862 @Override 863 public void run() { 864 for (String message : messages) { 865 subscribeInner(message, messageObserver); 866 } 867 } 868 }; 869 asyncExecute(runnable); 870 } 871 872 /** 873 * 订阅messages 874 * 875 * @param messages message数组 876 * @param messageObserver 当订阅的messages被触发了,会通知到messageObserver 877 */ 878 public void subscribeSync(final String[] messages, final MessageObserver messageObserver) { 879 AILog.userI(TAG, "subscribeSync messages =", (messages != null ? Arrays.toString(messages) : "null")); 880 final Object lock = new Object(); 881 synchronized (lock) { 882 Runnable runnable = new Runnable() { 883 @Override 884 public void run() { 885 synchronized (lock) { 886 for (String message : messages) { 887 subscribeInner(message, messageObserver); 888 } 889 lock.notifyAll(); 890 } 891 } 892 }; 893 asyncExecute(runnable); 894 try { 895 lock.wait(); 896 } catch (Exception e) { 897 AIJavaException.printException(e); 898 } 899 } 900 } 901 902 /** 903 * 订阅message 904 * 905 * @param message 单个的message 906 * @param messageObserver 当订阅的message被触发了,会通知到messageObserver 907 */ 908 public void subscribe(final String message, final MessageObserver messageObserver) { 909 AILog.userI(TAG, "subscribe message =", message); 910 Runnable runnable = new Runnable() { 911 @Override 912 public void run() { 913 subscribeInner(message, messageObserver); 914 } 915 }; 916 asyncExecute(runnable); 917 } 918 919 /** 920 * 订阅message 921 */ 922 private void subscribeInner(String message, MessageObserver messageObserver) { 923 messageObservers.put(message, messageObserver); 924 } 925 926 /** 927 * 注销CommandObserver. 注销后,这个observer将不再会接受到command的消息 928 * 929 * @param observer observer 930 */ 931 public void unSubscribe(final CommandObserver observer) { 932 AILog.userI(TAG, "unSubscribe CommandObserver"); 933 Runnable runnable = new Runnable() { 934 @Override 935 public void run() { 936 commandObservers.remove(observer); 937 } 938 }; 939 asyncExecute(runnable); 940 } 941 942 /** 943 * 注销CommandObserver. 注销后,这个observer将不再会接受到command的消息 944 * 945 * @param observer observer 946 */ 947 public void unSubscribeSync(final CommandObserver observer) { 948 AILog.userI(TAG, "unSubscribeSync CommandObserver"); 949 final Object lock = new Object(); 950 synchronized (lock) { 951 Runnable runnable = new Runnable() { 952 @Override 953 public void run() { 954 synchronized (lock) { 955 commandObservers.remove(observer); 956 lock.notifyAll(); 957 } 958 } 959 }; 960 asyncExecute(runnable); 961 try { 962 lock.wait(); 963 } catch (Exception e) { 964 AIJavaException.printException(e); 965 } 966 } 967 } 968 969 /** 970 * 注销NativeApiObserver. 注销后,这个observer将不再会接受到NativeApi的消息 971 * 972 * @param observer observer 973 */ 974 public void unSubscribe(final NativeApiObserver observer) { 975 AILog.userI(TAG, "unSubscribe NativeApiObserver"); 976 Runnable runnable = new Runnable() { 977 @Override 978 public void run() { 979 nativeApiObservers.remove(observer); 980 } 981 }; 982 asyncExecute(runnable); 983 } 984 985 /** 986 * 注销NativeApiObserver. 注销后,这个observer将不再会接受到NativeApi的消息 987 * 988 * @param observer observer 989 */ 990 public void unSubscribeSync(final NativeApiObserver observer) { 991 AILog.userI(TAG, "unSubscribeSync NativeApiObserver"); 992 final Object lock = new Object(); 993 synchronized (lock) { 994 Runnable runnable = new Runnable() { 995 @Override 996 public void run() { 997 synchronized (lock) { 998 nativeApiObservers.remove(observer); 999 lock.notifyAll(); 1000 } 1001 } 1002 }; 1003 asyncExecute(runnable); 1004 try { 1005 lock.wait(); 1006 } catch (Exception e) { 1007 AIJavaException.printException(e); 1008 } 1009 } 1010 } 1011 1012 1013 /** 1014 * 注销MessageObserver. 注销后,这个observer将不再会接受到messages的消息 1015 * 1016 * @param observer observer 1017 */ 1018 public void unSubscribe(final MessageObserver observer) { 1019 AILog.userI(TAG, "unSubscribe MessageObserver"); 1020 Runnable runnable = new Runnable() { 1021 @Override 1022 public void run() { 1023 messageObservers.remove(observer); 1024 } 1025 }; 1026 asyncExecute(runnable); 1027 } 1028 1029 /** 1030 * 注销MessageObserver. 注销后,这个observer将不再会接受到messages的消息 1031 * 1032 * @param observer observer 1033 */ 1034 public void unSubscribeSync(final MessageObserver observer) { 1035 AILog.userI(TAG, "unSubscribeSync MessageObserver"); 1036 final Object lock = new Object(); 1037 synchronized (lock) { 1038 Runnable runnable = new Runnable() { 1039 @Override 1040 public void run() { 1041 synchronized (lock) { 1042 messageObservers.remove(observer); 1043 lock.notifyAll(); 1044 } 1045 } 1046 }; 1047 asyncExecute(runnable); 1048 try { 1049 lock.wait(); 1050 } catch (Exception e) { 1051 AIJavaException.printException(e); 1052 } 1053 } 1054 } 1055 1056 1057 /** 1058 * 反馈native api的执行结果 1059 * 1060 * @param nativeApi dui平台上技能定义的nativeapi 1061 * @param duiWidget duiwidget实例 1062 */ 1063 public void feedbackNativeApiResult(String nativeApi, DuiWidget duiWidget) { 1064 AILog.userI(TAG, "feedbackNativeApiResult nativeApi =", nativeApi, ", duiWidget =", (duiWidget != null ? duiWidget.getJsonObject().toString() : "null")); 1065 if (bc == null) { 1066 return; 1067 } 1068 if (null == duiWidget) { 1069 JSONObject object = JSONObjectUtil.create() 1070 .put("errId", "080012") 1071 .put("errMsg", "查询异常,暂时不能为你提供服务") 1072 .put(Topic.Processor.InputDmError.NATIVE_API, NativeApiObserver.NATIVE_API_SCHEME + nativeApi) 1073 .build(); 1074 1075 ProcessorTopicUtil.publishInputDmError(bc, object); 1076 } else { 1077 JSONObject jsonObject = duiWidget.getJsonObject(); 1078 try { 1079 jsonObject.put(Topic.Processor.InputDmData.NATIVE_API, NativeApiObserver.NATIVE_API_SCHEME + nativeApi); 1080 ProcessorTopicUtil.publishInputDmData(bc, jsonObject); 1081 } catch (JSONException e) { 1082 AIJavaException.printException(e); 1083 } 1084 } 1085 } 1086 1087 public int getUiagentProt() { 1088 boolean enalbeDynamicUiagentPort = PlayerConfig.getInstance().getBooleanConfig("ENABLE_DYNAMIC_UIAGENT_PORT"); 1089 int port = 50002; 1090 if (enalbeDynamicUiagentPort) { 1091 String version = LocalKeysUtil.getInstance().getString("/local_keys/upload/productVersion"); 1092 if (!TextUtils.isEmpty(version)) { 1093 port += Integer.parseInt(version); 1094 return port; 1095 } 1096 } 1097 AILog.userI(TAG, "getUiagentProt result =", port); 1098 return port; 1099 } 1100 1101 /** 1102 * 获取资源包中H5资源的路径 1103 * 1104 * @return String H5资源index.html的路径 1105 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1106 */ 1107 public String getValidH5Path() throws DDSNotInitCompleteException { 1108 checkInitComplete(); 1109 String customHome = LocalKeysUtil.getInstance().getString(LocalKeys.Daemon.LOCAL_KEY_CUSTOM_HOME); 1110 String uiPath = null; 1111 if (TextUtils.isEmpty(customHome)) { 1112 uiPath = "file://" + context.getFilesDir().getAbsolutePath() + "/dds/custom/ui/index.html"; 1113 } else { 1114 uiPath = "file://" + customHome + "/ui/index.html"; 1115 } 1116 File indexFile = new File(uiPath.replace("file://", "")); 1117 if (!indexFile.exists()) { 1118 AILog.userI(TAG, "getValidH5Path result = null"); 1119 return null; 1120 } 1121 int port = getUiagentProt(); 1122 uiPath = uiPath + "?port=" + port; 1123 AILog.userI(TAG, "getValidH5Path result =", uiPath); 1124 return uiPath; 1125 } 1126 1127// public void push(String message) { 1128// throw new NotImplementedException("need to implement"); 1129// } 1130 1131 1132 /** 1133 * 此方法在Agent中已弃用,参见{@link WakeupEngine#getWakeupWords()} 1134 * 获取当前的唤醒词 1135 * 1136 * @return String[] 唤醒词数组 1137 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1138 */ 1139 @Deprecated 1140 public String[] getWakeupWords() throws DDSNotInitCompleteException { 1141 return getWakeupEngine().getWakeupWords(); 1142 } 1143 1144 1145 /** 1146 * 此方法在Agent中已弃用,参见{@link WakeupEngine#getMinorWakeupWord()} 1147 * 获取当前的副唤醒词 1148 * 1149 * @return String 副唤醒词, 若无则返回null 1150 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1151 */ 1152 @Deprecated 1153 public String getMinorWakeupWord() throws DDSNotInitCompleteException { 1154 return getWakeupEngine().getMinorWakeupWord(); 1155 } 1156 1157 1158 /** 1159 * 此方法在Agent中已弃用,参见{@link WakeupEngine#enableWakeup()} 1160 * 开启唤醒 1161 * 1162 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1163 */ 1164 @Deprecated 1165 public void enableWakeup() throws DDSNotInitCompleteException { 1166 getWakeupEngine().enableWakeup(); 1167 } 1168 1169 /** 1170 * 此方法在Agent中已弃用,参见{@link WakeupEngine#disableWakeup()} 1171 * 关闭唤醒 1172 * 1173 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1174 */ 1175 @Deprecated 1176 public void disableWakeup() throws DDSNotInitCompleteException { 1177 getWakeupEngine().disableWakeup(); 1178 } 1179 1180 /** 1181 * 开启新对话 1182 * <p> 1183 * 播报提示音后进入识别。若当前正在对话中,会先结束当前对话,再开启新对话。 1184 * 1185 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1186 */ 1187 public void startDialog() throws DDSNotInitCompleteException { 1188 startDialog(null); 1189 } 1190 1191 /** 1192 * 开启新对话 1193 * <p> 1194 * 播报提示音后进入识别。若当前正在对话中,会先结束当前对话,再开启新对话。 1195 * 1196 * @param jsonObject 自定义配置信息,可以配置回复语greeting等 1197 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1198 */ 1199 public void startDialog(JSONObject jsonObject) throws DDSNotInitCompleteException { 1200 checkInitComplete(); 1201 AILog.userI(TAG, "startDialog input =", jsonObject); 1202 if (bc != null) { 1203 ProcessorTopicUtil.publishDialogCtrl(bc, "start", jsonObject); 1204 } else { 1205 AILog.e(TAG, "startDialog failed due to null busclient"); 1206 } 1207 } 1208 1209 /** 1210 * 停止当前对话,包括停止合成,取消识别等 1211 * 1212 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1213 */ 1214 public void stopDialog() throws DDSNotInitCompleteException { 1215 stopDialog(null); 1216 } 1217 1218 public void breakDialog() throws DDSNotInitCompleteException { 1219 breakDialog(null); 1220 } 1221 1222 /** 1223 * 取消 1224 * @param jsonObject 1225 * @throws DDSNotInitCompleteException 1226 */ 1227 public void breakDialog(JSONObject jsonObject) throws DDSNotInitCompleteException { 1228 checkInitComplete(); 1229 AILog.userI(TAG, "breakDialog input =", jsonObject); 1230 if (bc != null) { 1231 ProcessorTopicUtil.publishDialogCtrl(bc, "break", jsonObject); 1232 } else { 1233 AILog.e(TAG, "startDialog failed due to null busclient"); 1234 } 1235 } 1236 1237 /** 1238 * 打开tip提示音 1239 * <p> 1240 * 1241 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1242 */ 1243 public void openTip() throws DDSNotInitCompleteException { 1244 checkInitComplete(); 1245 AILog.userI(TAG, "openTip"); 1246 if (bc != null) { 1247 ProcessorTopicUtil.publishTipCtrl(bc, "open"); 1248 } else { 1249 AILog.e(TAG, "openTip failed due to null busclient"); 1250 } 1251 } 1252 1253 /** 1254 * 关闭tip提示音 1255 * 1256 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1257 */ 1258 public void closeTip() throws DDSNotInitCompleteException { 1259 checkInitComplete(); 1260 AILog.userI(TAG, "closeTip"); 1261 if (bc != null) { 1262 ProcessorTopicUtil.publishTipCtrl(bc, "close"); 1263 } else { 1264 AILog.e(TAG, "closeTip failed due to null busclient"); 1265 } 1266 } 1267 1268 /** 1269 * 停止当前对话,包括停止合成,取消识别等 1270 * 1271 * @param jsonObject 自定义配置信息,可以配置合成文本,内部会在合成完该文本后停止当前对话 1272 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1273 */ 1274 public void stopDialog(JSONObject jsonObject) throws DDSNotInitCompleteException { 1275 checkInitComplete(); 1276 AILog.userI(TAG, "stopDialog input =", jsonObject); 1277 if (bc != null) { 1278 BusManager.getInstance().clearPendingMessage(NodeName.DMS); 1279 BusManager.getInstance().clearPendingMessage(NodeName.PROCESSOR); 1280 ProcessorTopicUtil.publishDialogCtrl(bc, "close", jsonObject); 1281 } else { 1282 AILog.e(TAG, "stopDialog failed due to null busclient"); 1283 } 1284 } 1285 1286 /** 1287 * @param skill 技能名称, 必填 1288 * @param task 任务名称, 必填 1289 * @param intent 意图名称, 必填 1290 * @param slots 语义槽, key-value Json, 可选 1291 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1292 * @deprecated 主动触发意图的接口 1293 * <p> 1294 * 跳过识别和语义,直接进入指定的意图对话 1295 */ 1296 @Deprecated 1297 public void triggerIntent(String skill, String task, String intent, String slots) 1298 throws DDSNotInitCompleteException { 1299 SkillIntent skillIntent = new SkillIntent("", task, intent, slots); 1300 skillIntent.setSkillName(skill); 1301 triggerIntent(skillIntent); 1302 } 1303 1304 /** 1305 * 主动触发意图的接口 1306 * <p> 1307 * 跳过识别和语义,直接进入指定的意图对话 1308 * 1309 * @param skillIntent 技能意图 1310 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1311 */ 1312 public void triggerIntent(SkillIntent skillIntent) throws DDSNotInitCompleteException { 1313 AILog.userI(TAG, "triggerIntent input =", skillIntent.toString()); 1314 checkInitComplete(); 1315 if (bc != null) { 1316 ProcessorTopicUtil.publishInputIntent(bc, skillIntent.toJson()); 1317 } else { 1318 AILog.e(TAG, "triggerIntent failed due to null busclient"); 1319 } 1320 } 1321 1322 /** 1323 * 点击唤醒/停止识别/打断播报 操作接口 1324 * 1325 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1326 * @see com.aispeech.dui.dds.DDSConfigBuilder.DmBuilder#setClickMode 1327 */ 1328 public void avatarClick() throws DDSNotInitCompleteException { 1329 avatarClick(""); 1330 } 1331 1332 /** 1333 * 点击唤醒/停止识别/打断播报 操作接口 1334 * 1335 * @param greeting 在唤醒时附带一则欢迎语 1336 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1337 */ 1338 public void avatarClick(String greeting) throws DDSNotInitCompleteException { 1339 checkInitComplete(); 1340 AILog.userI(TAG, "avatarClick input =", greeting); 1341 if (bc != null) { 1342 ProcessorTopicUtil.publishAvatarClick(bc, "tap", greeting); 1343 } else { 1344 AILog.e(TAG, "avatarClick failed due to null busclient"); 1345 } 1346 } 1347 1348 /** 1349 * 按下按键接口 1350 * 1351 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1352 */ 1353 public void avatarPress() throws DDSNotInitCompleteException { 1354 checkInitComplete(); 1355 AILog.userI(TAG, "avatarPress"); 1356 if (bc != null) { 1357 ProcessorTopicUtil.publishAvatarClick(bc, "press", ""); 1358 } else { 1359 AILog.e(TAG, "avatarPress failed due to null busclient"); 1360 } 1361 } 1362 1363 /** 1364 * 释放按键接口 1365 * 1366 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1367 */ 1368 public void avatarRelease() throws DDSNotInitCompleteException { 1369 checkInitComplete(); 1370 AILog.userI(TAG, "avatarRelease"); 1371 if (bc != null) { 1372 ProcessorTopicUtil.publishAvatarClick(bc, "release", ""); 1373 } else { 1374 AILog.e(TAG, "avatarRelease failed due to null busclient"); 1375 } 1376 } 1377 1378 /** 1379 * 获取当前的产品资源版本号 1380 * 1381 * @return String 资源版本号 1382 */ 1383 public String getResourceVersion() { 1384 String customHome = context.getFilesDir().getAbsolutePath() + "/dds/custom/"; 1385 String localCustomHome = LocalKeysUtil.getInstance().getString(LocalKeys.Daemon.LOCAL_KEY_CUSTOM_HOME); 1386 if (!TextUtils.isEmpty(localCustomHome)) 1387 customHome = localCustomHome; 1388 String content = FileUtils.readFileContent(customHome + "/product.cfg"); 1389 String result = null; 1390 if (!TextUtils.isEmpty(content)) { 1391 1392 JSONObject jsonContent = null; 1393 try { 1394 jsonContent = new JSONObject(content); 1395 result = jsonContent.optString("version"); 1396 } catch (JSONException e) { 1397 AIJavaException.printException(e); 1398 } 1399 } 1400 AILog.userI(TAG, "getResourceVersion result =", result); 1401 return result; 1402 } 1403 1404 private String getFromKeys(String key, String defaultValue) throws DDSNotInitCompleteException { 1405 String value = getFromKeys(key); 1406 return (value != null && "".equals(value)) ? value : defaultValue; 1407 } 1408 1409 /** 1410 * Get the string value from local_keys using the key. 1411 * 1412 * @param key 1413 * @return null for no keys stored. 1414 */ 1415 private String getFromKeys(String key) throws DDSNotInitCompleteException { 1416 checkInitPartComplete(); 1417 return LocalKeysUtil.getInstance().getString(key); 1418 } 1419 1420 /** 1421 * 此方法已在Agent中弃用,建议使用TTSEngine中的同名方法{@link TTSEngine#speak(String, int)} 1422 * <p> 1423 * 播报文本,支持SSML 1424 * <p> 1425 * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。 1426 * 1427 * @param text 播报文本 1428 * @param priority 优先级 1429 * <ul> 1430 * <li>优先级0-保留,与aios语音交互同级,仅限内部使用</li> 1431 * <li>优先级1-正常,默认选项,同级按序播放</li> 1432 * <li>优先级2-重要,可以插话优先级1,同级按序播放,播报完毕后继续播报刚才被插话的优先级1</li> 1433 * <li>优先级3-紧急,可以打断优先级1或优先级2,同级按序播放,播报完毕后播报下一句优先级2</li> 1434 * </ul> 1435 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1436 */ 1437 @Deprecated 1438 public void speak(String text, int priority) throws DDSNotInitCompleteException { 1439 getTTSEngine().speak(text, priority); 1440 } 1441 1442 /** 1443 * 此方法已在Agent中弃用,建议使用TTSEngine中的同名方法{@link TTSEngine#speak(String, int, String, int)} 1444 * <p> 1445 * 播报文本,支持SSML 1446 * <p> 1447 * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。 1448 * 1449 * @param text 播报文本 1450 * @param priority 优先级 1451 * <ul> 1452 * <li>优先级0-保留,与aios语音交互同级,仅限内部使用;</li> 1453 * <li>优先级1-正常,默认选项,同级按序播放;</li> 1454 * <li>优先级2-重要,可以插话优先级1,同级按序播放,播报完毕后继续播报刚才被插话的优先级1</li> 1455 * <li>优先级3-紧急,可以打断优先级1或优先级2,同级按序播放,播报完毕后播报下一句优先级2</li> 1456 * </ul> 1457 * @param ttsId 用于追踪该次播报的id,建议使用UUID. 1458 * @param audioFocus 该次播报的音频焦 1459 * <ul> 1460 * <li>priority == 0 1461 * {@link android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}</li> 1462 * <li>priority != 0 1463 * {@link android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}</li> 1464 * </ul> 1465 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1466 */ 1467 @Deprecated 1468 public void speak(String text, int priority, String ttsId, int audioFocus) throws DDSNotInitCompleteException { 1469 getTTSEngine().speak(text, priority, ttsId, audioFocus); 1470 } 1471 1472 /** 1473 * 此方法已在Agent中弃用,建议使用TTSEngine中的同名方法{@link TTSEngine#shutup(String)} 1474 * <p> 1475 * 停止播报接口 1476 * <p> 1477 * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。 1478 * 1479 * @param ttsId 和 {@link #speak(String, int, String, int)} ttsId.一致 1480 * ttsId与speak接口的ttsId一致,则停止或者移除该播报; 1481 * ttsId为空, 停止所有播报; 1482 * ttsId为"0",停止当前播报. 1483 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1484 */ 1485 @Deprecated 1486 public void shutup(String ttsId) throws DDSNotInitCompleteException { 1487 getTTSEngine().shutup(ttsId); 1488 } 1489 1490 public boolean updateAccessToken(String accessToken) throws DDSNotInitCompleteException { 1491 checkInitPartComplete(); 1492 if(!TextUtils.isEmpty(accessToken)) { 1493 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_AUTH_TYPE, AuthType.AISPEECH_ID); 1494 } else { 1495 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_AUTH_TYPE, AuthType.PROFILE); 1496 } 1497 AuthCallUtil.callAuthUpdateAccessToken(getBusClient(), accessToken); 1498 return true; 1499 } 1500 1501 public boolean updateAccessToken(String accessToken,String userId) throws DDSNotInitCompleteException { 1502 checkInitPartComplete(); 1503 if(!TextUtils.isEmpty(userId)) { 1504 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_USER_ID, userId); 1505 } else { 1506 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_USER_ID, ""); 1507 } 1508 updateAccessToken(accessToken); 1509 return true; 1510 } 1511 1512 public String getAccessToken() throws DDSNotInitCompleteException { 1513 return LocalKeysUtil.getInstance().getString(LocalKeys.Auth.LOCAL_KEY_AUTH_ACCESS_TOKEN, ""); 1514 } 1515 1516 /** 1517 * dca授权登录, 该方法需要在SDK初始化后使用; 1518 * 此方法应该在 OathSDK 登录获取 authCode 后使用 1519 * 与之对应的方法是 {@link Agent#clearAuthCode()} 1520 * 1521 * @param tokenListener 返回成功和错误的信息 1522 */ 1523 public void setAuthCode(final AuthInfo authInfo, final TokenListener tokenListener) throws DDSNotInitCompleteException { 1524 checkInitComplete(); 1525 AILog.userI(TAG, "setAuthCode input =", (authInfo != null ? authInfo.toString() : "null")); 1526 if (bc != null) { 1527 if (tokenListener == null) { 1528 AILog.w(TAG, "setAuthCode : TokenListener is null "); 1529 return; 1530 } 1531 if (authInfo == null) { 1532 tokenListener.onError(-1, "authInfo is null !!! "); 1533 return; 1534 } 1535 if (TextUtils.isEmpty(authInfo.getClientId())) { 1536 tokenListener.onError(-1, "clientId is empty"); 1537 return; 1538 } 1539 if (TextUtils.isEmpty(authInfo.getUserId())) { 1540 tokenListener.onError(-1, "userId is empty"); 1541 return; 1542 } 1543 if (TextUtils.isEmpty(authInfo.getAuthCode())) { 1544 tokenListener.onError(-1, "authCode is empty"); 1545 return; 1546 } 1547 if (TextUtils.isEmpty(authInfo.getCodeVerifier())) { 1548 tokenListener.onError(-1, "codeVerifier is empty"); 1549 return; 1550 } 1551 String authInfoStr = authInfo.toString(); 1552 AILog.i(TAG, "setAuthCode = " + authInfoStr); 1553 String localOauthInfoStr = LocalKeysUtil.getInstance().getString(LocalKeys.Auth.LOCAL_KEY_OAUTH_INFO); 1554 AILog.i(TAG, "localOauthInfoStr = " + localOauthInfoStr); 1555 if (!TextUtils.isEmpty(localOauthInfoStr)) { 1556 try { 1557 JSONObject oauthObj = new JSONObject(localOauthInfoStr); 1558 if (TextUtils.equals(oauthObj.optString("userId"), authInfo.getUserId()) 1559 && TextUtils.equals(oauthObj.optString("authCode"), authInfo.getAuthCode())) { 1560 AILog.i(TAG, "setAuthCode 重复调用,return。。。"); 1561 return; 1562 } 1563 } catch (Exception e) { 1564 e.printStackTrace(); 1565 } 1566 } 1567 1568 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_OAUTH_INFO, authInfoStr); 1569 OAuthSdk.initialize(context, authInfo.getClientId(), "dds"); 1570 AccessTokenManager.getInstance(context).start(authInfo.getAuthCode(), authInfo.getCodeVerifier(), authInfo.getRedirectUri(), new TokenListener() { 1571 @Override 1572 public void onSuccess(TokenResult tokenResult) { 1573 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_AUTH_TYPE, AuthType.AISPEECH_ID); 1574 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_USER_ID, authInfo.getUserId()); 1575 bc.publishSticky(Topic.Auth.MSG_AUTH_CHANGE_TYPE); 1576 LocalKeysUtil.getInstance().put(LocalKeys.OAuth.LOCAL_AUTH_TYPE_CLIEND_ID, authInfo.getClientId()); 1577 AILog.userO(TAG, "setAuthCode success"); 1578 tokenListener.onSuccess(tokenResult); 1579 } 1580 1581 @Override 1582 public void onError(int errorId, String error) { 1583 AILog.userO(TAG, "setAuthCode failed"); 1584 tokenListener.onError(errorId, error); 1585 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_OAUTH_INFO, ""); 1586 } 1587 }); 1588 } else { 1589 AILog.e(TAG, "setAuthCode failed due to null busclient"); 1590 } 1591 } 1592 1593 /** 1594 * 清除 authcode, 该方法需要在SDK初始化后使用; 1595 * 与之对应的方法是 {@link Agent#setAuthCode(AuthInfo, TokenListener)} 1596 * 1597 * @throws DDSNotInitCompleteException 1598 */ 1599 public void clearAuthCode() throws DDSNotInitCompleteException { 1600 checkInitComplete(); 1601 AILog.userI(TAG, "clearAuthCode"); 1602 AccessTokenManager.getInstance(context).clearToken(); 1603 AccessTokenManager.getInstance(context).clearTasks(); 1604 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_OAUTH_INFO, ""); 1605 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_AUTH_TYPE, AuthType.PROFILE); 1606 LocalKeysUtil.getInstance().put(LocalKeys.Auth.LOCAL_KEY_USER_ID, ""); 1607 LocalKeysUtil.getInstance().put(LocalKeys.OAuth.LOCAL_AUTH_TYPE_CLIEND_ID, ""); 1608 if (bc != null) { 1609 AuthCallUtil.callAuthUpdateAccessToken(bc, ""); 1610 bc.publishSticky(Topic.Auth.MSG_AUTH_CHANGE_TYPE); 1611 } else { 1612 AILog.e(TAG, "clearAuthCode failed due to null busclient"); 1613 } 1614 } 1615 1616 /** 1617 * 从 sdk 保存的数据里获取对应的值 1618 * <p> 1619 * 获取 oauth 的 userId 示例: 1620 * </p> 1621 * <p> 1622 * DDS.getInstance().getAgent().getLocalData("/local_keys/aispeech_user_id") 1623 * </p> 1624 * 1625 * @param key key 1626 * @return 值 1627 */ 1628 public String getLocalData(String key) throws DDSNotInitCompleteException { 1629 checkInitComplete(); 1630 return LocalKeysUtil.getInstance().getString(key); 1631 } 1632 1633 /** 1634 * 设置 数据到 sdk 1635 * <p> 1636 * 设置 oauth 的 userId 示例: 1637 * </p> 1638 * <p> 1639 * DDS.getInstance().getAgent().setLocalData("/local_keys/aispeech_user_id", "XXX"); 1640 * </p> 1641 * 1642 * @param key key 1643 * @param value value 1644 */ 1645 public void setLocalData(String key, String value) throws DDSNotInitCompleteException { 1646 checkInitComplete(); 1647 LocalKeysUtil.getInstance().put(key, value); 1648 } 1649 1650 /** 1651 * 设置场景模式 1652 * 1653 * @param mode 场景模式, 参考{@link DDSMode} 1654 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1655 */ 1656 public void setDDSMode(int mode) throws DDSNotInitCompleteException { 1657 checkInitComplete(); 1658 AILog.userI(TAG, "setDDSMode input =", mode); 1659 switch (mode) { 1660 case DDSMode.TTS_NORMAL: 1661 case DDSMode.TTS_SILENCE: 1662 PlayerNode.setTtsMode(mode); 1663 PlayerTopicUtil.publishSetPlayerMode(bc, mode); 1664 break; 1665 case DDSMode.PICKUP_FAR: 1666 case DDSMode.PICKUP_NEAR: 1667 if (bc != null) { 1668 JSONObject obj = new JSONObject(); 1669 try { 1670 obj.put("module", "pickup"); 1671 obj.put("config", mode == DDSMode.PICKUP_NEAR ? "near" : "far"); 1672 AgentTopicUtil.publishAgentSettings(bc, obj); 1673 } catch (JSONException e) { 1674 AILog.e(TAG, "setDDSMode failed due to JSONException"); 1675 AIJavaException.printException(e); 1676 } 1677 1678 } else { 1679 AILog.e(TAG, "setDDSMode failed due to null busclient"); 1680 } 1681 break; 1682 default: 1683 AILog.e(TAG, "setDDSMode: not support."); 1684 break; 1685 } 1686 1687 } 1688 1689 /** 1690 * 设置dds策略 1691 * 1692 * @param duplexMode 策略 DuplexMode.HALF_DUPLEX:一问一答策略, DuplexMode.FULL_DUPLEX:全双工策略 1693 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1694 */ 1695 public void setDuplexMode(final DuplexMode duplexMode) throws DDSNotInitCompleteException { 1696 checkInitComplete(); 1697 AILog.userI(TAG, "setDuplexMode input =", duplexMode.name()); 1698 if (bc != null) { 1699 String value = duplexMode.name(); 1700 ProcessorCallUtil.callSetDuplexMode(bc, value); 1701 1702 } else { 1703 AILog.e(TAG, "setDuplexMode failed due to null busclient"); 1704 } 1705 } 1706 1707 /** 1708 * 获取当前的运行模式 1709 * 1710 * @return DuplexMode.HALF_DUPLEX:一问一答策略, DuplexMode.FULL_DUPLEX:全双工策略 1711 * @throws DDSNotInitCompleteException 1712 */ 1713 public DuplexMode getDuplexMode() throws DDSNotInitCompleteException { 1714 checkInitComplete(); 1715 DuplexMode mode = DuplexMode.HALF_DUPLEX; 1716 String result = LocalKeysUtil.getInstance().getString("/local_keys/processor/policy"); 1717 if (TextUtils.equals("FULL_DUPLEX", result)) { 1718 mode = DuplexMode.FULL_DUPLEX; 1719 } 1720 AILog.userI(TAG, "getDuplexMode result =", mode); 1721 return mode; 1722 } 1723 1724 /** 1725 * 设置是否使用voip功能 1726 * 1727 * @param enableVoip true/false 1728 * @throws DDSNotInitCompleteException 1729 */ 1730 public void setEnableVoip(boolean enableVoip) throws DDSNotInitCompleteException { 1731 AILog.userI(TAG, "setEnableVoip input =", enableVoip); 1732 if (bc != null) { 1733 VoipTopicUtil.publishLocalVoipEnable(bc, enableVoip); 1734 } 1735 } 1736 1737 /** 1738 * 外部录音机拾音接口 1739 * <p> 1740 * 需要将DDSConfig.K_RECORDER_MODE设置为"external",该接口才会生效 1741 * 1742 * @param pcm 音频数据 1743 */ 1744 public void feedPcm(byte[] pcm) { 1745 RecorderExNode.feedPcm(pcm); 1746 } 1747 1748 /** 1749 * 外部录音机拾音接口 1750 * <p> 1751 * 需要将DDSConfig.K_RECORDER_MODE设置为"external",该接口才会生效 1752 * 并且需要提前调用: DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.SBC) 1753 * 1754 * @param sbc sbc格式的音频数据 1755 */ 1756 public void feedSbc(byte[] sbc) { 1757 RecorderExNode.feedSbc(sbc); 1758 } 1759 1760 /** 1761 * 外部录音机拾音接口 1762 * <p> 1763 * 需要将DDSConfig.K_RECORDER_MODE设置为"external",该接口才会生效 1764 * 并且需要提前调用: DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.OPUS) 1765 * 1766 * @param opus sbc格式的音频数据 1767 */ 1768 public void feedOpus(byte[] opus) { 1769 RecorderExNode.feedOpus(opus); 1770 } 1771 1772 /** 1773 * 设置外部录音机拾音模式 1774 * <p> 1775 * 需要将DDSConfig.K_RECORDER_MODE设置为"external",该接口才会生效 1776 * 1777 * @param recorderMode 音频格式枚举 1778 */ 1779 public void setRecorderMode(RecorderMode recorderMode) throws DDSNotInitCompleteException { 1780 checkInitComplete(); 1781 AILog.userI(TAG, "setRecorderMode input =", recorderMode.name()); 1782 if (bc != null) { 1783 RecorderExNode.setMode(recorderMode.name()); 1784 RecorderTopicUtil.publishUpdateRecorderMode(bc, recorderMode.name()); 1785 } else { 1786 AILog.e(TAG, "setRecorderMode failed due to null busclient"); 1787 } 1788 } 1789 1790 /** 1791 * 外部TTS引擎注册接口 1792 * <p> 1793 * 需要将DDSConfig.K_TTS_MODE设置为"external",该接口才会生效 1794 * 1795 * @param listener TTS请求监听器。如果为空,则为解除注册 1796 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1797 */ 1798 public void setTTSRequestListener(TTSRequestListener listener) throws DDSNotInitCompleteException { 1799 checkInitComplete(); 1800 AILog.userI(TAG, "setTTSRequestListener"); 1801 TtsExNode.subscribe(listener); 1802 } 1803 1804 /** 1805 * 外部TTS播报结束通知接口 1806 * <p> 1807 * 播放完毕后,需要调用该接口才能自动进入下一轮识别 1808 * <p> 1809 * 需要将DDSConfig.K_TTS_MODE设置为"external",该接口才会生效 1810 */ 1811 public void notifyTTSEnd() { 1812 notifyTTSEnd(null); 1813 } 1814 1815 /** 1816 * 外部TTS播报结束通知接口 1817 * <p> 1818 * 播放完毕后,需要调用该接口才能自动进入下一轮识别 1819 * <p> 1820 * 需要将DDSConfig.K_TTS_MODE设置为"external",该接口才会生效 1821 */ 1822 public void notifyTTSEnd(String ttsId) { 1823 TtsExNode.notifyTTSEnd(ttsId); 1824 } 1825 1826 /** 1827 * 更新词库接口 1828 * <p> 1829 * 更新指定词库的词条。 1830 * 更新结果可以通过sys.upload.result消息来获取。 1831 * 1832 * @param vocabs 需要更新的词库列表 1833 * @return 请求ID,用于追踪sys.upload.result 1834 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1835 * @see VocabIntent 1836 */ 1837 public String updateVocabs(final VocabIntent... vocabs) throws DDSNotInitCompleteException { 1838 checkInitComplete(); 1839 1840 final String id = UUIDUtil.randomUUID(); 1841 if (bc != null) { 1842 asyncExecute(new Runnable() { 1843 @Override 1844 public void run() { 1845 JSONObject obj = new JSONObject(); 1846 try { 1847 obj.put("reqId", id); 1848 1849 JSONArray arr = new JSONArray(); 1850 StringBuilder sb = new StringBuilder(); 1851 long t1 = System.currentTimeMillis(); 1852 for (VocabIntent s : vocabs) { 1853 if (s.isNumberExtension() && s.getContents() != null) { 1854 for (int i = 0; i < s.getContents().size(); i++) { 1855 String c = s.getContents().get(i); 1856 if (c != null && !c.contains(":")) { 1857 boolean transform2Num = StringUtil.str2Num(c, sb); 1858 if (transform2Num) { 1859 s.getContents().set(i, c + ":" + sb.toString()); 1860 } 1861 } 1862 } 1863 } 1864 arr.put(new JSONObject(s.toJson())); 1865 } 1866 AILog.d(TAG, "trans2Number cost " + (System.currentTimeMillis() - t1)); 1867 obj.put("vocabs", arr); 1868 } catch (JSONException e) { 1869 AILog.e(TAG, "updateVocabs failed due to JSONException"); 1870 AIJavaException.printException(e); 1871 } 1872 AILog.userI(TAG, "updateVocabs input =", obj.toString()); 1873 UploadTopicUtil.publishUploadVocabs(bc, obj,VocabIntent.ALL); 1874 } 1875 }); 1876 } else { 1877 AILog.e(TAG, "updateVocabs failed due to null busclient"); 1878 } 1879 return id; 1880 } 1881 1882 /** 1883 * 更新词库接口 1884 * <p> 1885 * 更新指定词库的词条。 1886 * 更新结果可以通过sys.upload.result消息来获取。 1887 * 1888 * @param mode VocabIntent.LOCAL VocabIntent.CLOUD 1889 * @param vocabs 需要更新的词库列表 1890 * @return 请求ID,用于追踪sys.upload.result 1891 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1892 * @see VocabIntent 1893 */ 1894 public String updateVocabs(final int mode,final VocabIntent... vocabs) throws DDSNotInitCompleteException { 1895 checkInitComplete(); 1896 1897 final String id = UUIDUtil.randomUUID(); 1898 if (bc != null) { 1899 asyncExecute(new Runnable() { 1900 @Override 1901 public void run() { 1902 JSONObject obj = new JSONObject(); 1903 try { 1904 obj.put("reqId", id); 1905 1906 JSONArray arr = new JSONArray(); 1907 StringBuilder sb = new StringBuilder(); 1908 long t1 = System.currentTimeMillis(); 1909 for (VocabIntent s : vocabs) { 1910 if (s.isNumberExtension() && s.getContents() != null) { 1911 for (int i = 0; i < s.getContents().size(); i++) { 1912 String c = s.getContents().get(i); 1913 if (c != null && !c.contains(":")) { 1914 boolean transform2Num = StringUtil.str2Num(c, sb); 1915 if (transform2Num) { 1916 s.getContents().set(i, c + ":" + sb.toString()); 1917 } 1918 } 1919 } 1920 } 1921 arr.put(new JSONObject(s.toJson())); 1922 } 1923 AILog.d(TAG, "trans2Number cost " + (System.currentTimeMillis() - t1)); 1924 obj.put("vocabs", arr); 1925 } catch (JSONException e) { 1926 AILog.e(TAG, "updateVocabs failed due to JSONException"); 1927 AIJavaException.printException(e); 1928 } 1929 AILog.userI(TAG, "updateVocabs input =", obj.toString()); 1930 UploadTopicUtil.publishUploadVocabs(bc, obj,mode); 1931 } 1932 }); 1933 } else { 1934 AILog.e(TAG, "updateVocabs failed due to null busclient"); 1935 } 1936 return id; 1937 } 1938 1939 /** 1940 * 更新热词识别接口(请求级别) 1941 * <p> 1942 * 每次请求都会带上热词词库, 例如: [{"type": "vocab", "name": "行政区", "data":["黄浦区"]}] 1943 * 本接口为覆盖式接口,以最新设置为准,如需要清除请求空参数即可 1944 * 1945 * @param phraseHints 需要更新的热词识别列表 1946 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 1947 * @see PhraseHintsIntent 1948 */ 1949 public void updatePhraseHints(PhraseHintsIntent... phraseHints) throws DDSNotInitCompleteException { 1950 checkInitComplete(); 1951 if (bc != null) { 1952 String phrashHintsJson; 1953 if (phraseHints != null) { 1954 phrashHintsJson = AIGsonUtil.getInstance().toJson(phraseHints); 1955 } else { 1956 phrashHintsJson = "[]"; 1957 } 1958 AILog.userI(TAG, "updatePhraseHints input =", phrashHintsJson); 1959 DmsTopicUtil.publishUploadPhrasehints(bc, phrashHintsJson); 1960 } else { 1961 AILog.e(TAG, "updatePhraseHints failed due to null busclient"); 1962 } 1963 } 1964 1965 /** 1966 * 设置热词,用于在一路中做热词增强识别。如果不启用该功能请不要使用该方法 1967 * <p> 1968 * 每次请求时会带上热词。上传形式如: [{"name":"common", "words":["示例"], ”boost“:3}] 1969 * </p> 1970 * 使用示例: 1971 * <pre> 1972 * PhraseList p = new PhraseList(); 1973 * p.setName("common"); // 槽位 默认是 common 1974 * p.setBoost(1); // 表示增强强度,从小到大取值是1,2,3; 默认值是1 1975 * p.addWord("苏州"); 1976 * p.addWord("上海"); 1977 * DDS.getInstance().getAgent().updatePhraseList(p); 1978 * </pre> 1979 * 1980 * @param phraseLists 热词 1981 */ 1982 public void updatePhraseList(PhraseList... phraseLists) { 1983 String data = phraseLists == null || phraseLists.length == 0 ? "" 1984 : AIGsonUtil.getInstance().toJson(phraseLists); 1985 AILog.userI(TAG, "updatePhraseList input =", data); 1986 LocalKeysUtil.getInstance().put(LocalKeys.Dms.LOCAL_KEY_PHRASELIST, data, false); 1987 } 1988 1989 /** 1990 * 设置需要进行识别过滤的词 1991 * 使用示例: 1992 * <pre> 1993 * String asrDropWords = "你好小驰,小苹果"; 1994 * DDS.getInstance().getAgent().updateAsrDropWords(asrDropWords); 1995 * </pre> 1996 * 1997 * @param asrDropWords 识别过滤词 1998 */ 1999 public void updateAsrDropWords(String asrDropWords) { 2000 AILog.userI(TAG, "updateAsrDropWords input =", asrDropWords); 2001 LocalKeysUtil.getInstance().put(LocalKeys.Dms.LOCAL_KEY_ASR_DROP_WORDS, asrDropWords, false); 2002 } 2003 2004 /** 2005 * @deprecated Replaced by {@link #updateVocabs(VocabIntent...)} 2006 */ 2007 @Deprecated 2008 public String updateVocab(String vocabName, String[] contents, boolean addOrDelete) throws 2009 DDSNotInitCompleteException { 2010 checkInitComplete(); 2011 2012 String id = UUIDUtil.randomUUID(); 2013 if (bc != null) { 2014 JSONObject obj = new JSONObject(); 2015 try { 2016 obj.put("reqId", id); 2017 obj.put("type", "vocab"); 2018 obj.put("vocabName", vocabName); 2019 if (null != contents) { 2020 JSONArray contentsArray = new JSONArray(); 2021 for (String s : contents) { 2022 contentsArray.put(s); 2023 } 2024 obj.put("vocabs", contentsArray); 2025 } 2026 obj.put("method", addOrDelete ? "POST" : "DELETE"); 2027 } catch (JSONException e) { 2028 AILog.e(TAG, "updateVocab failed due to JSONException"); 2029 AIJavaException.printException(e); 2030 } 2031 UploadTopicUtil.publishUploadVocab(bc, obj,VocabIntent.ALL); 2032 } else { 2033 AILog.e(TAG, "updateVocab failed due to null busclient"); 2034 } 2035 2036 return id; 2037 } 2038 2039 /** 2040 * 更新设备状态,用来保存设备的一些状态,比如:蓝牙是否打开 2041 * 更新结果可以通过sys.upload.result消息来获取 2042 * <p> 2043 * 2044 * @param deviceJson 设备状态信息的Json串,例如: 2045 * {"bluetooth":{”state“:"disconnected"}},在技能中使用$context.bluetooth.state$将会获取到返回值"disconnected" 2046 * @return 请求ID,用于追踪sys.upload.result 2047 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2048 * @deprecated Replaced by {@link Agent#updateProductContext(ContextIntent)}} 2049 * {@link Agent#updateSkillContext(ContextIntent)}} 2050 */ 2051 @Deprecated 2052 public String updateDeviceInfo(String deviceJson) throws 2053 DDSNotInitCompleteException { 2054 checkInitComplete(); 2055 String id = null; 2056 try { 2057 JSONObject jsonObject = new JSONObject(deviceJson); 2058 Iterator<String> keyIterator = jsonObject.keys(); 2059 if (keyIterator.hasNext()) { 2060 String key = keyIterator.next(); 2061 ContextIntent contextIntent = new ContextIntent(key, jsonObject.opt(key).toString()); 2062 id = updateProductContext(contextIntent); 2063 } 2064 } catch (Exception e) { 2065 AIJavaException.printException(e); 2066 } 2067 return id; 2068 } 2069 2070 /** 2071 * 更新设备状态,产品级的配置。比如:定位信息,设备硬件状态等 2072 * 更新结果可以通过sys.upload.result消息来获取 2073 * 2074 * @param intent 请求的ContextIntent对象,包括key和value 2075 * 如:ContextIntent intent = new ContextIntent("location",{"city":"苏州市"}); 2076 * updateProductContext(intent); 2077 * 技能里通过$context.location.city$即可获取到"苏州市" 2078 * @return 请求的ID,用于追踪sys.upload.result 2079 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2080 */ 2081 public String updateProductContext(ContextIntent intent) throws DDSNotInitCompleteException { 2082 checkInitComplete(); 2083 AILog.userI(TAG, "updateProductContext obj =", (intent != null ? intent.toString() : "")); 2084 2085 String id = UUIDUtil.randomUUID(); 2086 2087 if (bc != null) { 2088 DmsTopicUtil.publishSystemSettings(bc, intent.keyValue2JSONArray(), id, intent.getOption()); 2089 } else { 2090 AILog.e(TAG, "updateProductContext failed due to null busclient"); 2091 } 2092 2093 return id; 2094 } 2095 2096 /** 2097 * 更新技能配置,需要调用ContextIntent.setSkillId设置技能ID 2098 * 更新结果可以通过sys.upload.result消息来获取 2099 * 2100 * @param intent 请求的ContextIntent对象,包括key、value、skillId。 2101 * @return 请求的ID,用于追踪sys.upload.result 2102 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2103 */ 2104 public String updateSkillContext(ContextIntent intent) throws DDSNotInitCompleteException { 2105 checkInitComplete(); 2106 AILog.userI(TAG, "updateSkillContext obj =", (intent != null ? intent.toString() : "")); 2107 if (TextUtils.isEmpty(intent.getSkillId())) { 2108 throw new IllegalArgumentException("updateSkillContext, ContextIntent must set skillId!"); 2109 } 2110 2111 String id = UUIDUtil.randomUUID(); 2112 2113 if (bc != null) { 2114 DmsTopicUtil.publishSkillSettings(bc, intent.keyValue2JSONArray(), id, intent.getSkillId(), intent.getOption()); 2115 } else { 2116 AILog.e(TAG, "updateSkillContext failed due to null busclient"); 2117 } 2118 2119 return id; 2120 } 2121 2122 /** 2123 * 在多轮对话中强制设置为首轮,进入延迟聆听状态。(全双工模式下支持此接口) 2124 * 如果当前在对话中,则进入延迟聆听状态并发送消息:sys.dialog.endSkillDm 2125 * 如果当前在播报中,则打断当前播报进入延迟聆听状态并发送消息:sys.dialog.endSkillDm 2126 * 如果当前不在对话中,此接口无效 2127 * 2128 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2129 */ 2130 public String endSkillInDialog() throws DDSNotInitCompleteException { 2131 checkInitComplete(); 2132 AILog.userI(TAG, "endSkillInDialog..."); 2133 2134 if (bc != null) { 2135 ProcessorTopicUtil.publishEndSkillInDialog(bc); 2136 } else { 2137 AILog.e(TAG, "endSkillInDialog failed due to null busclient"); 2138 } 2139 return null; 2140 } 2141 2142 /** 2143 * 自定义多模态事件同步,该接口用于客户端给对话中控发送一个事件 2144 * 2145 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2146 */ 2147 public void updateCustomSyncInfo(JSONObject obj) throws DDSNotInitCompleteException { 2148 checkInitComplete(); 2149 AILog.userI(TAG, "updateCustomSyncInfo input =", (obj != null ? obj.toString() : "")); 2150 if (bc != null && obj != null) { 2151 DmsTopicUtil.publishCustomContextSync(bc, obj); 2152 } else { 2153 AILog.e(TAG, "updateCustomSyncInfo failed due to null busclient"); 2154 } 2155 } 2156 2157 /** 2158 * 多模态事件同步,该接口用于客户端给对话中控发送一个事件,例如: 2159 * <li> 2160 * <ul>不再支持: <s>在多轮对话中强制设置为首轮,传入数据为: new JSONObject().put("endSkill", "true")</s></ul> 2161 * </li> 2162 * 2163 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2164 */ 2165 public String updateDispatchEvent(JSONObject obj) throws DDSNotInitCompleteException { 2166 checkInitComplete(); 2167 AILog.userI(TAG, "updateDispatchEvent input =", (obj != null ? obj.toString() : "")); 2168 if (bc != null && obj != null) { 2169 String requestId = UUIDUtil.randomUUID(); 2170 try { 2171 obj.put("requestId", requestId); 2172 } catch (JSONException e) { 2173 AIJavaException.printException(e); 2174 } 2175 ProcessorTopicUtil.publishUpdateDispatchEvent(bc, obj); 2176 return requestId; 2177 } else { 2178 AILog.e(TAG, "updateDispatchEvent failed due to null busclient"); 2179 } 2180 return null; 2181 } 2182 2183 /** 2184 * 设置对话离线与在线模式 2185 * 2186 * @param policy: 0: 在线优先模式,如果网络离线自动切换到离线 2187 * 1: 强制离线模式 2188 * 2: 强制在线模式 2189 * 3: 混合对话模式 2190 */ 2191 public void updateDmsPolicy(int policy) throws DDSNotInitCompleteException { 2192 checkInitComplete(); 2193 AILog.userI(TAG, "updateDmsPolicy input =", policy); 2194 if (bc != null) { 2195 DmsTopicUtil.publishUpdateDmsPolicy(bc, policy); 2196 } else { 2197 AILog.e(TAG, "updateDmsPolicy failed due to null busclient"); 2198 } 2199 } 2200 2201 /** 2202 * 对话同步接口 2203 * 2204 * @param obj 2205 * @throws DDSNotInitCompleteException 2206 */ 2207 public void updateDmSync(JSONObject obj) throws DDSNotInitCompleteException { 2208 checkInitComplete(); 2209 AILog.userI(TAG, "updateDmSync input =", (obj != null ? obj.toString() : "")); 2210 2211 if (bc != null && obj != null) { 2212 ProcessorTopicUtil.publishInputDmSync(bc, ProcessorJsonBuilder.getDmSyncObj(obj)); 2213 } else { 2214 AILog.e(TAG, "updateDmSync failed due to null busclient"); 2215 } 2216 } 2217 2218 private Object getContext(String inputKey, String type, String skillId) { 2219 String data = LocalKeysUtil.getInstance().getString(skillId == null ? type : (type + skillId)); 2220 try { 2221 JSONObject obj = new JSONObject(data); 2222 return obj.opt(inputKey); 2223 } catch (Exception e) { 2224 } 2225 return null; 2226 } 2227 2228 /** 2229 * 获取产品的配置信息 2230 * 2231 * @param inputKey 配置项名称 2232 * @return 配置项值 2233 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2234 */ 2235 public Object getProductContext(String inputKey) throws DDSNotInitCompleteException { 2236 checkInitComplete(); 2237 Object result = getContext(inputKey, LocalKeys.Dms.LOCAL_KEY_SYSTEM_SETTINGS, null); 2238 AILog.userI(TAG, "getProductContext result =", result); 2239 return result; 2240 } 2241 2242 /** 2243 * 获取技能的配置信息 2244 * 2245 * @param skillId 技能ID 2246 * @param inputKey 配置项名称 2247 * @return 配置项值 2248 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2249 */ 2250 public Object getSkillContext(String skillId, String inputKey) throws DDSNotInitCompleteException { 2251 checkInitComplete(); 2252 Object result = getContext(inputKey, "/local_keys/", skillId); 2253 AILog.userI(TAG, "getProductContext result =", result); 2254 return result; 2255 } 2256 2257 2258 /** 2259 * 清空客户端上传的设备信息 2260 * 2261 * @param key 不为null时,清空指定key的设备信息,如"state" 2262 * @return 请求ID,用于追踪sys.upload.result 2263 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2264 */ 2265 public String clearDeviceInfo(String key) throws DDSNotInitCompleteException { 2266 // v2接口不再支持删除全部cinfo信息, 所以key不可以为null 2267 AILog.userI(TAG, "clearDeviceInfo input =", key); 2268 if (TextUtils.isEmpty(key)) { 2269 return ""; 2270 } 2271 checkInitComplete(); 2272 2273 ContextIntent contextIntent = new ContextIntent(key, ""); 2274 contextIntent.setOption("delete"); 2275 return updateProductContext(contextIntent); 2276 } 2277 2278 /** 2279 * 获取客户端保存的设备信息 2280 * 2281 * @return 设备信息的jsonString,例如:{"bluetooth":"disconnected"} 2282 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2283 */ 2284 public String getDeviceInfo() throws DDSNotInitCompleteException { 2285 checkInitComplete(); 2286 String result = LocalKeysUtil.getInstance().getString(LocalKeys.Dms.LOCAL_KEY_SYSTEM_SETTINGS); 2287 if (TextUtils.isEmpty(result)) { 2288 result = LocalKeysUtil.getInstance().getString(LocalKeys.Upload.LOCAL_KEYS_DEVICE_INFO); 2289 } 2290 AILog.userI(TAG, "getDeviceInfo result =", result); 2291 return result; 2292 } 2293 2294 /** 2295 * 此接口在Agent中已弃用,参见{@link WakeupEngine#updateMinorWakeupWord(String, String, String, String[])} 2296 * 更新副唤醒词的接口 2297 * <p> 2298 * 支持设置一个副唤醒词,重复调用会以最新的副唤醒词为准 2299 * 2300 * @param word 副唤醒词, 若设置null,则清空当前的副唤醒词 2301 * @param pinyin 副唤醒词的拼音 2302 * @param threshold 副唤醒词的阈值, 若设置null,则自动估算 2303 * @param greetings 副唤醒词的欢迎语, 若设置null,则与主唤醒词保持一致 2304 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2305 */ 2306 @Deprecated 2307 public void updateMinorWakeupWord(String word, String pinyin, String threshold, String[] greetings) throws 2308 DDSNotInitCompleteException { 2309 getWakeupEngine().updateMinorWakeupWord(word, pinyin, threshold, greetings); 2310 } 2311 2312 /** 2313 * 获取ASR引擎 2314 * 2315 * @return ASREngine实例 2316 */ 2317 public ASREngine getASREngine() { 2318 return ASREngine.getInstance(); 2319 } 2320 2321 /** 2322 * 获取TTS引擎 2323 * 2324 * @return TTSEngine实例 2325 */ 2326 public TTSEngine getTTSEngine() { 2327 return TTSEngine.getInstance(); 2328 } 2329 2330 /** 2331 * 获取Wakeup引擎 2332 * 2333 * @return Wakeup实例 2334 */ 2335 public WakeupEngine getWakeupEngine() { 2336 return WakeupEngine.getInstance(); 2337 } 2338 2339 /** 2340 * 获取Vprint引擎 2341 * 2342 * @return Vprint实例 2343 */ 2344// public VprintEngine getVprintEngine() { 2345// return VprintEngine.getInstance(); 2346// } 2347 2348 /** 2349 * 此接口在Agent中已弃用,参见{@link WakeupEngine#updateCommandWakeupWord(String[], String[], String[], String[], String[][])} 2350 * 更新命令唤醒词的接口,这类唤醒词会在唤醒之后执行一条指令,不能打断正在播报的语音 2351 * <p> 2352 * 支持设置多个命令唤醒词,所以参数为数组,重复调用会以最新的命令唤醒词数组为准。 2353 * 2354 * @param actions 命令唤醒词对应的command命令(必须) 2355 * @param words 命令唤醒词的汉字(必须) 2356 * @param pinyin 命令唤醒词的拼音(必须) 2357 * @param threshold 命令唤醒词的阈值(必须 2358 * @param greetings 命令唤醒词对应的唤醒语,一个唤醒词可以设置多条欢迎语,所以参数为二维数组,如果想要某个唤醒词不要欢迎语,那么该第二维数组的string可以设置为空字符串"" 2359 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2360 */ 2361 @Deprecated 2362 public void updateCommandWakeupWord(String[] actions, String[] words, String[] pinyin, String[] threshold, String[][] greetings) throws DDSNotInitCompleteException { 2363 getWakeupEngine().updateCommandWakeupWord(actions, words, pinyin, threshold, greetings); 2364 } 2365 2366 /** 2367 * 此接口在Agent中已弃用,参见{@link WakeupEngine#clearCommandWakeupWord()} 2368 * 清空命令唤醒词的接口 2369 * 2370 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2371 */ 2372 @Deprecated 2373 public void clearCommandWakeupWord() throws DDSNotInitCompleteException { 2374 checkInitComplete(); 2375 WakeupTopicUtil.publishStickyRenameMinorWord(bc, null); 2376 } 2377 2378 2379 /** 2380 * 此接口在Agent中已弃用,参见{@link WakeupEngine#clearShortCutWakeupWord()} 2381 * 清空打断唤醒词的接口 2382 * 2383 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2384 */ 2385 @Deprecated 2386 public void clearShortCutWakeupWord() throws DDSNotInitCompleteException { 2387 getWakeupEngine().clearShortCutWakeupWord(); 2388 } 2389 2390 /** 2391 * 此接口在Agent中已弃用,参见{@link WakeupEngine#updateShortcutWakeupWord(String[], String[], String[])} 2392 * 更新打断唤醒词的接口,这类唤醒词能打断正在播报的语音并且将唤醒词送入识别 2393 * <p> 2394 * 支持设置多个打断唤醒词,所以参数为数组,重复调用会以最新的打断唤醒词数组为准。 2395 * 2396 * @param words 打断唤醒词的汉字(必须) 2397 * @param pinyin 打断唤醒词的拼音(必须) 2398 * @param threshold 打断唤醒词的阈值(必须 2399 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2400 */ 2401 @Deprecated 2402 public void updateShortcutWakeupWord(String[] words, String[] pinyin, String[] threshold) throws DDSNotInitCompleteException { 2403 getWakeupEngine().updateShortcutWakeupWord(words, pinyin, threshold); 2404 } 2405 2406 /** 2407 * 设置VAD后端停顿时间的接口 2408 * <p> 2409 * 若VAD在用户说话时停顿超过一定的时间,则认为用户已经说完,发出sys.vad.end消息,结束录音。 2410 * 2411 * @param millis 后端停顿时间,单位为毫秒。默认值为500毫秒。 2412 * @return true-设置成功;false-设置失败 2413 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2414 */ 2415 @Deprecated 2416 public boolean setVadPauseTime(long millis) throws DDSNotInitCompleteException { 2417 return getASREngine().setVadPauseTime(millis); 2418 } 2419 2420 /** 2421 * 获取VAD后端停顿时间的接口 2422 * 2423 * @return millis 后端停顿时间,单位为毫秒。 2424 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2425 */ 2426 @Deprecated 2427 public long getVadPauseTime() throws DDSNotInitCompleteException { 2428 return getASREngine().getVadPauseTime(); 2429 } 2430 2431 /** 2432 * 输入文本的接口 2433 * <p> 2434 * 跳过对话中的识别过程,直接开始对话中的语义理解 2435 * <p> 2436 * 若当前没有在对话中,则以文本作为首轮说法,新发起一轮对话请求 2437 * 若当前正在对话中,则跳过识别,直接发送文本 2438 * 2439 * @param text 输入的文本内容 2440 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2441 */ 2442 public void sendText(String text) throws DDSNotInitCompleteException { 2443 checkInitComplete(); 2444 AILog.userI(TAG, "sendText input =", text); 2445 if (bc != null) { 2446 ProcessorTopicUtil.publishInputText(bc, text, DDSConstant.DmsInputInnerType.USER_INPUT_TEXT); 2447 } else { 2448 AILog.e(TAG, "feedText failed due to null busclient"); 2449 } 2450 } 2451 2452 public List<SkillConfig> getSkillList() throws DDSNotInitCompleteException { 2453 List<SkillConfig> list = new ArrayList<>(); 2454 2455 String path = getFromKeys(LocalKeys.Daemon.LOCAL_KEY_CUSTOM_HOME); 2456 if (TextUtils.isEmpty(path)) { 2457 AILog.e(TAG, "Never Happened! invalid custom_home"); 2458 return list; 2459 } 2460 2461 String productCfg = null; 2462 try { 2463 productCfg = FileUtils.readFile(path + "/product.cfg"); 2464 } catch (IOException e) { 2465 AIJavaException.printException(e); 2466 } 2467 if (TextUtils.isEmpty(productCfg)) { 2468 AILog.e(TAG, "Never Happened! invalid product.cfg"); 2469 return list; 2470 } 2471 2472 try { 2473 JSONObject obj = new JSONObject(productCfg); 2474 JSONArray array = obj.getJSONArray("skill_list"); 2475 for (int i = 0; i < array.length(); i++) { 2476 JSONObject skillObj = array.getJSONObject(i); 2477 SkillConfig skillConfig = new SkillConfig(); 2478 skillConfig.setId(skillObj.getString("id")); 2479 skillConfig.setName(skillObj.getString("name")); 2480 AILog.i(TAG, skillConfig.toString()); 2481 list.add(skillConfig); 2482 } 2483 } catch (JSONException e) { 2484 AIJavaException.printException(e); 2485 } 2486 AILog.userI(TAG, "getSkillList result =", list.toString()); 2487 return list; 2488 } 2489 2490 /** 2491 * 暂停当前对话 2492 * <p> 2493 * 2494 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2495 */ 2496 public void pauseDialog() throws DDSNotInitCompleteException { 2497 checkInitComplete(); 2498 AILog.userI(TAG, "pauseDialog"); 2499 if (bc != null) { 2500 ProcessorTopicUtil.publishDialogCtrl(bc, "freeze"); 2501 } else { 2502 AILog.e(TAG, "pauseDialog failed due to null busclient"); 2503 } 2504 } 2505 2506 /** 2507 * 恢复当前对话 2508 * <p> 2509 * 2510 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 2511 */ 2512 public void resumeDialog() throws DDSNotInitCompleteException { 2513 checkInitComplete(); 2514 AILog.userI(TAG, "resumeDialog"); 2515 if (bc != null) { 2516 ProcessorTopicUtil.publishDialogCtrl(bc, "resume"); 2517 } else { 2518 AILog.e(TAG, "resumeDialog failed due to null busclient"); 2519 } 2520 } 2521 2522 /** 2523 * 此接口支持修改对话中的语音播报,修改文本展示等功能 2524 * 2525 * @param callback 对话监听器 2526 * @deprecated 该接口极端情况会导致rpc死锁问题,已舍弃。建议使用支持{@code topic}的{{@link #setDMTaskCallback(DMTaskCallback)}}接口 2527 */ 2528 @Deprecated 2529 public void setDMCallback(DMCallback callback) { 2530 this.mDMCallback = callback; 2531 if (bc != null) { 2532 ProcessorTopicUtil.publishDmsWaitOperation(bc, mDMCallback != null); 2533 } else { 2534 AILog.e(TAG, "setDMCallback failed due to null busclient"); 2535 } 2536 } 2537 2538 /** 2539 * 此接口支持修改唤醒逻辑,是否需要忽略唤醒词类型强行唤醒/忽略唤醒 2540 * 2541 * @param dmWkupRetCallback 对话监听器 2542 */ 2543 public void setDMWkupRetCallback(DMWkupRetCallback dmWkupRetCallback) { 2544 this.mDmWkupRetCallback = dmWkupRetCallback; 2545 AILog.userI(TAG, "setDMWkupRetCallback input =", (dmWkupRetCallback != null)); 2546 if (mDmWkupRetCallback != null) { 2547 if (bc != null) { 2548 JSONObject jo = new JSONObject(); 2549 try { 2550 jo.put(Topic.Processor.WkupOperationEnable.STATE, "enable"); 2551 ProcessorTopicUtil.publishWkupOperationEnable(bc, jo); 2552 } catch (Exception e) { 2553 e.printStackTrace(); 2554 } 2555 } else { 2556 AILog.e(TAG, "setDMWkupRetCallback failed due to null busclient"); 2557 } 2558 } else { 2559 if (bc != null) { 2560 JSONObject jo = new JSONObject(); 2561 try { 2562 jo.put(Topic.Processor.WkupOperationEnable.STATE, "disable"); 2563 ProcessorTopicUtil.publishWkupOperationEnable(bc, jo); 2564 } catch (Exception e) { 2565 e.printStackTrace(); 2566 } 2567 } else { 2568 AILog.e(TAG, "setDMWkupRetCallback null failed due to null busclient"); 2569 } 2570 } 2571 } 2572 2573 /** 2574 * 配置对话的额外参数,由客户和服务器约定键值。设置后会在下次对话开始时带上 2575 * <p> 2576 * 对话发送 recorder.stream.start 消息的报文时,都会在 context.attributes 字段加上这里配置 JSON 2577 * </p> 2578 * 2579 * @param extInfo 额外参数, 为空时标识额外参数置空 2580 */ 2581 public void setDialogExtInfo(JSONObject extInfo) { 2582 AILog.userI(TAG, "setDialogExtInfo input =", extInfo); 2583 LocalKeysUtil.getInstance().put(LocalKeys.BA.LOCAL_KEY_BA_EXT_INFO, extInfo == null ? "" : extInfo.toString()); 2584 } 2585 2586 public void setAudioFocusCallback(AudioFocusCallback audioFocusCallback) { 2587 this.mAudioFocusCallback = audioFocusCallback; 2588 AILog.userI(TAG, "setAudioFocusCallback"); 2589 if (mAudioFocusCallback != null) { 2590 if (bc != null) { 2591 bc.publishSticky(TOPIC_PLAYER_AUDIOFOCUS_CALLBACK); 2592 } else { 2593 AILog.e(TAG, "setAudioFocusCallback failed due to null busclient"); 2594 } 2595 } 2596 } 2597 2598 2599 /** 2600 * 新增技能锁定接口,让用户说的话只落在锁定的技能里 2601 * 2602 * @param jsonArray skillId array, 2603 * [ 2604 * {"skillId": "123123"}, 2605 * {"skillId": "123123"}, 2606 * {"skillId": "123123"}, 2607 * ] 2608 */ 2609 public void updatePermanentSkill(JSONArray jsonArray) throws DDSNotInitCompleteException { 2610 checkInitComplete(); 2611 AILog.userI(TAG, "updatePermanentSkill input =", (jsonArray != null ? jsonArray.toString() : "null")); 2612 if (bc != null) { 2613 if (jsonArray != null) { 2614 ProcessorTopicUtil.publishUpdatePermanentSkill(bc, jsonArray); 2615 } else { 2616 AILog.e(TAG, "updatePermanentSkill failed due to null jsonArray"); 2617 } 2618 } else { 2619 AILog.e(TAG, "updatePermanentSkill failed due to null busclient"); 2620 } 2621 } 2622 2623 /** 2624 * 此接口支持修改对话中的语音播报,修改文本展示等功能 2625 * 2626 * @param dmTaskCallback 对话监听器 2627 */ 2628 public void setDMTaskCallback(DMTaskCallback dmTaskCallback) { 2629 AILog.userI(TAG, "setDMTaskCallback"); 2630 if(CallbackManager.getInstance().getOnDmTaskCallback() != null) { 2631 throw new IllegalStateException("you have setDMTaskCallbackSync"); 2632 } 2633 this.mDMTaskCallback = dmTaskCallback; 2634 if (bc != null) { 2635 ProcessorTopicUtil.publishTaskOperationEnable(bc, mDMTaskCallback != null); 2636 } else { 2637 AILog.e(TAG, "setDMTaskCallback failed due to null busclient"); 2638 } 2639 } 2640 2641 /** 2642 * 此接口支持修改对话中的语音播报,修改文本展示等功能,同步方法 2643 * 2644 * @param dmTaskCallbackSync 对话监听器 2645 */ 2646 public void setDMTaskCallbackSync(final DMTaskCallbackSync dmTaskCallbackSync) { 2647 AILog.userI(TAG, "setDMTaskCallback"); 2648 if(mDMTaskCallback != null) { 2649 throw new IllegalStateException("you have setDMTaskCallback"); 2650 } 2651 if(dmTaskCallbackSync != null) { 2652 CallbackManager.getInstance().setOnDmTaskCallback(new OnDmTaskCallback() { 2653 @Override 2654 public void onDMTaskResult(JSONObject dmTaskResult) { 2655 DMTaskCallbackSync.Type type = DMTaskCallbackSync.Type.getTypeByVal(dmTaskResult.optString("from")); 2656 dmTaskCallbackSync.onDMTaskResultSync(dmTaskResult,type); 2657 } 2658 }); 2659 } else { 2660 CallbackManager.getInstance().setOnDmTaskCallback(null); 2661 } 2662 } 2663 2664 2665 /** 2666 * 此接口支持动态修改 custom tips 2667 * 2668 * @param obj <p> 2669 * 取值:JSON字符串,如:<br> 2670 * { 2671 * "71304":"这是识别结果为空的自定义播报", 2672 * "71305":"这是语义结果为空的自定义播报", 2673 * "71308":"这是进入闲聊技能的自定义播报", 2674 * "713**":"*****" 2675 * }<br> 2676 * @throws DDSNotInitCompleteException 2677 */ 2678 public void setCustomTips(JSONObject obj) throws DDSNotInitCompleteException { 2679 checkInitComplete(); 2680 AILog.userI(TAG, "setCustomTips input =", (obj != null ? obj.toString() : "null")); 2681 if (bc != null) { 2682 ProcessorTopicUtil.publishTipCustomTips(bc, obj); 2683 } else { 2684 AILog.e(TAG, "setCustomTips failed due to null busclient"); 2685 } 2686 } 2687 2688 @Override 2689 public void onNoObserverAttached(String topic) { 2690 AILog.d(TAG, "onNoObserverAttached: " + topic); 2691// synchronized (topicsLock) { 2692 subscribedTopics.remove(topic); 2693 if (bc != null) { 2694 bc.unsubscribe(topic); 2695 } 2696// } 2697 2698 } 2699 2700 @Override 2701 public void onFirstObserverAttached(String topic) { 2702 AILog.d(TAG, "onFirstObserverAttached: " + topic); 2703 subscribedTopics.add(topic); 2704 if (bc != null) { 2705 bc.subscribe(topic); 2706 } 2707 } 2708 2709 private void uploadData() { 2710 SysTopicUtil.publishUploadHardwareInfo(bc, DeviceUtil.queryHardwareInfo(context)); 2711 try { 2712 JSONObject jsonObject = new JSONObject(); 2713 jsonObject.put("type",NetworkUtils.getCurrentNetType(context)); 2714 SysTopicUtil.publishSysNetworkType(bc, jsonObject); 2715 } catch (JSONException e) { 2716 e.printStackTrace(); 2717 } 2718 } 2719 2720 private void checkInitComplete() throws DDSNotInitCompleteException { 2721 if (DDS.getInstance().getInitStatus() != DDS.INIT_COMPLETE_FULL) { 2722 throw new DDSNotInitCompleteException(); 2723 } 2724 } 2725 2726 private void checkInitPartComplete() throws DDSNotInitCompleteException { 2727 if (DDS.getInstance().getInitStatus() == DDS.INIT_COMPLETE_NONE) { 2728 throw new DDSNotInitCompleteException(); 2729 } 2730 } 2731 2732 public Set<String> getSubscribedTopics() { 2733 return subscribedTopics; 2734 } 2735 2736 2737 private class ConnectionChangeReceiver extends BroadcastReceiver { 2738 2739 @Override 2740 public void onReceive(Context context, Intent intent) { 2741 ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 2742 if (connectivityManager != null) { 2743 NetworkInfo info = connectivityManager.getActiveNetworkInfo(); 2744 JSONObject jsonObject = new JSONObject(); 2745 String delayStr = PlayerConfig.getInstance().getStringConfig(DDSConfig.K_NETWORKMANAGER_DELAY); 2746 if (!TextUtils.isEmpty(delayStr)) { 2747 int delay = Integer.parseInt(delayStr); 2748 SystemClock.sleep(delay); 2749 } 2750 if (info != null) { 2751 switch (info.getType()) { 2752 case ConnectivityManager.TYPE_ETHERNET: 2753 // 联网之后检测token是否可用 2754 AccessTokenManager.getInstance(Agent.this.context).checkAccessToken(); 2755 if (bc != null) { 2756 try { 2757 jsonObject.put("type", "ETHERNET"); 2758 SysTopicUtil.publishSysNetworkType(bc, jsonObject); 2759 } catch (Exception e) { 2760 AIJavaException.printException(e); 2761 } 2762 } 2763 break; 2764 case ConnectivityManager.TYPE_WIFI: 2765 // 联网之后检测token是否可用 2766 AccessTokenManager.getInstance(Agent.this.context).checkAccessToken(); 2767 if (bc != null) { 2768 try { 2769 jsonObject.put("type", "WIFI"); 2770 AILog.e(TAG, "net = " + jsonObject.toString()); 2771 SysTopicUtil.publishSysNetworkType(bc, jsonObject); 2772 } catch (Exception e) { 2773 AIJavaException.printException(e); 2774 } 2775 } 2776 break; 2777 case ConnectivityManager.TYPE_MOBILE: 2778 // 联网之后检测token是否可用 2779 AccessTokenManager.getInstance(Agent.this.context).checkAccessToken(); 2780 String type = getNetworkTypeName(info.getSubtype()); 2781 String generation = "TYPE_MOBILE"; 2782 if (type.equals("GPRS") || type.equals("EDGE") || type.equals("CDMA")) { 2783 generation = "2G"; 2784 } else if (type.equals("UMTS") || type.equals("HSDPA") || type.equals("TD_SCDMA") 2785 || type.contains("EVDO") || type.contains("HS")) { 2786 generation = "3G"; 2787 } else if (type.equals("LTE")) { 2788 generation = "4G"; 2789 } 2790 if (bc != null) { 2791 try { 2792 jsonObject.put("type", generation); 2793 SysTopicUtil.publishSysNetworkType(bc, jsonObject); 2794 } catch (Exception e) { 2795 AIJavaException.printException(e); 2796 } 2797 } 2798 break; 2799 default: 2800 break; 2801 } 2802 } else { 2803 ArrayList<MessageObserver> observers = messageObservers.get("sys.fatal.exception"); 2804 if (observers != null) { 2805 for (MessageObserver observer : observers) { 2806 try { 2807 AILog.e(TAG, "MESSAGE_FATAL_EXCEPTION for TVUI!"); 2808 observer.onMessage("sys.fatal.exception", null); 2809 } catch (Exception e) { 2810 throw new BusClient.BusClientImplementException(e); 2811 } 2812 } 2813 } 2814 2815 if (bc != null) { 2816 try { 2817 jsonObject.put("type", "none"); 2818 SysTopicUtil.publishSysNetworkType(bc, jsonObject); 2819 } catch (Exception e) { 2820 AIJavaException.printException(e); 2821 } 2822 } 2823 } 2824 } 2825 } 2826 } 2827 2828 private String getNetworkTypeName(int type) { 2829 switch (type) { 2830 case TelephonyManager.NETWORK_TYPE_GPRS: 2831 return "GPRS"; 2832 case TelephonyManager.NETWORK_TYPE_EDGE: 2833 return "EDGE"; 2834 case TelephonyManager.NETWORK_TYPE_UMTS: 2835 return "UMTS"; 2836 case TelephonyManager.NETWORK_TYPE_CDMA: 2837 return "CDMA"; 2838 case TelephonyManager.NETWORK_TYPE_EVDO_0: 2839 return "EVDO revision 0"; 2840 case TelephonyManager.NETWORK_TYPE_EVDO_A: 2841 return "EVDO revision A"; 2842 case TelephonyManager.NETWORK_TYPE_1xRTT: 2843 return "1xRTT"; 2844 case TelephonyManager.NETWORK_TYPE_HSDPA: 2845 return "HSDPA"; 2846 case TelephonyManager.NETWORK_TYPE_HSUPA: 2847 return "HSUPA"; 2848 case TelephonyManager.NETWORK_TYPE_HSPA: 2849 return "HSPA"; 2850 case TelephonyManager.NETWORK_TYPE_IDEN: 2851 return "iDen"; 2852 case TelephonyManager.NETWORK_TYPE_EVDO_B: 2853 return "EVDO revision B"; 2854 case TelephonyManager.NETWORK_TYPE_LTE: 2855 return "LTE"; 2856 case TelephonyManager.NETWORK_TYPE_EHRPD: 2857 return "eHRPD"; 2858 case TelephonyManager.NETWORK_TYPE_HSPAP: 2859 return "HSPA+"; 2860 case TelephonyManager.NETWORK_TYPE_GSM: 2861 return "GSM"; 2862 case TelephonyManager.NETWORK_TYPE_TD_SCDMA: 2863 return "TD_SCDMA"; 2864 case TelephonyManager.NETWORK_TYPE_IWLAN: 2865 return "IWLAN"; 2866 default: 2867 return "UNKNOWN"; 2868 } 2869 } 2870 2871}