123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- package com.norman.webviewup.lib;
- import android.content.Context;
- import android.content.SharedPreferences;
- import android.content.pm.ApplicationInfo;
- import android.content.pm.PackageInfo;
- import android.os.Build;
- import android.os.Handler;
- import android.os.HandlerThread;
- import android.os.IBinder;
- import android.os.IInterface;
- import android.os.Looper;
- import android.webkit.WebView;
- import com.norman.webviewup.lib.download.DownloadAction;
- import com.norman.webviewup.lib.download.DownloaderSink;
- import com.norman.webviewup.lib.hook.PackageManagerHook;
- import com.norman.webviewup.lib.hook.WebViewUpdateServiceHook;
- import com.norman.webviewup.lib.reflect.RuntimeAccess;
- import com.norman.webviewup.lib.service.interfaces.IServiceManager;
- import com.norman.webviewup.lib.service.interfaces.IWebViewFactory;
- import com.norman.webviewup.lib.service.interfaces.IWebViewUpdateService;
- import com.norman.webviewup.lib.util.ApkUtils;
- import java.io.File;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Objects;
- import java.util.concurrent.atomic.AtomicBoolean;
- import java.util.concurrent.atomic.AtomicReference;
- public class WebViewUpgrade {
- private static final List<UpgradeCallback> UPGRADE_CALLBACK_LIST = new ArrayList<>();
- private static final String UPGRADE_DIRECTORY = "WebViewUpgrade";
- private static final int STATUS_UNINIT = 0;
- private static final int STATUS_RUNNING = 1;
- private static final int STATUS_FAIL = 2;
- private static final int STATUS_COMPLETE = 3;
- private static UpgradeOptions UPGRADE_OPTIONS;
- private static int UPGRADE_STATUS = STATUS_UNINIT;
- private static float UPGRADE_PROCESS;
- private static String SYSTEM_WEB_VIEW_PACKAGE_NAME;
- private static String SYSTEM_WEB_VIEW_PACKAGE_VERSION;
- private static Throwable UPGRADE_THROWABLE;
- private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
- public synchronized static void addUpgradeCallback(UpgradeCallback upgradeCallback) {
- if (upgradeCallback == null) return;
- if (UPGRADE_CALLBACK_LIST.contains(upgradeCallback)) return;
- UPGRADE_CALLBACK_LIST.add(upgradeCallback);
- }
- public synchronized static void removeUpgradeCallback(UpgradeCallback upgradeCallback) {
- if (upgradeCallback == null) return;
- if (!UPGRADE_CALLBACK_LIST.contains(upgradeCallback)) return;
- UPGRADE_CALLBACK_LIST.remove(upgradeCallback);
- }
- public synchronized static boolean isProcessing() {
- return UPGRADE_STATUS == STATUS_RUNNING;
- }
- public synchronized static boolean isCompleted() {
- return UPGRADE_STATUS == STATUS_COMPLETE;
- }
- public synchronized static boolean isFailed() {
- return UPGRADE_STATUS == STATUS_FAIL;
- }
- public synchronized static boolean isInited() {
- return UPGRADE_STATUS != STATUS_UNINIT;
- }
- public synchronized static Throwable getUpgradeError(){
- return UPGRADE_THROWABLE;
- }
- public synchronized static float getUpgradeProcess(){
- return UPGRADE_PROCESS;
- }
- public synchronized static void upgrade(UpgradeOptions options) {
- try {
- if (UPGRADE_STATUS == STATUS_RUNNING ||UPGRADE_STATUS == STATUS_COMPLETE ) {
- return;
- }
- UPGRADE_OPTIONS = options;
- UPGRADE_STATUS = STATUS_RUNNING;
- UPGRADE_THROWABLE = null;
- HandlerThread upgradeThread = new HandlerThread("WebViewUpgrade");
- upgradeThread.start();
- Handler upgradeHandler = new Handler(upgradeThread.getLooper());
- upgradeHandler.post(new UPGRADE_ACTION(upgradeHandler));
- } catch (Throwable throwable) {
- callErrorCallback(throwable);
- }
- }
- static class UPGRADE_ACTION implements Runnable {
- private final Handler handler;
- private Context context;
- private String apkUrl;
- private String packageName;
- private String versionName;
- private String apkPath;
- private String soLibDir;
- private String soLibInstallCompleteKey;
- private SharedPreferences sharedPreferences;
- public UPGRADE_ACTION(Handler handler) {
- this.handler = handler;
- }
- @Override
- public void run() {
- try {
- UpgradeOptions options = UPGRADE_OPTIONS;
- DownloaderSink downloaderSink = options.downloaderSink;
- context = options.context;
- apkUrl = options.url;
- packageName = options.packageName;
- versionName = options.versionName;
- apkPath = new File(context.getFilesDir(),
- UPGRADE_DIRECTORY
- + "/" + packageName
- + "/" + versionName
- + "/base.apk").getAbsolutePath();
- soLibDir = new File(context.getFilesDir(),
- UPGRADE_DIRECTORY
- + "/" + packageName
- + "/" + versionName
- + "/libs").getAbsolutePath();
- sharedPreferences = context
- .getSharedPreferences(
- UPGRADE_DIRECTORY,
- Context.MODE_PRIVATE);
- soLibInstallCompleteKey = packageName + ":" + versionName;
- File soDir = new File(soLibDir);
- if (!soDir.exists()) {
- soDir.mkdirs();
- }
- boolean installComplete = sharedPreferences
- .getBoolean(soLibInstallCompleteKey, false);
- if (installComplete) {
- upgradeWebView();
- } else {
- DownloadAction downloadAction = downloaderSink.createDownload(apkUrl, apkPath);
- if (downloadAction.isCompleted()) {
- extractNativeLibrary();
- upgradeWebView();
- } else {
- downloadAction.addCallback(new DownloadAction.Callback() {
- @Override
- public void onComplete(String path) {
- handler.post(() -> {
- try {
- extractNativeLibrary();
- upgradeWebView();
- }catch (Throwable throwable){
- callErrorCallback(throwable);
- handler.getLooper().quit();
- }
- });
- }
- @Override
- public void onFail(Throwable throwable) {
- callErrorCallback(throwable);
- handler.getLooper().quit();
- }
- @Override
- public void onProcess(float percent) {
- callProcessCallback(percent * 0.90f);
- }
- });
- }
- downloadAction.start();
- }
- } catch (Throwable throwable) {
- callErrorCallback(throwable);
- handler.getLooper().quit();
- }
- }
- private void extractNativeLibrary() {
- callProcessCallback(0.92f);
- ApkUtils.extractNativeLibrary(apkPath, soLibDir);
- callProcessCallback(0.94f);
- sharedPreferences
- .edit()
- .putBoolean(soLibInstallCompleteKey, true)
- .commit();
- }
- private void upgradeWebView() {
- callProcessCallback(0.95f);
- replaceWebViewProvider(context,
- packageName,
- versionName,
- apkPath,
- soLibDir);
- handler.getLooper().quit();
- }
- }
- private static void replaceWebViewProvider(Context context,
- String packageName,
- String versionName,
- String apkPath,
- String soLibDir) {
- PackageManagerHook managerHook = null;
- WebViewUpdateServiceHook updateServiceHook = null;
- try {
- callProcessCallback(0.96f);
- PackageInfo packageInfo = context.getPackageManager()
- .getPackageArchiveInfo(apkPath, 0);
- if (packageInfo == null) {
- throw new NullPointerException("path: " + apkPath + " is not apk");
- }
- if (!Objects.equals(packageInfo.packageName, packageName)) {
- throw new IllegalArgumentException("packageName:"
- + packageInfo.packageName
- + " in the options is different from packageName:"
- + packageName + " in the apk");
- }
- if (!Objects.equals(packageInfo.versionName, versionName)) {
- throw new IllegalArgumentException("versionName:"
- + packageInfo.versionName
- + " in the options is different from versionName:"
- + versionName + " in the apk");
- }
- int sdkVersion = Build.VERSION.SDK_INT;
- ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (sdkVersion < applicationInfo.minSdkVersion) {
- throw new RuntimeException("The current system version " + sdkVersion + " is smaller than the minimum version " + applicationInfo.minSdkVersion + "required by the apk " + apkPath);
- }
- }
- checkWebView();
- managerHook = new PackageManagerHook(context, packageName, apkPath, soLibDir);
- updateServiceHook = new WebViewUpdateServiceHook(context, packageName);
- managerHook.hook();
- callProcessCallback(0.97f);
- updateServiceHook.hook();
- callProcessCallback(0.98f);
- Object lock = new Object();
- AtomicBoolean loadOver = new AtomicBoolean();
- AtomicReference<Throwable> throwableReference = new AtomicReference<>(null);
- MAIN_HANDLER.post(() -> {
- try {
- loadSystemWebViewPackage();
- checkWebView();
- new WebView(context);
- } catch (Throwable throwable) {
- throwableReference.set(throwable);
- } finally {
- synchronized (lock) {
- loadOver.set(true);
- lock.notifyAll();
- }
- }
- });
- synchronized (lock) {
- long startTime = System.currentTimeMillis();
- while (!loadOver.get()) {
- try {
- lock.wait(100);
- } catch (InterruptedException ignore) {
- }
- if ((System.currentTimeMillis() - startTime) > 5000) {
- throwableReference.set(new RuntimeException("webView load timeOut"));
- break;
- }
- }
- }
- Throwable throwable = throwableReference.get();
- if (throwable != null) {
- throw new RuntimeException(throwable);
- }
- callProcessCallback(1.0f);
- callCompleteCallback();
- } finally {
- if (managerHook != null) {
- managerHook.restore();
- }
- if (updateServiceHook != null) {
- updateServiceHook.restore();
- }
- }
- }
- private static void checkWebView() {
- IWebViewFactory webViewFactory = RuntimeAccess.staticAccess(IWebViewFactory.class);
- Object providerInstance = webViewFactory.getProviderInstance();
- if (providerInstance != null) {
- throw new IllegalStateException("WebViewProvider has been created, and the upgrade function can only be used before the webview is instantiated");
- }
- }
- private static void callErrorCallback(Throwable throwable) {
- synchronized (WebViewUpgrade.class) {
- UPGRADE_STATUS = STATUS_FAIL;
- UPGRADE_THROWABLE = throwable;
- }
- runInMainThread(() -> {
- synchronized (WebViewUpgrade.class) {
- for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_LIST) {
- upgradeCallback.onUpgradeError(throwable);
- }
- }
- });
- }
- private static void callProcessCallback(float percent) {
- synchronized (WebViewUpgrade.class){
- UPGRADE_PROCESS = percent;
- }
- runInMainThread(() -> {
- synchronized (WebViewUpgrade.class) {
- for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_LIST) {
- upgradeCallback.onUpgradeProcess(percent);
- }
- }
- });
- }
- private static void callCompleteCallback() {
- synchronized (WebViewUpgrade.class) {
- UPGRADE_STATUS = STATUS_COMPLETE;
- }
- runInMainThread(() -> {
- synchronized (WebViewUpgrade.class) {
- for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_LIST) {
- upgradeCallback.onUpgradeComplete();
- }
- }
- });
- }
- private static void runInMainThread(Runnable runnable) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- runnable.run();
- } else {
- MAIN_HANDLER.post(runnable);
- }
- }
- public synchronized static String getSystemWebViewPackageName() {
- if (SYSTEM_WEB_VIEW_PACKAGE_NAME != null) {
- return SYSTEM_WEB_VIEW_PACKAGE_NAME;
- }
- loadSystemWebViewPackage();
- return SYSTEM_WEB_VIEW_PACKAGE_NAME;
- }
- public synchronized static String getSystemWebViewPackageVersion() {
- if (SYSTEM_WEB_VIEW_PACKAGE_VERSION != null) {
- return SYSTEM_WEB_VIEW_PACKAGE_VERSION;
- }
- loadSystemWebViewPackage();
- return SYSTEM_WEB_VIEW_PACKAGE_VERSION;
- }
- public synchronized static String getUpgradeWebViewPackageName() {
- return UPGRADE_OPTIONS != null ? UPGRADE_OPTIONS.packageName : null;
- }
- public synchronized static String getUpgradeWebViewVersion() {
- return UPGRADE_OPTIONS != null ? UPGRADE_OPTIONS.versionName : null;
- }
- private static void loadSystemWebViewPackage() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- try {
- PackageInfo packageInfo = WebView.getCurrentWebViewPackage();
- SYSTEM_WEB_VIEW_PACKAGE_NAME = packageInfo.packageName;
- SYSTEM_WEB_VIEW_PACKAGE_VERSION = packageInfo.versionName;
- } catch (Throwable ignore) {
- }
- }
- if (SYSTEM_WEB_VIEW_PACKAGE_NAME == null) {
- try {
- IServiceManager serviceManager = RuntimeAccess.staticAccess(IServiceManager.class);
- IBinder binder = serviceManager.getService(IWebViewUpdateService.SERVICE);
- IWebViewUpdateService service = RuntimeAccess.staticAccess(IWebViewUpdateService.class);
- IInterface iInterface = service.asInterface(binder);
- service = RuntimeAccess.objectAccess(IWebViewUpdateService.class, iInterface);
- PackageInfo packageInfo = service.getCurrentWebViewPackage();
- SYSTEM_WEB_VIEW_PACKAGE_NAME = packageInfo.packageName;
- SYSTEM_WEB_VIEW_PACKAGE_VERSION = packageInfo.versionName;
- } catch (Throwable ignore) {
- }
- }
- }
- }
|