From 9ac71ff8af8086e52badbdfffb9a3328fa61eb9d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 2 Aug 2018 00:41:10 +0800 Subject: [PATCH] Simplify asynchronous tasks --- .../com/topjohnwu/magisk/MagiskFragment.java | 2 +- .../com/topjohnwu/magisk/MagiskManager.java | 5 - .../com/topjohnwu/magisk/ModulesFragment.java | 11 +- .../com/topjohnwu/magisk/ReposFragment.java | 35 ++-- .../topjohnwu/magisk/SettingsActivity.java | 6 +- .../com/topjohnwu/magisk/SplashActivity.java | 26 ++- .../topjohnwu/magisk/asyncs/CheckUpdates.java | 57 +++---- .../com/topjohnwu/magisk/asyncs/FlashZip.java | 4 +- .../topjohnwu/magisk/asyncs/HideManager.java | 97 ----------- .../topjohnwu/magisk/asyncs/LoadModules.java | 36 ----- .../com/topjohnwu/magisk/asyncs/PatchAPK.java | 153 ++++++++++++++++++ .../magisk/asyncs/RestoreImages.java | 39 ----- .../topjohnwu/magisk/asyncs/UpdateRepos.java | 92 +++++------ .../magisk/components/UninstallDialog.java | 19 ++- .../magisk/receivers/ManagerUpdate.java | 18 ++- .../magisk/services/UpdateCheckService.java | 2 +- .../com/topjohnwu/magisk/utils/PatchAPK.java | 78 --------- .../com/topjohnwu/magisk/utils/Topic.java | 10 ++ .../com/topjohnwu/magisk/utils/Utils.java | 20 +++ 19 files changed, 325 insertions(+), 385 deletions(-) delete mode 100644 app/src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java delete mode 100644 app/src/full/java/com/topjohnwu/magisk/asyncs/LoadModules.java create mode 100644 app/src/full/java/com/topjohnwu/magisk/asyncs/PatchAPK.java delete mode 100644 app/src/full/java/com/topjohnwu/magisk/asyncs/RestoreImages.java delete mode 100644 app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java diff --git a/app/src/full/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/full/java/com/topjohnwu/magisk/MagiskFragment.java index 4b0bccd3d..48fd26c80 100644 --- a/app/src/full/java/com/topjohnwu/magisk/MagiskFragment.java +++ b/app/src/full/java/com/topjohnwu/magisk/MagiskFragment.java @@ -172,7 +172,7 @@ public class MagiskFragment extends BaseFragment // Trigger state check if (Download.checkNetworkStatus(mm)) { - new CheckUpdates().exec(); + CheckUpdates.check(); } else { mSwipeRefreshLayout.setRefreshing(false); } diff --git a/app/src/full/java/com/topjohnwu/magisk/MagiskManager.java b/app/src/full/java/com/topjohnwu/magisk/MagiskManager.java index 327ea3bb9..04115c751 100644 --- a/app/src/full/java/com/topjohnwu/magisk/MagiskManager.java +++ b/app/src/full/java/com/topjohnwu/magisk/MagiskManager.java @@ -6,7 +6,6 @@ import android.content.res.Configuration; import android.preference.PreferenceManager; import android.text.TextUtils; -import com.topjohnwu.magisk.container.Module; import com.topjohnwu.magisk.database.MagiskDatabaseHelper; import com.topjohnwu.magisk.database.RepoDatabaseHelper; import com.topjohnwu.magisk.utils.LocaleManager; @@ -15,16 +14,12 @@ import com.topjohnwu.superuser.ContainerApp; import com.topjohnwu.superuser.Shell; import java.lang.ref.WeakReference; -import java.util.Map; public class MagiskManager extends ContainerApp { // Info public boolean hasInit = false; - // Data - public Map moduleMap; - // Global resources public SharedPreferences prefs; public MagiskDatabaseHelper mDB; diff --git a/app/src/full/java/com/topjohnwu/magisk/ModulesFragment.java b/app/src/full/java/com/topjohnwu/magisk/ModulesFragment.java index 7ed391ad9..6db89fdb4 100644 --- a/app/src/full/java/com/topjohnwu/magisk/ModulesFragment.java +++ b/app/src/full/java/com/topjohnwu/magisk/ModulesFragment.java @@ -17,14 +17,15 @@ import android.view.ViewGroup; import android.widget.TextView; import com.topjohnwu.magisk.adapters.ModulesAdapter; -import com.topjohnwu.magisk.asyncs.LoadModules; import com.topjohnwu.magisk.components.BaseFragment; import com.topjohnwu.magisk.container.Module; import com.topjohnwu.magisk.utils.Topic; +import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.superuser.Shell; import java.util.ArrayList; import java.util.List; +import java.util.Map; import butterknife.BindView; import butterknife.ButterKnife; @@ -57,7 +58,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber { mSwipeRefreshLayout.setOnRefreshListener(() -> { recyclerView.setVisibility(View.GONE); - new LoadModules().exec(); + Utils.loadModules(); }); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -84,7 +85,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber { @Override public void onPublish(int topic, Object[] result) { - updateUI(); + updateUI((Map) result[0]); } @Override @@ -128,9 +129,9 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber { } } - private void updateUI() { + private void updateUI(Map moduleMap) { listModules.clear(); - listModules.addAll(mm.moduleMap.values()); + listModules.addAll(moduleMap.values()); if (listModules.size() == 0) { emptyRv.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); diff --git a/app/src/full/java/com/topjohnwu/magisk/ReposFragment.java b/app/src/full/java/com/topjohnwu/magisk/ReposFragment.java index 164edbb53..0be2d88f3 100644 --- a/app/src/full/java/com/topjohnwu/magisk/ReposFragment.java +++ b/app/src/full/java/com/topjohnwu/magisk/ReposFragment.java @@ -2,6 +2,7 @@ package com.topjohnwu.magisk; import android.app.AlertDialog; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.RecyclerView; @@ -17,8 +18,11 @@ import android.widget.TextView; import com.topjohnwu.magisk.adapters.ReposAdapter; import com.topjohnwu.magisk.asyncs.UpdateRepos; import com.topjohnwu.magisk.components.BaseFragment; +import com.topjohnwu.magisk.container.Module; import com.topjohnwu.magisk.utils.Topic; +import java.util.Map; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -40,30 +44,24 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber { @Nullable @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_repos, container, false); unbinder = ButterKnife.bind(this, view); mSwipeRefreshLayout.setRefreshing(true); + recyclerView.setVisibility(View.GONE); mSwipeRefreshLayout.setOnRefreshListener(() -> { recyclerView.setVisibility(View.VISIBLE); emptyRv.setVisibility(View.GONE); - new UpdateRepos(true).exec(); + new UpdateRepos().exec(true); }); - getActivity().setTitle(R.string.downloads); + requireActivity().setTitle(R.string.downloads); return view; } - @Override - public void onResume() { - adapter = new ReposAdapter(mm.repoDB, mm.moduleMap); - recyclerView.setAdapter(adapter); - super.onResume(); - } - @Override public void onPause() { super.onPause(); @@ -72,14 +70,23 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber { @Override public int[] getSubscribedTopics() { - return new int[] {Topic.REPO_LOAD_DONE}; + return new int[] {Topic.MODULE_LOAD_DONE, Topic.REPO_LOAD_DONE}; } @Override public void onPublish(int topic, Object[] result) { - mSwipeRefreshLayout.setRefreshing(false); - recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE); - emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + if (topic == Topic.MODULE_LOAD_DONE) { + adapter = new ReposAdapter(mm.repoDB, (Map) result[0]); + recyclerView.setAdapter(adapter); + recyclerView.setVisibility(View.VISIBLE); + emptyRv.setVisibility(View.GONE); + } + if (Topic.isPublished(getSubscribedTopics())) { + adapter.notifyDBChanged(); + mSwipeRefreshLayout.setRefreshing(false); + recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE); + emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } } @Override diff --git a/app/src/full/java/com/topjohnwu/magisk/SettingsActivity.java b/app/src/full/java/com/topjohnwu/magisk/SettingsActivity.java index 11a87e1e4..b21f3101d 100644 --- a/app/src/full/java/com/topjohnwu/magisk/SettingsActivity.java +++ b/app/src/full/java/com/topjohnwu/magisk/SettingsActivity.java @@ -21,7 +21,7 @@ import android.widget.EditText; import android.widget.Toast; import com.topjohnwu.magisk.asyncs.CheckUpdates; -import com.topjohnwu.magisk.asyncs.HideManager; +import com.topjohnwu.magisk.asyncs.PatchAPK; import com.topjohnwu.magisk.components.BaseActivity; import com.topjohnwu.magisk.receivers.DownloadReceiver; import com.topjohnwu.magisk.utils.Download; @@ -163,7 +163,7 @@ public class SettingsActivity extends BaseActivity implements Topic.Subscriber { if (Data.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) { if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) { hideManager.setOnPreferenceClickListener((pref) -> { - new HideManager(getActivity()).exec(); + PatchAPK.hideManager(requireActivity()); return true; }); generalCatagory.removePreference(restoreManager); @@ -287,7 +287,7 @@ public class SettingsActivity extends BaseActivity implements Topic.Subscriber { Topic.publish(false, Topic.RELOAD_ACTIVITY); break; case Const.Key.UPDATE_CHANNEL: - new CheckUpdates().exec(); + CheckUpdates.check(); break; case Const.Key.CHECK_UPDATES: Utils.setupUpdateCheck(); diff --git a/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java b/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java index 59a9c94c1..4abb7c85e 100644 --- a/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java +++ b/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java @@ -7,7 +7,6 @@ import android.os.Build; import android.os.Bundle; import com.topjohnwu.magisk.asyncs.CheckUpdates; -import com.topjohnwu.magisk.asyncs.LoadModules; import com.topjohnwu.magisk.asyncs.UpdateRepos; import com.topjohnwu.magisk.components.BaseActivity; import com.topjohnwu.magisk.database.RepoDatabaseHelper; @@ -23,8 +22,13 @@ public class SplashActivity extends BaseActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Force create a shell if not created yet - boolean root = Shell.rootAccess(); + // Magisk working as expected + if (Shell.rootAccess() && Data.magiskVersionCode > 0) { + // Update check service + Utils.setupUpdateCheck(); + // Load modules + Utils.loadModules(); + } mm.repoDB = new RepoDatabaseHelper(this); Data.importPrefs(); @@ -42,21 +46,11 @@ public class SplashActivity extends BaseActivity { // Setup shortcuts sendBroadcast(new Intent(this, ShortcutReceiver.class)); - LoadModules loadModuleTask = new LoadModules(); - if (Download.checkNetworkStatus(this)) { // Fire update check - new CheckUpdates().exec(); - // Add repo update check - loadModuleTask.setCallBack(() -> new UpdateRepos(false).exec()); - } - - // Magisk working as expected - if (root && Data.magiskVersionCode > 0) { - // Update check service - Utils.setupUpdateCheck(); - // Fire asynctasks - loadModuleTask.exec(); + CheckUpdates.check(); + // Repo update check + new UpdateRepos().exec(); } // Write back default values diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java index b06e5b675..98afc6c59 100644 --- a/app/src/full/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java +++ b/app/src/full/java/com/topjohnwu/magisk/asyncs/CheckUpdates.java @@ -1,9 +1,10 @@ package com.topjohnwu.magisk.asyncs; +import android.os.AsyncTask; + import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Data; -import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.utils.NotificationMgr; import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.WebService; @@ -11,19 +12,9 @@ import com.topjohnwu.magisk.utils.WebService; import org.json.JSONException; import org.json.JSONObject; -public class CheckUpdates extends ParallelTask { +public class CheckUpdates { - private boolean showNotification; - - public CheckUpdates() { - this(false); - } - - public CheckUpdates(boolean b) { - showNotification = b; - } - - private int getInt(JSONObject json, String name, int defValue) { + private static int getInt(JSONObject json, String name, int defValue) { if (json == null) return defValue; try { @@ -33,7 +24,7 @@ public class CheckUpdates extends ParallelTask { } } - private String getString(JSONObject json, String name, String defValue) { + private static String getString(JSONObject json, String name, String defValue) { if (json == null) return defValue; try { @@ -43,7 +34,7 @@ public class CheckUpdates extends ParallelTask { } } - private JSONObject getJson(JSONObject json, String name) { + private static JSONObject getJson(JSONObject json, String name) { try { return json.getJSONObject(name); } catch (JSONException e) { @@ -51,9 +42,7 @@ public class CheckUpdates extends ParallelTask { } } - @Override - protected Void doInBackground(Void... voids) { - MagiskManager mm = Data.MM(); + public static void fetchUpdates() { String jsonStr = ""; switch (Data.updateChannel) { case Const.Value.STABLE_CHANNEL: @@ -63,7 +52,7 @@ public class CheckUpdates extends ParallelTask { jsonStr = WebService.getString(Const.Url.BETA_URL); break; case Const.Value.CUSTOM_CHANNEL: - jsonStr = WebService.getString(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, "")); + jsonStr = WebService.getString(Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, "")); break; } @@ -71,7 +60,7 @@ public class CheckUpdates extends ParallelTask { try { json = new JSONObject(jsonStr); } catch (JSONException e) { - return null; + return; } JSONObject magisk = getJson(json, "magisk"); @@ -88,20 +77,24 @@ public class CheckUpdates extends ParallelTask { JSONObject uninstaller = getJson(json, "uninstaller"); Data.uninstallerLink = getString(uninstaller, "link", null); - - return null; } - @Override - protected void onPostExecute(Void v) { - if (showNotification) { - if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) { - NotificationMgr.managerUpdate(); - } else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) { - NotificationMgr.magiskUpdate(); + public static void check(Runnable cb) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + fetchUpdates(); + if (cb != null) { + if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) { + NotificationMgr.managerUpdate(); + } else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) { + NotificationMgr.magiskUpdate(); + } + cb.run(); } - } - Topic.publish(Topic.UPDATE_CHECK_DONE); - super.onPostExecute(v); + Topic.publish(Topic.UPDATE_CHECK_DONE); + }); + } + + public static void check() { + check(null); } } diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/FlashZip.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/FlashZip.java index 1fa2b39da..f14a520cd 100644 --- a/app/src/full/java/com/topjohnwu/magisk/asyncs/FlashZip.java +++ b/app/src/full/java/com/topjohnwu/magisk/asyncs/FlashZip.java @@ -94,8 +94,8 @@ public class FlashZip extends ParallelTask { console.add("! This zip is not a Magisk Module!"); break; case 1: - // Success - new LoadModules().exec(); + // Reload modules + Utils.loadModules(); break; } activity.reboot.setVisibility(result > 0 ? View.VISIBLE : View.GONE); diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java deleted file mode 100644 index 9f47921c2..000000000 --- a/app/src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.topjohnwu.magisk.asyncs; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.widget.Toast; - -import com.topjohnwu.magisk.Const; -import com.topjohnwu.magisk.Data; -import com.topjohnwu.magisk.MagiskManager; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.utils.PatchAPK; -import com.topjohnwu.magisk.utils.RootUtils; -import com.topjohnwu.magisk.utils.Utils; -import com.topjohnwu.superuser.Shell; -import com.topjohnwu.superuser.ShellUtils; -import com.topjohnwu.superuser.io.SuFile; -import com.topjohnwu.superuser.io.SuFileOutputStream; - -import java.io.FileNotFoundException; -import java.security.SecureRandom; - -public class HideManager extends ParallelTask { - - private ProgressDialog dialog; - - public HideManager(Activity activity) { - super(activity); - } - - private String genPackageName(String prefix, int length) { - StringBuilder builder = new StringBuilder(length); - builder.append(prefix); - length -= prefix.length(); - SecureRandom random = new SecureRandom(); - String base = "abcdefghijklmnopqrstuvwxyz"; - String alpha = base + base.toUpperCase(); - String full = alpha + "0123456789.........."; - char next, prev = '\0'; - for (int i = 0; i < length; ++i) { - if (prev == '.' || i == length - 1 || i == 0) { - next = alpha.charAt(random.nextInt(alpha.length())); - } else { - next = full.charAt(random.nextInt(full.length())); - } - builder.append(next); - prev = next; - } - return builder.toString(); - } - - @Override - protected void onPreExecute() { - dialog = ProgressDialog.show(getActivity(), - getActivity().getString(R.string.hide_manager_toast), - getActivity().getString(R.string.hide_manager_toast2)); - } - - @Override - protected Boolean doInBackground(Void... voids) { - MagiskManager mm = Data.MM(); - - // Generate a new app with random package name - SuFile repack = new SuFile("/data/local/tmp/repack.apk"); - String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length()); - - try { - if (!PatchAPK.patchPackageID( - mm.getPackageCodePath(), - new SuFileOutputStream(repack), - Const.ORIG_PKG_NAME, pkg)) - return false; - } catch (FileNotFoundException e) { - return false; - } - - // Install the application - if (!ShellUtils.fastCmdResult(Shell.getShell(), "pm install " + repack)) - return false; - - repack.delete(); - - mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg); - Data.exportPrefs(); - RootUtils.uninstallPkg(Const.ORIG_PKG_NAME); - - return true; - } - - @Override - protected void onPostExecute(Boolean b) { - dialog.dismiss(); - if (!b) { - Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG); - } - super.onPostExecute(b); - } -} diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/LoadModules.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/LoadModules.java deleted file mode 100644 index 46e4d51a3..000000000 --- a/app/src/full/java/com/topjohnwu/magisk/asyncs/LoadModules.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.topjohnwu.magisk.asyncs; - -import com.topjohnwu.magisk.Const; -import com.topjohnwu.magisk.Data; -import com.topjohnwu.magisk.MagiskManager; -import com.topjohnwu.magisk.container.Module; -import com.topjohnwu.magisk.container.ValueSortedMap; -import com.topjohnwu.magisk.utils.Topic; -import com.topjohnwu.superuser.io.SuFile; - -public class LoadModules extends ParallelTask { - - private String[] getModList() { - SuFile path = new SuFile(Const.MAGISK_PATH); - return path.list((file, name) -> !name.equals("lost+found") && !name.equals(".core")); - } - - @Override - protected Void doInBackground(Void... voids) { - MagiskManager mm = Data.MM(); - mm.moduleMap = new ValueSortedMap<>(); - - for (String name : getModList()) { - Module module = new Module(Const.MAGISK_PATH + "/" + name); - mm.moduleMap.put(module.getId(), module); - } - - return null; - } - - @Override - protected void onPostExecute(Void v) { - Topic.publish(Topic.MODULE_LOAD_DONE); - super.onPostExecute(v); - } -} diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/PatchAPK.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/PatchAPK.java new file mode 100644 index 000000000..1a9fc0358 --- /dev/null +++ b/app/src/full/java/com/topjohnwu/magisk/asyncs/PatchAPK.java @@ -0,0 +1,153 @@ +package com.topjohnwu.magisk.asyncs; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.widget.Toast; + +import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.Data; +import com.topjohnwu.magisk.MagiskManager; +import com.topjohnwu.magisk.R; +import com.topjohnwu.magisk.utils.RootUtils; +import com.topjohnwu.magisk.utils.Utils; +import com.topjohnwu.magisk.utils.ZipUtils; +import com.topjohnwu.superuser.ShellUtils; +import com.topjohnwu.superuser.io.SuFile; +import com.topjohnwu.superuser.io.SuFileOutputStream; +import com.topjohnwu.utils.JarMap; + +import java.security.SecureRandom; +import java.util.jar.JarEntry; + +public class PatchAPK { + + private static String genPackageName(String prefix, int length) { + StringBuilder builder = new StringBuilder(length); + builder.append(prefix); + length -= prefix.length(); + SecureRandom random = new SecureRandom(); + String base = "abcdefghijklmnopqrstuvwxyz"; + String alpha = base + base.toUpperCase(); + String full = alpha + "0123456789.........."; + char next, prev = '\0'; + for (int i = 0; i < length; ++i) { + if (prev == '.' || i == length - 1 || i == 0) { + next = alpha.charAt(random.nextInt(alpha.length())); + } else { + next = full.charAt(random.nextInt(full.length())); + } + builder.append(next); + prev = next; + } + return builder.toString(); + } + + private static int findOffset(byte buf[], byte pattern[]) { + int offset = -1; + for (int i = 0; i < buf.length - pattern.length; ++i) { + boolean match = true; + for (int j = 0; j < pattern.length; ++j) { + if (buf[i + j] != pattern[j]) { + match = false; + break; + } + } + if (match) { + offset = i; + break; + } + } + return offset; + } + + /* It seems that AAPT sometimes generate another type of string format */ + private static boolean fallbackPatch(byte xml[], String from, String to) { + + byte[] target = new byte[from.length() * 2 + 2]; + for (int i = 0; i < from.length(); ++i) { + target[i * 2] = (byte) from.charAt(i); + } + int offset = findOffset(xml, target); + if (offset < 0) + return false; + byte[] dest = new byte[target.length - 2]; + for (int i = 0; i < to.length(); ++i) { + dest[i * 2] = (byte) to.charAt(i); + } + System.arraycopy(dest, 0, xml, offset, dest.length); + return true; + } + + private static boolean findAndPatch(byte xml[], String from, String to) { + byte target[] = (from + '\0').getBytes(); + int offset = findOffset(xml, target); + if (offset < 0) + return fallbackPatch(xml, from, to); + System.arraycopy(to.getBytes(), 0, xml, offset, to.length()); + return true; + } + + private static boolean patchAndHide() { + MagiskManager mm = Data.MM(); + + // Generate a new app with random package name + SuFile repack = new SuFile("/data/local/tmp/repack.apk"); + String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length()); + + try { + JarMap apk = new JarMap(mm.getPackageCodePath()); + if (!patchPackageID(apk, Const.ORIG_PKG_NAME, pkg)) + return false; + ZipUtils.signZip(apk, new SuFileOutputStream(repack)); + } catch (Exception e) { + return false; + } + + // Install the application + if (!ShellUtils.fastCmdResult("pm install " + repack)) + return false; + + repack.delete(); + + mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg); + Data.exportPrefs(); + RootUtils.uninstallPkg(Const.ORIG_PKG_NAME); + + return true; + } + + public static boolean patchPackageID(JarMap apk, String from, String to) { + try { + JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST); + byte xml[] = apk.getRawData(je); + + if (!findAndPatch(xml, from, to)) + return false; + if (!findAndPatch(xml, from + ".provider", to + ".provider")) + return false; + + // Write in changes + apk.getOutputStream(je).write(xml); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + public static void hideManager(Activity activity) { + ProgressDialog dialog = ProgressDialog.show(activity, + activity.getString(R.string.hide_manager_toast), + activity.getString(R.string.hide_manager_toast2)); + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + boolean b = patchAndHide(); + Data.mainHandler.post(() -> { + dialog.cancel(); + if (!b) { + Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG); + } + }); + }); + } +} diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/RestoreImages.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/RestoreImages.java deleted file mode 100644 index 8c55b6958..000000000 --- a/app/src/full/java/com/topjohnwu/magisk/asyncs/RestoreImages.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.topjohnwu.magisk.asyncs; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.widget.Toast; - -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.utils.Utils; -import com.topjohnwu.superuser.ShellUtils; - -public class RestoreImages extends ParallelTask { - - private ProgressDialog dialog; - - public RestoreImages(Activity activity) { - super(activity); - } - - @Override - protected void onPreExecute() { - Activity a = getActivity(); - dialog = ProgressDialog.show(a, a.getString(R.string.restore_img), a.getString(R.string.restore_img_msg)); - } - - @Override - protected Boolean doInBackground(Void... voids) { - return ShellUtils.fastCmdResult("restore_imgs"); - } - - @Override - protected void onPostExecute(Boolean result) { - dialog.cancel(); - if (result) { - Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT); - } else { - Utils.toast(R.string.restore_fail, Toast.LENGTH_LONG); - } - } -} diff --git a/app/src/full/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java b/app/src/full/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java index 8e507ce3e..76ab80a5e 100644 --- a/app/src/full/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java +++ b/app/src/full/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java @@ -1,6 +1,7 @@ package com.topjohnwu.magisk.asyncs; import android.database.Cursor; +import android.os.AsyncTask; import android.text.TextUtils; import com.topjohnwu.magisk.Const; @@ -34,7 +35,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -public class UpdateRepos extends ParallelTask { +public class UpdateRepos { private static final int CHECK_ETAG = 0; private static final int LOAD_NEXT = 1; @@ -44,16 +45,14 @@ public class UpdateRepos extends ParallelTask { private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1); private MagiskManager mm; - private List etags, newEtags = new LinkedList<>(); + private List etags, newEtags; private Set cached; - private boolean forceUpdate; private ExecutorService threadPool; - public UpdateRepos(boolean force) { - Topic.reset(Topic.REPO_LOAD_DONE); + public UpdateRepos() { mm = Data.MM(); - forceUpdate = force; threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE); + newEtags = new LinkedList<>(); } private void waitTasks() { @@ -85,7 +84,10 @@ public class UpdateRepos extends ParallelTask { set.remove(id); repo.update(date); mm.repoDB.addRepo(repo); - publishProgress(); + Data.mainHandler.post(() -> { + if (ReposFragment.adapter != null) + ReposFragment.adapter.notifyDBChanged(); + }); } catch (Repo.IllegalRepoException e) { Logger.debug(e.getMessage()); mm.repoDB.removeRepo(id); @@ -142,47 +144,45 @@ public class UpdateRepos extends ParallelTask { return true; } - @Override - protected void onProgressUpdate(Void... values) { - if (ReposFragment.adapter != null) - ReposFragment.adapter.notifyDBChanged(); - } - - @Override - protected Void doInBackground(Void... voids) { - etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(",")); - cached = mm.repoDB.getRepoIDSet(); - - if (loadPage(0, CHECK_ETAG)) { - waitTasks(); - - // The leftover cached means they are removed from online repo - mm.repoDB.removeRepo(cached); - - // Update ETag - mm.prefs.edit().putString(Const.Key.ETAG_KEY, TextUtils.join(",", newEtags)).apply(); - } else if (forceUpdate) { - Cursor c = mm.repoDB.getRawCursor(); - while (c.moveToNext()) { - Repo repo = new Repo(c); - threadPool.execute(() -> { - try { - repo.update(); - mm.repoDB.addRepo(repo); - } catch (Repo.IllegalRepoException e) { - Logger.debug(e.getMessage()); - mm.repoDB.removeRepo(repo); - } - }); - } - waitTasks(); + private void fullReload() { + Cursor c = mm.repoDB.getRawCursor(); + while (c.moveToNext()) { + Repo repo = new Repo(c); + threadPool.execute(() -> { + try { + repo.update(); + mm.repoDB.addRepo(repo); + } catch (Repo.IllegalRepoException e) { + Logger.debug(e.getMessage()); + mm.repoDB.removeRepo(repo); + } + }); } - return null; + waitTasks(); } - @Override - protected void onPostExecute(Void v) { - Topic.publish(Topic.REPO_LOAD_DONE); - super.onPostExecute(v); + public void exec(boolean force) { + Topic.reset(Topic.REPO_LOAD_DONE); + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(",")); + cached = mm.repoDB.getRepoIDSet(); + + if (loadPage(0, CHECK_ETAG)) { + waitTasks(); + + // The leftover cached means they are removed from online repo + mm.repoDB.removeRepo(cached); + + // Update ETag + mm.prefs.edit().putString(Const.Key.ETAG_KEY, TextUtils.join(",", newEtags)).apply(); + } else if (force) { + fullReload(); + } + Topic.publish(Topic.REPO_LOAD_DONE); + }); + } + + public void exec() { + exec(false); } } diff --git a/app/src/full/java/com/topjohnwu/magisk/components/UninstallDialog.java b/app/src/full/java/com/topjohnwu/magisk/components/UninstallDialog.java index fa1ad9290..93164f72b 100644 --- a/app/src/full/java/com/topjohnwu/magisk/components/UninstallDialog.java +++ b/app/src/full/java/com/topjohnwu/magisk/components/UninstallDialog.java @@ -1,20 +1,23 @@ package com.topjohnwu.magisk.components; import android.app.Activity; +import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.NonNull; import android.text.TextUtils; +import android.widget.Toast; import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.FlashActivity; import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.asyncs.RestoreImages; import com.topjohnwu.magisk.receivers.DownloadReceiver; import com.topjohnwu.magisk.utils.Download; +import com.topjohnwu.magisk.utils.Utils; +import com.topjohnwu.superuser.Shell; public class UninstallDialog extends CustomAlertDialog { @@ -23,7 +26,19 @@ public class UninstallDialog extends CustomAlertDialog { MagiskManager mm = Data.MM(); setTitle(R.string.uninstall_magisk_title); setMessage(R.string.uninstall_magisk_msg); - setNeutralButton(R.string.restore_img, (d, i) -> new RestoreImages(activity).exec()); + setNeutralButton(R.string.restore_img, (d, i) -> { + ProgressDialog dialog = ProgressDialog.show(activity, + activity.getString(R.string.restore_img), + activity.getString(R.string.restore_img_msg)); + Shell.su("restore_imgs").submit(result -> { + dialog.cancel(); + if (result.isSuccess()) { + Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT); + } else { + Utils.toast(R.string.restore_fail, Toast.LENGTH_LONG); + } + }); + }); if (!TextUtils.isEmpty(Data.uninstallerLink)) { setPositiveButton(R.string.complete_uninstall, (d, i) -> Download.receive(activity, new DownloadReceiver() { diff --git a/app/src/full/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java b/app/src/full/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java index 248832baa..1642a0197 100644 --- a/app/src/full/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java +++ b/app/src/full/java/com/topjohnwu/magisk/receivers/ManagerUpdate.java @@ -7,12 +7,13 @@ import android.net.Uri; import android.os.AsyncTask; import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.asyncs.PatchAPK; import com.topjohnwu.magisk.utils.Download; -import com.topjohnwu.magisk.utils.PatchAPK; +import com.topjohnwu.magisk.utils.ZipUtils; +import com.topjohnwu.utils.JarMap; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; public class ManagerUpdate extends BroadcastReceiver { @@ -31,13 +32,14 @@ public class ManagerUpdate extends BroadcastReceiver { public void onDownloadDone(Context context, Uri uri) { if (!context.getPackageName().equals(Const.ORIG_PKG_NAME)) { AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - String o = uri.getPath(); - String p = o.substring(0, o.lastIndexOf('.')) + "-patched.apk"; + String orig = uri.getPath(); + String patch = orig.substring(0, orig.lastIndexOf('.')) + "-patched.apk"; try { - PatchAPK.patchPackageID(o, new BufferedOutputStream(new FileOutputStream(p)), - Const.ORIG_PKG_NAME, context.getPackageName()); - } catch (FileNotFoundException ignored) { } - super.onDownloadDone(context, Uri.fromFile(new File(p))); + JarMap apk = new JarMap(orig); + PatchAPK.patchPackageID(apk, Const.ORIG_PKG_NAME, context.getPackageName()); + ZipUtils.signZip(apk, new BufferedOutputStream(new FileOutputStream(patch))); + super.onDownloadDone(context, Uri.fromFile(new File(patch))); + } catch (Exception ignored) { } }); } else { super.onDownloadDone(context, uri); diff --git a/app/src/full/java/com/topjohnwu/magisk/services/UpdateCheckService.java b/app/src/full/java/com/topjohnwu/magisk/services/UpdateCheckService.java index 41bda1e7c..b5198ed50 100644 --- a/app/src/full/java/com/topjohnwu/magisk/services/UpdateCheckService.java +++ b/app/src/full/java/com/topjohnwu/magisk/services/UpdateCheckService.java @@ -11,7 +11,7 @@ public class UpdateCheckService extends JobService { @Override public boolean onStartJob(JobParameters params) { Shell.getShell(); - new CheckUpdates(true).setCallBack(() -> jobFinished(params, false)).exec(); + CheckUpdates.check(() -> jobFinished(params, false)); return true; } diff --git a/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java b/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java deleted file mode 100644 index 8f57a3224..000000000 --- a/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.topjohnwu.magisk.utils; - -import com.topjohnwu.magisk.Const; -import com.topjohnwu.utils.JarMap; - -import java.io.OutputStream; -import java.util.jar.JarEntry; - -public class PatchAPK { - - private static int findOffset(byte buf[], byte pattern[]) { - int offset = -1; - for (int i = 0; i < buf.length - pattern.length; ++i) { - boolean match = true; - for (int j = 0; j < pattern.length; ++j) { - if (buf[i + j] != pattern[j]) { - match = false; - break; - } - } - if (match) { - offset = i; - break; - } - } - return offset; - } - - /* It seems that AAPT sometimes generate another type of string format */ - private static boolean fallbackPatch(byte xml[], String from, String to) { - - byte[] target = new byte[from.length() * 2 + 2]; - for (int i = 0; i < from.length(); ++i) { - target[i * 2] = (byte) from.charAt(i); - } - int offset = findOffset(xml, target); - if (offset < 0) - return false; - byte[] dest = new byte[target.length - 2]; - for (int i = 0; i < to.length(); ++i) { - dest[i * 2] = (byte) to.charAt(i); - } - System.arraycopy(dest, 0, xml, offset, dest.length); - return true; - } - - private static boolean findAndPatch(byte xml[], String from, String to) { - byte target[] = (from + '\0').getBytes(); - int offset = findOffset(xml, target); - if (offset < 0) - return fallbackPatch(xml, from, to); - System.arraycopy(to.getBytes(), 0, xml, offset, to.length()); - return true; - } - - public static boolean patchPackageID(String fileName, OutputStream out, String from, String to) { - try { - JarMap apk = new JarMap(fileName); - JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST); - byte xml[] = apk.getRawData(je); - - if (!findAndPatch(xml, from, to)) - return false; - if (!findAndPatch(xml, from + ".provider", to + ".provider")) - return false; - - // Write in changes - apk.getOutputStream(je).write(xml); - - // Sign the APK - ZipUtils.signZip(apk, out); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - return true; - } -} diff --git a/app/src/full/java/com/topjohnwu/magisk/utils/Topic.java b/app/src/full/java/com/topjohnwu/magisk/utils/Topic.java index 9112c1b28..6fafbee16 100644 --- a/app/src/full/java/com/topjohnwu/magisk/utils/Topic.java +++ b/app/src/full/java/com/topjohnwu/magisk/utils/Topic.java @@ -81,6 +81,16 @@ public class Topic { } } + public static boolean isPublished(@TopicID int... topics) { + for (int topic : topics) { + if (topicList[topic] == null) + return false; + if (!topicList[topic].published) + return false; + } + return true; + } + private static class Store { boolean published = false; Set subscribers = new HashSet<>(); diff --git a/app/src/full/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/full/java/com/topjohnwu/magisk/utils/Utils.java index 311709c02..62b0b312c 100644 --- a/app/src/full/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/full/java/com/topjohnwu/magisk/utils/Utils.java @@ -7,15 +7,20 @@ import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; +import android.os.AsyncTask; import android.provider.OpenableColumns; import android.widget.Toast; import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.MagiskManager; +import com.topjohnwu.magisk.container.Module; +import com.topjohnwu.magisk.container.ValueSortedMap; import com.topjohnwu.magisk.services.UpdateCheckService; +import com.topjohnwu.superuser.io.SuFile; import java.util.Locale; +import java.util.Map; public class Utils { @@ -90,4 +95,19 @@ public class Utils { public static void toast(int resId, int duration) { Data.mainHandler.post(() -> Toast.makeText(Data.MM(), resId, duration).show()); } + + public static void loadModules() { + Topic.reset(Topic.MODULE_LOAD_DONE); + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + Map moduleMap = new ValueSortedMap<>(); + SuFile path = new SuFile(Const.MAGISK_PATH); + String[] modules = path.list( + (file, name) -> !name.equals("lost+found") && !name.equals(".core")); + for (String name : modules) { + Module module = new Module(Const.MAGISK_PATH + "/" + name); + moduleMap.put(module.getId(), module); + } + Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap); + }); + } } \ No newline at end of file