/* * Tencent is pleased to support the open source community by making Tinker available. * * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package com.dl7.tinkerlib.crash; import android.content.Context; import android.content.SharedPreferences; import android.os.SystemClock; import com.dl7.tinkerlib.reporter.SampleTinkerReport; import com.dl7.tinkerlib.util.TinkerManager; import com.dl7.tinkerlib.util.Utils; import com.tencent.tinker.lib.tinker.TinkerApplicationHelper; import com.tencent.tinker.lib.util.TinkerLog; import com.tencent.tinker.loader.app.ApplicationLike; import com.tencent.tinker.loader.shareutil.ShareConstants; import com.tencent.tinker.loader.shareutil.ShareTinkerInternals; /** * optional, use dynamic configuration is better way * for native crash, * <p/> * Created by zhangshaowen on 16/7/3. * tinker's crash is caught by {@code LoadReporter.onLoadException} * use {@code TinkerApplicationHelper} api, no need to install tinker! */ public class SampleUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final String TAG = "Tinker.SampleUncaughtExHandler"; private final Thread.UncaughtExceptionHandler ueh; private static final long QUICK_CRASH_ELAPSE = 10 * 1000; public static final int MAX_CRASH_COUNT = 3; private static final String DALVIK_XPOSED_CRASH = "Class ref in pre-verified class resolved to unexpected implementation"; public SampleUncaughtExceptionHandler() { ueh = Thread.getDefaultUncaughtExceptionHandler(); } @Override public void uncaughtException(Thread thread, Throwable ex) { TinkerLog.e(TAG, "uncaughtException:" + ex.getMessage()); tinkerFastCrashProtect(); tinkerPreVerifiedCrashHandler(ex); ueh.uncaughtException(thread, ex); } /** * Such as Xposed, if it try to load some class before we load from patch files. * With dalvik, it will crash with "Class ref in pre-verified class resolved to unexpected implementation". * With art, it may crash at some times. But we can't know the actual crash type. * If it use Xposed, we can just clean patch or mention user to uninstall it. */ private void tinkerPreVerifiedCrashHandler(Throwable ex) { Throwable throwable = ex; boolean isXposed = false; while (throwable != null) { if (!isXposed) { isXposed = Utils.isXposedExists(throwable); } if (isXposed) { //method 1 ApplicationLike applicationLike = TinkerManager.getTinkerApplicationLike(); if (applicationLike == null || applicationLike.getApplication() == null) { return; } if (!TinkerApplicationHelper.isTinkerLoadSuccess(applicationLike)) { return; } boolean isCausedByXposed = false; //for art, we can't know the actually crash type //just ignore art if (throwable instanceof IllegalAccessError && throwable.getMessage().contains(DALVIK_XPOSED_CRASH)) { //for dalvik, we know the actual crash type isCausedByXposed = true; } if (isCausedByXposed) { SampleTinkerReport.onXposedCrash(); TinkerLog.e(TAG, "have xposed: just clean tinker"); //kill all other process to ensure that all process's code is the same. ShareTinkerInternals.killAllOtherProcess(applicationLike.getApplication()); TinkerApplicationHelper.cleanPatch(applicationLike); ShareTinkerInternals.setTinkerDisableWithSharedPreferences(applicationLike.getApplication()); return; } } throwable = throwable.getCause(); } } /** * if tinker is load, and it crash more than MAX_CRASH_COUNT, then we just clean patch. */ private boolean tinkerFastCrashProtect() { ApplicationLike applicationLike = TinkerManager.getTinkerApplicationLike(); if (applicationLike == null || applicationLike.getApplication() == null) { return false; } if (!TinkerApplicationHelper.isTinkerLoadSuccess(applicationLike)) { return false; } final long elapsedTime = SystemClock.elapsedRealtime() - applicationLike.getApplicationStartElapsedTime(); //this process may not install tinker, so we use TinkerApplicationHelper api if (elapsedTime < QUICK_CRASH_ELAPSE) { String currentVersion = TinkerApplicationHelper.getCurrentVersion(applicationLike); if (ShareTinkerInternals.isNullOrNil(currentVersion)) { return false; } SharedPreferences sp = applicationLike.getApplication().getSharedPreferences(ShareConstants.TINKER_SHARE_PREFERENCE_CONFIG, Context.MODE_MULTI_PROCESS); int fastCrashCount = sp.getInt(currentVersion, 0) + 1; if (fastCrashCount >= MAX_CRASH_COUNT) { SampleTinkerReport.onFastCrashProtect(); TinkerApplicationHelper.cleanPatch(applicationLike); TinkerLog.e(TAG, "tinker has fast crash more than %d, we just clean patch!", fastCrashCount); return true; } else { sp.edit().putInt(currentVersion, fastCrashCount).commit(); TinkerLog.e(TAG, "tinker has fast crash %d times", fastCrashCount); } } return false; } }