123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- package com.makeit.callservice.tts.util;
- import android.Manifest;
- import android.content.Context;
- import android.content.pm.PackageManager;
- import android.os.Handler;
- import android.os.Message;
- import androidx.core.content.ContextCompat;
- import com.baidu.tts.client.SpeechSynthesizer;
- import com.baidu.tts.client.TtsMode;
- import com.makeit.callservice.tts.control.InitConfig;
- import org.json.JSONObject;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import java.net.UnknownHostException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.LinkedHashMap;
- import java.util.Map;
- import java.util.TreeSet;
- /**
- * Created by fujiayi on 2017/12/28.
- */
- /**
- * 自动排查工具,用于集成后发现错误。
- * <p>
- * 可以检测如下错误:
- * 1. PermissionCheck : AndroidManifest,xml 需要的部分权限
- * 2. JniCheck: 检测so文件是否安装在指定目录
- * 3. AppInfoCheck: 联网情况下 , 检测appId appKey secretKey是否正确
- * 4. ApplicationIdCheck: 显示包名applicationId, 提示用户手动去官网检查
- * 5. ParamKeyExistCheck: 检查key是否存在,目前检查 SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE
- * 和PARAM_TTS_SPEECH_MODEL_FILE
- * 6. OfflineResourceFileCheck 检查离线资源文件(需要从assets目录下复制),是否存在
- * <p>
- * <p>
- * 示例使用代码:
- * AutoCheck.getInstance(getApplicationContext()).check(initConfig, new Handler() {
- *
- * @Override public void handleMessage(Message msg) {
- * if (msg.what == 100) {
- * AutoCheck autoCheck = (AutoCheck) msg.obj;
- * synchronized (autoCheck) {
- * String message = autoCheck.obtainDebugMessage();
- * toPrint(message); // 可以用下面一行替代,在logcat中查看代码
- * //Log.w("AutoCheckMessage",message);
- * }
- * }
- * }
- * <p>
- * });
- */
- public class AutoCheck {
- private static AutoCheck instance;
- private LinkedHashMap<String, Check> checks;
- private static Context context;
- private boolean hasError = false;
- volatile boolean isFinished = false;
- /**
- * 获取实例,非线程安全
- *
- * @return
- */
- public static AutoCheck getInstance(Context context) {
- if (instance == null || AutoCheck.context != context) {
- instance = new AutoCheck(context);
- }
- return instance;
- }
- public void check(final InitConfig initConfig, final Handler handler) {
- Thread t = new Thread(new Runnable() {
- @Override
- public void run() {
- AutoCheck obj = innerCheck(initConfig);
- isFinished = true;
- synchronized (obj) { // 偶发,同步线程信息
- Message msg = handler.obtainMessage(100, obj);
- handler.sendMessage(msg);
- }
- }
- });
- t.start();
- }
- private AutoCheck innerCheck(InitConfig config) {
- checks.put("检查申请的Android权限", new PermissionCheck(context));
- checks.put("检查4个so文件是否存在", new JniCheck(context));
- checks.put("检查AppId AppKey SecretKey",
- new AppInfoCheck(config.getAppId(), config.getAppKey(), config.getSecretKey()));
- checks.put("检查包名", new ApplicationIdCheck(context, config.getAppId()));
- if (TtsMode.MIX.equals(config.getTtsMode())) {
- Map<String, String> params = config.getParams();
- String fileKey = SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE;
- checks.put("检查离线资TEXT文件参数", new ParamKeyExistCheck(params, fileKey,
- "SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE未设置 ,"));
- checks.put("检查离线资源TEXT文件", new OfflineResourceFileCheck(params.get(fileKey)));
- fileKey = SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE;
- checks.put("检查离线资Speech文件参数", new ParamKeyExistCheck(params, fileKey,
- "SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE未设置 ,"));
- checks.put("检查离线资源Speech文件", new OfflineResourceFileCheck(params.get(fileKey)));
- }
- for (Map.Entry<String, Check> e : checks.entrySet()) {
- Check check = e.getValue();
- check.check();
- if (check.hasError()) {
- break;
- }
- }
- return this;
- }
- public String obtainErrorMessage() {
- PrintConfig config = new PrintConfig();
- return formatString(config);
- }
- public String obtainDebugMessage() {
- PrintConfig config = new PrintConfig();
- config.withInfo = true;
- return formatString(config);
- }
- public String obtainAllMessage() {
- PrintConfig config = new PrintConfig();
- config.withLog = true;
- config.withInfo = true;
- return formatString(config);
- }
- public String formatString(PrintConfig config) {
- StringBuilder sb = new StringBuilder();
- hasError = false;
- for (HashMap.Entry<String, Check> entry : checks.entrySet()) {
- Check check = entry.getValue();
- String testName = entry.getKey();
- if (check.hasError()) {
- if (!hasError) {
- hasError = true;
- }
- sb.append("【错误】【").append(testName).append(" 】 ").append(check.getErrorMessage()).append("\n");
- if (check.hasFix()) {
- sb.append("【修复方法】【").append(testName).append(" 】 ").append(check.getFixMessage()).append("\n");
- }
- }
- if (config.withInfo && check.hasInfo()) {
- sb.append("【请手动检查】【").append(testName).append("】 ").append(check.getInfoMessage()).append("\n");
- }
- if (config.withLog && (config.withLogOnSuccess || hasError) && check.hasLog()) {
- sb.append("【log】:" + check.getLogMessage()).append("\n");
- }
- }
- if (!hasError) {
- sb.append("集成自动排查工具: 恭喜没有检测到任何问题\n");
- }
- return sb.toString();
- }
- public void clear() {
- checks.clear();
- hasError = false;
- }
- private AutoCheck(Context context) {
- this.context = context;
- checks = new LinkedHashMap<String, Check>();
- }
- private static class PrintConfig {
- public boolean withFix = true;
- public boolean withInfo = false;
- public boolean withLog = false;
- public boolean withLogOnSuccess = false;
- }
- private static class PermissionCheck extends Check {
- private Context context;
- public PermissionCheck(Context context) {
- this.context = context;
- }
- @Override
- public void check() {
- String[] permissions = {
- Manifest.permission.INTERNET,
- Manifest.permission.ACCESS_NETWORK_STATE,
- Manifest.permission.MODIFY_AUDIO_SETTINGS,
- // Manifest.permission.WRITE_EXTERNAL_STORAGE,
- // Manifest.permission.WRITE_SETTINGS,
- Manifest.permission.READ_PHONE_STATE,
- Manifest.permission.ACCESS_WIFI_STATE,
- // Manifest.permission.CHANGE_WIFI_STATE
- };
- ArrayList<String> toApplyList = new ArrayList<String>();
- for (String perm : permissions) {
- if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
- toApplyList.add(perm);
- // 进入到这里代表没有权限.
- }
- }
- if (!toApplyList.isEmpty()) {
- errorMessage = "缺少权限:" + toApplyList;
- fixMessage = "请从AndroidManifest.xml复制相关权限";
- }
- }
- }
- private static class JniCheck extends Check {
- private Context context;
- private String[] soNames;
- public JniCheck(Context context) {
- this.context = context;
- soNames = new String[]{"libbd_etts.so", "libBDSpeechDecoder_V1.so", "libgnustl_shared.so"};
- }
- @Override
- public void check() {
- String path = context.getApplicationInfo().nativeLibraryDir;
- appendLogMessage("Jni so文件目录 " + path);
- File[] files = new File(path).listFiles();
- TreeSet<String> set = new TreeSet<>();
- if (files != null) {
- for (File file : files) {
- if (file.canRead()) {
- set.add(file.getName());
- }
- }
- }
- appendLogMessage("Jni目录内文件: " + set.toString());
- for (String name : soNames) {
- if (!set.contains(name)) {
- errorMessage = "Jni目录" + path + " 缺少可读的so文件:" + name + ", 该目录文件列表: " + set.toString();
- fixMessage = "如果您的app内没有其它so文件,请复制demo里的src/main/jniLibs至同名目录。"
- + " 如果app内有so文件,请合并目录放一起(注意目录取交集,多余的目录删除)。";
- break;
- }
- }
- }
- }
- private static class ParamKeyExistCheck extends Check {
- private Map<String, String> params;
- private String key;
- private String prefixErrorMessage;
- public ParamKeyExistCheck(Map<String, String> params, String key, String prefixErrorMessage) {
- this.params = params;
- this.key = key;
- this.prefixErrorMessage = prefixErrorMessage;
- }
- @Override
- public void check() {
- if (params == null || !params.containsKey(key)) {
- errorMessage = prefixErrorMessage + " 参数中没有设置:" + key;
- fixMessage = "请参照demo在设置 " + key + "参数";
- }
- }
- }
- private static class OfflineResourceFileCheck extends Check {
- private String filename;
- private String nullMessage;
- public OfflineResourceFileCheck(String filename) {
- this.filename = filename;
- this.nullMessage = nullMessage;
- }
- @Override
- public void check() {
- File file = new File(filename);
- boolean isSuccess = true;
- if (!file.exists()) {
- errorMessage = "资源文件不存在:" + filename;
- isSuccess = false;
- } else if (!file.canRead()) {
- errorMessage = "资源文件不可读:" + filename;
- isSuccess = false;
- }
- if (!isSuccess) {
- fixMessage = "请将demo中src/main/assets目录下同名文件复制到 " + filename;
- }
- }
- }
- private static class ApplicationIdCheck extends Check {
- private String appId;
- private Context context;
- public ApplicationIdCheck(Context context, String appId) {
- this.appId = appId;
- this.context = context;
- }
- @Override
- public void check() {
- infoMessage = "如果您集成过程中遇见离线合成初始化问题,请检查网页上appId:" + appId
- + " 应用是否开通了合成服务,并且网页上的应用填写了Android包名:"
- + getApplicationId();
- }
- private String getApplicationId() {
- return context.getPackageName();
- }
- }
- private static class AppInfoCheck extends Check {
- private String appId;
- private String appKey;
- private String secretKey;
- public AppInfoCheck(String appId, String appKey, String secretKey) {
- this.appId = appId;
- this.appKey = appKey;
- this.secretKey = secretKey;
- }
- @Override
- public void check() {
- do {
- appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
- if (appId == null || appId.isEmpty()) {
- errorMessage = "appId 为空";
- fixMessage = "填写appID";
- break;
- }
- if (appKey == null || appKey.isEmpty()) {
- errorMessage = "appKey 为空";
- fixMessage = "填写appID";
- break;
- }
- if (secretKey == null || secretKey.isEmpty()) {
- errorMessage = "secretKey 为空";
- fixMessage = "secretKey";
- break;
- }
- } while (false);
- try {
- checkOnline();
- } catch (UnknownHostException e) {
- infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
- } catch (Exception e) {
- errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
- fixMessage = " 重新检测appId, appKey, appSecret是否正确";
- }
- }
- public void checkOnline() throws Exception {
- String urlpath = "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id="
- + appKey + "&client_secret=" + secretKey;
- URL url = new URL(urlpath);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("GET");
- conn.setConnectTimeout(1000);
- InputStream is = conn.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(is));
- StringBuilder result = new StringBuilder();
- String line = "";
- do {
- line = reader.readLine();
- if (line != null) {
- result.append(line);
- }
- } while (line != null);
- String res = result.toString();
- appendLogMessage("openapi return " + res);
- JSONObject jsonObject = new JSONObject(res);
- String error = jsonObject.optString("error");
- if (error != null && !error.isEmpty()) {
- throw new Exception("appkey secretKey 错误" + ", error:" + error + ", json is" + result);
- }
- String token = jsonObject.getString("access_token");
- if (token == null || !token.endsWith("-" + appId)) {
- throw new Exception("appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token);
- }
- }
- }
- private abstract static class Check {
- protected String errorMessage = null;
- protected String fixMessage = null;
- protected String infoMessage = null;
- protected StringBuilder logMessage;
- public Check() {
- logMessage = new StringBuilder();
- }
- public abstract void check();
- public boolean hasError() {
- return errorMessage != null;
- }
- public boolean hasFix() {
- return fixMessage != null;
- }
- public boolean hasInfo() {
- return infoMessage != null;
- }
- public boolean hasLog() {
- return !logMessage.toString().isEmpty();
- }
- public void appendLogMessage(String message) {
- logMessage.append(message + "\n");
- }
- public String getErrorMessage() {
- return errorMessage;
- }
- public String getFixMessage() {
- return fixMessage;
- }
- public String getInfoMessage() {
- return infoMessage;
- }
- public String getLogMessage() {
- return logMessage.toString();
- }
- }
- }
|