001package com.aispeech.dui.dds; 002 003import android.content.Context; 004import android.content.IntentFilter; 005import android.os.Build; 006import android.os.Handler; 007import android.text.TextUtils; 008 009import com.aispeech.dds.libdaemon.DaemonUtil; 010import com.aispeech.dui.BaseNode; 011import com.aispeech.dui.BusClient; 012import com.aispeech.dui.dds.agent.Agent; 013import com.aispeech.dui.dds.agent.MessageObserver; 014import com.aispeech.dui.dds.auth.AccessTokenManager; 015import com.aispeech.dui.dds.auth.AuthType; 016import com.aispeech.dui.dds.exceptions.DDSNotInitCompleteException; 017import com.aispeech.dui.dds.update.DDSUpdater; 018import com.aispeech.dui.dds.utils.AuthUtil; 019import com.aispeech.dui.dds.utils.CheckCrashUtil; 020import com.aispeech.dui.dds.utils.Engines; 021import com.aispeech.dui.dds.utils.FileUtils; 022import com.aispeech.dui.manager.AIJavaException; 023import com.aispeech.dui.manager.AILog; 024import com.aispeech.dui.manager.BusManager; 025import com.aispeech.dui.manager.ThreadName; 026import com.aispeech.dui.oauth.AuthorizationManager; 027import com.aispeech.dui.oauth.OAuthSdk; 028import com.aispeech.dui.oauth.TokenListener; 029import com.aispeech.interceptor.DDSInterceptor; 030import com.aispeech.interceptor.IInterceptor; 031import com.aispeech.libbase.AIGsonUtil; 032import com.aispeech.libbase.AIUtil; 033import com.aispeech.libbase.debug.DebugBean; 034import com.aispeech.libbase.debug.DebugConstant; 035import com.aispeech.libcomm.abslite.EngineProxy; 036import com.aispeech.libcomm.abslite.IEngine; 037import com.aispeech.libcomm.business.LocalKeys; 038import com.aispeech.libcomm.business.LocalKeysUtil; 039import com.aispeech.libcomm.business.call.AuthCallUtil; 040import com.aispeech.libcomm.business.config.PlayerConfig; 041import com.aispeech.libcomm.business.topic.AuthTopicUtil; 042import com.aispeech.libcomm.business.topic.Topic; 043import com.aispeech.libcomm.business.util.PcmUtil; 044import com.aispeech.libcomm.business.util.VersionUtil; 045 046import org.json.JSONException; 047import org.json.JSONObject; 048 049import java.io.IOException; 050import java.net.InetSocketAddress; 051import java.net.ServerSocket; 052import java.util.Random; 053import java.util.regex.Matcher; 054import java.util.regex.Pattern; 055 056 057/** 058 * Disclaim 059 * <p> 060 * This program is the property of AI Speech Ltd. It shall be communicated to 061 * authorized personnel only. It is not to be disclosed outside the group without 062 * prior written consent. If you are not sure if you’re authorized to read this 063 * program, please contact info@aispeech.com before reading. 064 * <p> 065 * Created by jinrui.gan on 17-3-12. 066 */ 067 068public class DDS { 069 /** 070 * Extract dds.bin error. 071 */ 072 public static final int ERROR_EXTRACT_DDS_BIN = 0x00000001; 073 /** 074 * Lasa execute bootloader error. 075 * Contact us. 076 */ 077 public static final int ERROR_LASA_EXECUTE_FAILED = 0x00000002; 078 /** 079 * Waiting sys.kernel.ready timeout error. 080 * Contact us. 081 */ 082 public static final int ERROR_KERNEL_READY_TIMEOUT = 0x00000003; 083 /* 084 * No enough space left on the disk. 085 */ 086 public static final int ERROR_NO_SPACE_LEFT = 0x00000004; 087 088 /** 089 * md5sum not exist. will break DDS 090 */ 091 public static final int ERROR_MD5SUM_NOT_EXIST = 0x00000005; 092 093 public static final String ERROR_MD5SUM_NOT_EXIST_MSG = "md5sum file not exist"; 094 095 private static final String TAG = "DDS"; 096 public static final int INIT_COMPLETE_NONE = 0; 097 public static final int INIT_COMPLETE_NOT_FULL = 1; 098 public static final int INIT_COMPLETE_FULL = 2; 099 public static final int INIT_COMPLETE_WAIT_REFRESH_TOKEN = 3;// 等待刷新token结果 100 private static final int STATE_IDLE = 0; 101 private static final int STATE_BUSY = 1; 102 public static final int AUTH_COMPLETE_NONE = 0; 103 public static final int AUTH_COMPLETE_FULL = 2; 104 private volatile static DDS mInstance; 105 private Agent agent; 106 private DDSUpdater updater; 107 private AIBootloader aiBootloader; 108 private Handler workerHandler; 109 public Context mContext; 110 private String[] bootErrorMsg = new String[]{ 111 "解压dds.bin失败", 112 "内核执行出错", 113 "系统启动超时", 114 "存储空间不足", 115 "IO异常" 116 }; 117 /** 118 * Indicate that core and hybrid nodes has been boot up. 119 */ 120 private int isInitComplete = INIT_COMPLETE_NONE; 121 122 /** 123 * Indicate that auth has been boot up. 124 */ 125 private int isAuthComplete = -1; 126 127 private boolean isError = false; 128 private int state = STATE_IDLE; 129// private CountDownLatch countDownLatch = new CountDownLatch(1); 130 public static final String DNS_SERVER = "127.0.0.1:5353"; 131 public static String BUS_SERVER_ADDR = "127.0.0.1:50001"; 132 private static int INIT_TIMEOUT = 30; 133 private long mInitTimeStamp = 0;//初始化时间戳 134 private DDSBroadcastReceiver mDDSBroadcastReceiver = new DDSBroadcastReceiver(); 135 private Random mPortRandom = new Random(); 136 137 private DDS() { 138 } 139 140 /** 141 * 获取DDS实例 142 * 143 * @return DDS DDS实例 144 */ 145 public static synchronized DDS getInstance() { 146 DDS localResource = mInstance; 147 if (localResource == null) { 148 synchronized (DDS.class) { 149 localResource = mInstance; 150 if (localResource == null) { 151 mInstance = localResource = new DDS(); 152 } 153 } 154 } 155 return localResource; 156 } 157 158 /** 159 * 获取DDS SDK的版本号 160 * 161 * @return String 版本号 162 */ 163 public String getVersionName() { 164 AILog.userI(TAG, "getVersionName result =", Version.DDS_SDK_VERSION_NAME); 165 return Version.DDS_SDK_VERSION_NAME; 166 } 167 168 /** 169 * 初始化DDS SDK 170 * 171 * @param context Android context. 172 * @param configs 初始化的配置 173 * @param ddsInitListener 初始化回调方法 174 * @param ddsAuthListener 授权回调方法 175 */ 176 public synchronized void init(final Context context, final DDSConfig configs, final 177 DDSInitListener ddsInitListener, DDSAuthListener ddsAuthListener) { 178 AILog.userI(TAG, "DDSInit config =", configs.toString()); 179 AILog.i(TAG, "====> init. Version: " + getVersionName()); 180 mInitTimeStamp = System.currentTimeMillis(); 181 mContext = context.getApplicationContext(); 182// CrashUtils.init(mContext); 183 184 if (state == STATE_BUSY) { 185 AILog.e(TAG, "already running, ignore."); 186 return; 187 } 188 AuthorizationManager.getInstance().setContext(mContext); 189 AccessTokenManager.getInstance(mContext).clearTasks(); 190 AIUtil.init(mContext); 191 CheckCrashUtil.getInstance().startInitDDS(mContext); 192 boolean useInnerLog = configs.getBooleanConfig("USE_INNER_LOG",true); 193 EngineProxy.createDebug().setUseInnerLog(useInnerLog); 194 EngineProxy.createDebug().init(); 195 196 state = STATE_BUSY; 197 if (workerHandler == null) { 198 workerHandler = BusManager.getInstance().getInnerHandler(); 199 } 200 workerHandler.post(new InitRunnable(mContext, configs.copyDeep(), ddsInitListener, 201 ddsAuthListener)); 202 203 registerBroadcast(mContext); 204 } 205 206 private void registerBroadcast(Context context) { 207 IntentFilter intentFilter = new IntentFilter(); 208 intentFilter.addAction(DebugConstant.Action.INTENT_BUS_ADDRESS_GET); 209 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 210 context.registerReceiver(mDDSBroadcastReceiver, intentFilter,Context.RECEIVER_EXPORTED); 211 } else { 212 context.registerReceiver(mDDSBroadcastReceiver, intentFilter); 213 } 214 } 215 216 /** 217 * 销毁DDS SDK 218 */ 219 220 public synchronized void release() { 221 // release方法调用同步方法, 此方法已降到100ms以内, 可以使用同步方法 222 AILog.userI(TAG, "release"); 223 releaseSync(); 224 } 225 226 /** 227 * 同步销毁 DDS SDK 228 */ 229 public synchronized void releaseSync() { 230 AILog.userI(TAG, "releaseSync"); 231 isAuthComplete = AUTH_COMPLETE_NONE; 232 AILog.i(TAG, "=====> release sync"); 233 if (state == STATE_IDLE) { 234 AILog.e(TAG, "already release, ignore."); 235 return; 236 } 237 try { 238 AccessTokenManager.getInstance(mContext).release(); 239 mContext.unregisterReceiver(mDDSBroadcastReceiver); 240 } catch (Exception e) { 241 AIJavaException.printException(e); 242 } 243 244 245 if (agent != null) { 246 agent.stop(); 247 } 248 if (aiBootloader != null) { 249 aiBootloader.release(); 250 } 251 if (updater != null) { 252 updater.release(); 253 } 254 releaseSyncActions(); 255 AILog.releaseLogThread(); 256 state = STATE_IDLE; 257 mInstance = null; 258 } 259 260 261 /** 262 * 获取Agent 263 * 264 * @return Agent agent实例 265 */ 266 public Agent getAgent() { 267 return agent; 268 } 269 270 /** 271 * 获取DDSUpdater 272 * 273 * @return DDSUpdater DDSUpdater实例 274 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception 275 */ 276 public DDSUpdater getUpdater() throws DDSNotInitCompleteException { 277 checkInitPartComplete(); 278 AILog.userI(TAG, "getUpdater"); 279 return updater; 280 } 281 282 /** 283 * 执行设备授权操作 284 * 异步返回,见 {@link DDSAuthListener} 285 */ 286 public void doAuth() throws DDSNotInitCompleteException { 287 BusClient bc = getBusClient(); 288 AILog.userI(TAG, "doAuth"); 289 if (bc != null) { 290 AuthTopicUtil.publishAuthStart(bc); 291 } else { 292 AILog.e(TAG, "BusClent is null when call doAuth!"); 293 } 294 } 295 296 /** 297 * 返回Profile授权方式下的deviceName信息 298 * 299 * @return deviceName 未授权则该值为空字符串"" 300 * @throws DDSNotInitCompleteException 301 */ 302 public String getDeviceName() throws DDSNotInitCompleteException { 303 checkInitPartComplete(); 304 BusClient bc = getBusClient(); 305 String result = ""; 306 if (bc != null) { 307 result = AuthCallUtil.callAuthDeviceName(bc); 308 } else { 309 AILog.e(TAG, "BusClent is null when call getDeviceName!"); 310 } 311 AILog.userI(TAG, "getDeviceName result =", result); 312 return result; 313 } 314 315 private BusClient getBusClient() { 316 if (getAgent() != null) { 317 return getAgent().getBusClient(); 318 } 319 return null; 320 } 321 322 /** 323 * 授权是否成功,该方法适用于api key授权 324 * 325 * @return boolean true:成功 false:没有成功 326 */ 327 public boolean isAuthSuccess() { 328 boolean ok = isAuthComplete == AUTH_COMPLETE_FULL; 329 AILog.userI(TAG, "isAuthSuccess result =", ok); 330 return ok; 331 } 332 333 /** 334 * 更新accessToken,该方法适用于oauth授权,可以调用oauth sdk获取accessToken 335 * 336 * @param accessToken oauth授权获取到的accessToken 337 * @return boolean true:更新成功 false:更新失败 338 * @throws DDSNotInitCompleteException 此方法已经弃用,oauth授权的最新 api 参见{@link Agent#setAuthCode(com.aispeech.dui.dds.auth.AuthInfo, TokenListener)} 339 */ 340 @Deprecated 341 private boolean updateAccessToken(String accessToken) throws DDSNotInitCompleteException { 342 checkInitPartComplete(); 343 AuthCallUtil.callAuthUpdateAccessToken(getAgent().getBusClient(), accessToken); 344 return true; 345 } 346 347 /** 348 * 初始化是否成功 349 * 350 * @return boolean true:成功 false:没有成功 351 * {@link DDS#getInitStatus() } 352 */ 353 @Deprecated 354 public boolean isInitComplete() { 355 boolean ok = (isInitComplete == INIT_COMPLETE_FULL || isInitComplete == INIT_COMPLETE_NOT_FULL); 356 AILog.userI(TAG, "isInitComplete result =", ok); 357 return ok; 358 } 359 360 361 /** 362 * 获取当前初始化的状态 363 * 364 * @return DDS.INIT_COMPLETE_NONE 还未初始化完成,表示DDS正在初始化 365 * DDS.INIT_COMPLETE_NOT_FULL 部分初始化完成,表示DDS已经初始化完成,但还没有完成更新 366 * DDS.INIT_COMPLETE_FULL 完全初始化完成,已经完成更新 367 */ 368 public int getInitStatus() { 369 switch (isInitComplete) { 370 case INIT_COMPLETE_NOT_FULL: 371 return INIT_COMPLETE_NOT_FULL; 372 case INIT_COMPLETE_FULL: 373 return INIT_COMPLETE_FULL; 374 default: 375 return INIT_COMPLETE_NONE; 376 } 377 } 378 379 380 /** 381 * 设置日志模式,支持在任何时候调用 382 * 383 * @param level 可选值2,3,4,5,6 默认值:4 384 */ 385 public boolean setDebugMode(int level) { 386 if (level <= 0) return false; 387 AILog.userI(TAG, "setDebugMode =", level); 388 AILog.setLogLevel(level); 389 EngineProxy.createDebug().setLogLevel(level); 390 return true; 391 } 392 393 /** 394 * 设置日志模式,支持在任何时候调用 395 * 396 * @param level 可选值2,3,4,5,6 默认值:4 397 */ 398 public boolean setDebugMode(int level, String logCachePath) { 399 if (level <= 0) return false; 400 AILog.userI(TAG, "setDebugMode =", level, ", logCachePath = ", logCachePath); 401 AILog.setLogLevel(level); 402 EngineProxy.createDebug().setLogLevel(level); 403 return true; 404 } 405 406 /** 407 * 设置是否支持音频调试,支持在任何时候调用,打开之后会保存调试音频 408 * 409 * @param enable true/false 410 * @return 411 */ 412 public boolean setAudioDebug(boolean enable) { 413 AILog.userI(TAG, "setAudioDebug =", enable); 414 setAudioDebug(enable, Engines.ALL); 415 return true; 416 } 417 418 /** 419 * 动态修改音频保存路径,调用后以此路径为准 420 * 421 * @param path 支持动态修改音频保存路径,默认为cache目录 422 * @return 423 */ 424 public boolean setAudioSavePath(String path) { 425 AILog.userI(TAG, "setAudioSavePath =", path); 426 EngineProxy.createDebug().setAudioSavePath(path); 427 return true; 428 } 429 430 /** 431 * 设置是否支持音频调试,支持在任何时候调用,打开之后会保存调试音频 432 * @param enable true: 开始保存音频; false: 关闭保存音频; 433 * @param engines 与 enable 有关联,如果enable=true,表示开启指定引擎保存音频;如果enable=false,表示不保存指定引擎音频; 434 * */ 435 public boolean setAudioDebug(boolean enable, int engines) { 436 String mAudioSavePath = EngineProxy.createDebug().getAudioSavePath(); 437 if(TextUtils.isEmpty(mAudioSavePath)) { 438 throw new IllegalStateException("Please call setAudioSavePath before init DDS"); 439 } 440 AILog.userI(TAG, "setAudioDebug =", enable, ", engines =", engines); 441 EngineProxy.createDebug().setAudioDebug(enable, engines); 442 443 if((engines & Engines.DDS_DMS) > 0){ 444 PcmUtil.setEnable(enable); 445 } 446 return true; 447 } 448 449 private void checkInitComplete() throws DDSNotInitCompleteException { 450 if (getInitStatus() != DDS.INIT_COMPLETE_FULL) { 451 throw new DDSNotInitCompleteException(); 452 } 453 } 454 455 private void checkInitPartComplete() throws DDSNotInitCompleteException { 456 if (getInitStatus() == DDS.INIT_COMPLETE_NONE) { 457 throw new DDSNotInitCompleteException(); 458 } 459 } 460 461 private class InitRunnable implements Runnable { 462 463 private DDSInitListener ddsInitListener; 464 private DDSAuthListener ddsAuthListener; 465 private String filesDir; 466 467 private int getPort() { 468 int port = mPortRandom.nextInt(10000) + 50000; 469 470 if (isPortConnectable(port)) { 471 AILog.d(TAG, "端口生成成功"); 472 return port; 473 } else { 474 AILog.d(TAG, "端口被占用--正在尝试重新生成端口"); 475 return getPort(); 476 } 477 } 478 479 private boolean isPortConnectable(int port) { 480 ServerSocket socket = null; 481 try { 482 socket = new ServerSocket(); 483 socket.setReuseAddress(true); 484 socket.bind(new InetSocketAddress("127.0.0.1", port)); 485 } catch (Exception e) { 486 AIJavaException.printException(e); 487 return false; 488 } finally { 489 if (socket != null) { 490 try { 491 socket.close(); 492 AILog.d(TAG, "=====socket关闭"); 493 } catch (Exception e) { 494 AIJavaException.printException(e); 495 } 496 } 497 } 498 return true; 499 } 500 501 private boolean isContainChinese(String str) { 502 Pattern p = Pattern.compile("[\u4e00-\u9fa5]"); 503 Matcher m = p.matcher(str); 504 if (m.find()) { 505 return true; 506 } 507 return false; 508 } 509 510 private void callbackInitListener(int isInitComplete) { 511 boolean isInitCompleteBoo = isInitComplete == INIT_COMPLETE_FULL; 512 if (isInitCompleteBoo) { 513 CheckCrashUtil.getInstance().initDDSSuccess(); 514 } 515 ddsInitListener.onInitComplete(isInitCompleteBoo); 516 } 517 518 private void prepareConfig(DDSConfig configs) { 519 if (TextUtils.equals("none", configs.getConfig(DDSConfig.K_AUTH_TYPE))) { 520 configs.addConfig(DDSConfig.K_ACCESS_TOKEN, "geComesHere"); 521 } 522 configs.addConfig(DDSConfig.K_AUTH_SIGNATURE, AuthUtil.getKeyHash(mContext)); 523 String securityCode = mContext.getPackageName() + ":" + AuthUtil.getKeyHash(mContext); 524 configs.addConfig(DDSConfig.K_SECURITY_CODE, securityCode); 525 String apiKey = configs.getConfig(DDSConfig.K_API_KEY); 526 if (mContext.getPackageName().equals("com.aispeech.dui.dds.demo") 527 && apiKey != null && apiKey.equals("4ff3171a1ef1f122381692fa5a2f52ea")) { 528 //dui dev app 529 configs.addConfig(DDSConfig.K_DEVICE_INFO, AuthUtil.getDeviceData(mContext,false)); 530 } else { 531 configs.addConfig(DDSConfig.K_DEVICE_INFO, AuthUtil.getDeviceData(mContext,true)); 532 } 533 534 if (TextUtils.equals("true", configs.getConfig(DDSConfig.K_ENABLE_DYNAMIC_PORT))) {// 使用动态端口号 535 BUS_SERVER_ADDR = "127.0.0.1:" + getPort(); 536 } 537 configs.addConfig(DDSConfig.K_LBRIDGE_ADDR, BUS_SERVER_ADDR); 538 539 if (configs.containsConfig(DDSConfig.K_INIT_TIMEOUT)) { 540 INIT_TIMEOUT = Integer.valueOf(configs.getConfig(DDSConfig.K_INIT_TIMEOUT)); 541 } 542 AILog.i(TAG, "BUS_SERVER_ADDR is " + BUS_SERVER_ADDR); 543 AILog.i(TAG, "config->" + configs.toString()); 544 } 545 546 public InitRunnable(final Context context, final DDSConfig configs, final 547 DDSInitListener ddsInitListener, DDSAuthListener ddsAuthListener) { 548 this.ddsInitListener = ddsInitListener; 549 this.ddsAuthListener = ddsAuthListener; 550 prepareConfig(configs); 551 agent = new Agent(context); 552 aiBootloader = new AIBootloader(context, configs); 553 updater = new DDSUpdater(context, configs, agent); 554 filesDir = context.getFilesDir().toString(); 555 } 556 557 @Override 558 public void run() { 559 int errId = aiBootloader.start(); 560 if (errId != 0) { 561 AILog.userO(TAG, "ddsInitListener.onError result =", errId); 562 ddsInitListener.onError(errId, bootErrorMsg[errId - 1]); 563 isError = true; 564 } 565 agent.subscribe(new String[]{ 566 Topic.Sys.MSG_SYS_HYBRID_MISSING, 567 Topic.Sys.MSG_SYS_KERNEL_READY, 568 Topic.Auth.AuthFinish.TOPIC_NAME, 569 Topic.Sys.SysTokenInvalid.TOPIC_NAME, 570 Topic.Auth.TokenRefreshFail.TOPIC_NAME, 571 Topic.Auth.TokenRefreshFinish.TOPIC_NAME, 572 Topic.Sys.SysDebug.TOPIC_NAME 573 }, new MessageObserver() { 574 575 @Override 576 public void onMessage(String message, String data) { 577 AILog.d(TAG, "agent: " + message); 578 BusClient bc = getBusClient(); 579 if (message.equals(Topic.Sys.MSG_SYS_HYBRID_MISSING)) { 580 if (bc != null) { 581 bc.removeSticky(message); 582 } 583 isInitComplete = INIT_COMPLETE_NOT_FULL; 584 AILog.userO(TAG, "ddsInitListener.onInitComplete result =", false); 585 callbackInitListener(isInitComplete); 586 } else if (message.equals(Topic.Sys.MSG_SYS_KERNEL_READY)) { 587 VersionUtil.dumpVersion(); 588 589 AILog.d(TAG, "STARTUP.COST " + (System.currentTimeMillis() - mInitTimeStamp)); 590 if (bc != null) { 591 bc.removeSticky(message); 592 } 593 // 初始化成功之后去刷新token 594 String auth_type = LocalKeysUtil.getInstance().getString(LocalKeys.Auth.LOCAL_KEY_AUTH_TYPE, ""); 595 String refreshToken = AuthorizationManager.getInstance().getRefreshToken(); 596 boolean needWaitRefreshToken = PlayerConfig.getInstance().getBooleanConfig(DDSConfig.K_INIT_WAIT_OAUTH_REFRESH_TOKEN); 597 AILog.i(TAG, "kernel.ready auth_type = " + auth_type + ", refreshToken = " + refreshToken + ", needWaitRefreshToken = " + needWaitRefreshToken); 598 if (TextUtils.equals(auth_type, AuthType.AISPEECH_ID) && !TextUtils.isEmpty(refreshToken)) { 599 AILog.d(TAG, "start refreshToken timer..."); 600 String cliendId = LocalKeysUtil.getInstance().getString(LocalKeys.OAuth.LOCAL_AUTH_TYPE_CLIEND_ID, ""); 601 OAuthSdk.initialize(mContext, cliendId, "dds"); 602 AccessTokenManager.getInstance(mContext).checkRefreshToken(true); 603 if (needWaitRefreshToken) { 604 isInitComplete = INIT_COMPLETE_WAIT_REFRESH_TOKEN; 605 AILog.d(TAG, "wait refresh token..."); 606 return; 607 } 608 } 609 610 isInitComplete = INIT_COMPLETE_FULL; 611 AILog.d(TAG, "sys.kernel.ready countdown latch"); 612 AILog.d(TAG, "onInitComplete = true"); 613 AILog.userO(TAG, "ddsInitListener.onInitComplete result =", (isInitComplete == INIT_COMPLETE_FULL)); 614 callbackInitListener(isInitComplete); 615 } else if (message.equals(Topic.Auth.AuthFinish.TOPIC_NAME)) { 616 if (ddsAuthListener == null) { 617 return; 618 } 619 try { 620 JSONObject jsonData = new JSONObject(data); 621 String state = jsonData.optString(Topic.Auth.AuthFinish.STATE); 622 if (TextUtils.equals(state, "success")) { 623 isAuthComplete = AUTH_COMPLETE_FULL; 624 AILog.userO(TAG, "ddsAuthListener.onAuthSuccess"); 625 ddsAuthListener.onAuthSuccess(); 626 } else if (TextUtils.equals(state, "failed")) { 627 String errId = jsonData.optString("errId"); 628 String error = jsonData.optString("error"); 629 StringBuilder builder = new StringBuilder(); 630 builder.append("\n=================================================="); 631 builder.append("\n====================授权失败======================="); 632 builder.append("\n=============apk 版本 -> "); 633 builder.append(AuthUtil.getBuildVariant(mContext)); 634 builder.append("\n============apk SHA256-> "); 635 builder.append(AuthUtil.getKeyHash(mContext)); 636 builder.append("\n============apk packageName -> "); 637 builder.append(mContext.getPackageName()); 638 builder.append("\n============errorId -> "); 639 builder.append(errId); 640 builder.append("\n============errorInfo -> "); 641 builder.append(error); 642 builder.append("\n=================================================="); 643 AILog.e(TAG, builder.toString()); 644 isAuthComplete = AUTH_COMPLETE_NONE; 645 ddsAuthListener.onAuthFailed(errId, error); 646 AILog.userO(TAG, "ddsAuthListener.onAuthFailed result =", errId); 647 } 648 } catch (JSONException e) { 649 AIJavaException.printException(e); 650 } 651 } else if (message.equals(Topic.Sys.SysTokenInvalid.TOPIC_NAME)) { 652 AILog.e(TAG, message + ", will call refreshForInit"); 653 AccessTokenManager.getInstance(mContext).refreshForInit(); 654 if (ddsAuthListener == null) { 655 AILog.e(TAG, "ddsAuthListener is null return"); 656 return; 657 } 658 isAuthComplete = AUTH_COMPLETE_NONE; 659 AILog.userO(TAG, "ddsAuthListener.onAuthFailed result = 070611, access token is invalid"); 660 ddsAuthListener.onAuthFailed("070611", "access token is invalid"); 661 } else if (message.equals(Topic.Auth.TokenRefreshFinish.TOPIC_NAME)) { 662 AILog.d(TAG, "token_refresh.finish isInitComplete = " + isInitComplete); 663 if (isInitComplete == INIT_COMPLETE_WAIT_REFRESH_TOKEN) { 664 isInitComplete = INIT_COMPLETE_FULL; 665 AILog.d(TAG, "token_refresh.finish sys.kernel.ready countdown latch"); 666 AILog.d(TAG, "token_refresh.finish onInitComplete = true"); 667 AILog.userO(TAG, "ddsInitListener.onInitComplete token_refresh.finish result =", (isInitComplete == INIT_COMPLETE_FULL)); 668 callbackInitListener(isInitComplete); 669 } 670 } else if (message.equals(Topic.Auth.TokenRefreshFail.TOPIC_NAME)) { 671 try { 672 AILog.d(TAG, "token_refresh.fail isInitComplete = " + isInitComplete); 673 JSONObject jsonData = new JSONObject(data); 674 int errId = jsonData.optInt(Topic.Auth.TokenRefreshFail.ERR_ID); 675 AILog.d(TAG, "刷新Token失败 errId = " + errId); 676 if (errId == 70624) { 677 if (ddsAuthListener != null) { 678 isAuthComplete = AUTH_COMPLETE_NONE; 679 AILog.userO(TAG, "ddsAuthListener.onAuthFailed result = 070611, access token is invalid"); 680 ddsAuthListener.onAuthFailed("070611", "access token is invalid"); 681 } 682 } 683 if (isInitComplete == INIT_COMPLETE_WAIT_REFRESH_TOKEN) { 684 isInitComplete = INIT_COMPLETE_FULL; 685 AILog.d(TAG, "token_refresh.fail onInitComplete = true"); 686 callbackInitListener(isInitComplete); 687 } 688 if (errId == 70624) { 689 AILog.d(TAG, "刷新Token失败,此Token不可用,需要重新绑定,clearAuthCode。。。"); 690 agent.clearAuthCode(); 691 } 692 } catch (Exception e) { 693 e.printStackTrace(); 694 } 695 } else if (TextUtils.equals(message, Topic.Sys.SysDebug.TOPIC_NAME)) { 696 if (bc != null) { 697 bc.removeSticky(message); 698 } 699 parseDebugInfo(data); 700 } 701 } 702 }); 703 agent.start(); 704 workerHandler.postDelayed(new Runnable() { 705 @Override 706 public void run() { 707 if(getInitStatus() == INIT_COMPLETE_NONE) { 708 ddsInitListener.onError(ERROR_KERNEL_READY_TIMEOUT, "初始化超时"); 709 } 710 } 711 },INIT_TIMEOUT * 1000L); 712 AILog.d(TAG,"start init..."); 713 } 714 } 715 716 717 private void parseDebugInfo(String data) { 718 AILog.e(TAG, "debug date is -> " + data); 719 DebugBean debugBean = (DebugBean) AIGsonUtil.getInstance().jsonToBean(data, DebugBean.class); 720 if (debugBean == null) return; 721 AILog.e(TAG, "debugBean is -> " + debugBean.toString()); 722 int debugType = debugBean.getDebugType(); 723 switch (debugType) { 724 case DebugBean.TYPE_LOG_DEBUG_START: 725 setDebugMode(2); 726 break; 727 case DebugBean.TYPE_AUDIO_DEBUG_START: 728 setAudioDebug(true); 729 break; 730 case DebugBean.TYPE_DEBUG_STOP: 731 stopDebug(); 732 break; 733 default: 734 } 735 } 736 737 private class ReleaseRunnable implements Runnable { 738 @Override 739 public void run() { 740 isInitComplete = INIT_COMPLETE_NONE; 741 DaemonUtil.getInstance().release(); 742 isError = false; 743 744 AILog.w(TAG, "dds shutdown"); 745 } 746 } 747 748 private void releaseSyncActions() { 749 isInitComplete = INIT_COMPLETE_NONE; 750 DaemonUtil.getInstance().release(); 751 isError = false; 752 AILog.w(TAG, "dds shutdown"); 753 } 754 755 private void resetExitFlag(String dir) { 756 try { 757 FileUtils.writeFile(String.valueOf(System.currentTimeMillis()), dir); 758 } catch (IOException e) { 759 AIJavaException.printException(e); 760 } 761 } 762 763 /** 764 * 设置DDS异常监听器,如果DDS有不可逆的异常会通过此接口抛出,客户收到此异常之后可以消毁并重启DDS 765 * 766 * @param listener 767 */ 768 public void setDDSErrorListener(final DDSErrorListener listener) { 769// AIJavaException.setExceptionCallback(new AIJavaException.ExceptionCallback() { 770// @Override 771// public void onException(String e) { 772// if (listener != null) { 773// listener.onError(404, JSONObjectUtil.create() 774// .put("error", e) 775// .build()); 776// } 777// } 778// }); 779 } 780 781 /** 782 * 设置录音机音频反转监听器 783 * 784 * @param listener 785 */ 786 public boolean setReverseRecorderDataListener(ReverseRecorderDataListener listener) { 787 AILog.userI(TAG, "setReverseRecorderDataListener"); 788 if (aiBootloader != null && aiBootloader.mRecorderNode != null) { 789 return aiBootloader.mRecorderNode.setReverseRecorderDataListener(listener); 790 } 791 return false; 792 } 793 794 /** 795 * 开启调试模式: 796 * 1. 日志级别调为V 797 * 2. 保存调试音频 798 */ 799 public void startDebug() { 800 AILog.userI(TAG, "startDebug"); 801 setDebugMode(2); 802 setAudioDebug(true); 803 } 804 805 /** 806 * 关闭调试模式: 807 * 1. 日志级别修改为E 808 * 2. 停止保存调试音频 809 */ 810 public void stopDebug() { 811 AILog.userI(TAG, "stopDebug"); 812 setDebugMode(6); 813 setAudioDebug(false); 814 } 815 816 public void addCustomNode(BaseNode node) { 817 AILog.userI(TAG, "addCustomNode node =", (node != null ? node.getName() : "null")); 818 DaemonUtil.getInstance().addNode(node); 819 } 820 821 /** 822 * 用户设置自己实现的单个功能,目前支持 wakeup 和 vad 823 * <p> 824 * 必须在 {@link #init } 方法前调用本方法 825 * </p> 826 * <ul> 827 * <li>IEngine.Name.WAKEUP_SINGLE_MIC 实现接口 {@link com.aispeech.libcomm.abslite.IWakeupEngine}</li> 828 * <li>IEngine.Name.WAKEUP_MULTIPLE_MIC 实现接口 {@link com.aispeech.libcomm.abslite.IWakeupEngine}</li> 829 * <li>IEngine.Name.WAKEUP_INCREMENT 实现接口 {@link com.aispeech.libcomm.abslite.IWakeupEngine}</li> 830 * <li>IEngine.Name.VAD 实现接口 {@link com.aispeech.libcomm.abslite.IVadEngine}</li> 831 * </ul> 832 * 833 * @param name 名称 834 * @param engineClazz 对应的实现类, 为 null 时表示将之前的设置取消 835 * @return true 设置成功,false engineClazz 实现的类不对 836 */ 837 public boolean setOutsideEngine(IEngine.Name name, Class<? extends IEngine> engineClazz) { 838 return EngineProxy.setOutsideEngine(name, engineClazz); 839 } 840 841 842 /** 843 * 添加dds拦截器 844 * 845 * @param iInterceptor 846 */ 847 public void addInterceptor(IInterceptor iInterceptor) { 848 AILog.userI(TAG, "addInterceptor iInterceptor =", (iInterceptor != null ? iInterceptor.getName() : "null")); 849 DDSInterceptor.getInstance().addInterceptor(iInterceptor); 850 } 851}