Use internal library for networking

This commit is contained in:
topjohnwu 2018-12-12 05:51:45 -05:00
parent eab74ef06b
commit 59b1e63bdf
29 changed files with 552 additions and 365 deletions

View File

@ -71,8 +71,8 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.core:core:1.0.1'
implementation project(':net')
fullImplementation project(':utils')
fullImplementation 'com.amitshekhar.android:android-networking:1.0.2'
fullImplementation 'androidx.appcompat:appcompat:1.0.2'
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"

View File

@ -67,7 +67,7 @@ public class Const {
public static class Url {
public static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/stable.json";
public static final String BETA_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/beta.json";
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed";
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed&page=%d";
public static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
public static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
public static final String PAYPAL_URL = "https://www.paypal.me/topjohnwu";

View File

@ -1,78 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import dalvik.system.DexClassLoader;
public class CheckSafetyNet extends ParallelTask<Void, Void, Void> {
public static final File dexPath =
new File(Data.MM().getFilesDir().getParent() + "/snet", "snet.apk");
private ISafetyNetHelper helper;
public CheckSafetyNet(Activity activity) {
super(activity);
}
private void dlSnet() throws Exception {
Shell.sh("rm -rf " + dexPath.getParent()).exec();
dexPath.getParentFile().mkdir();
HttpURLConnection conn = WebService.mustRequest(Const.Url.SNET_URL);
try (
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
InputStream in = new BufferedInputStream(conn.getInputStream())) {
ShellUtils.pump(in, out);
} finally {
conn.disconnect();
}
}
private void dyload() throws Exception {
DexClassLoader loader = new DexClassLoader(dexPath.getPath(), dexPath.getParent(),
null, ISafetyNetHelper.class.getClassLoader());
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.Snet");
helper = (ISafetyNetHelper) clazz.getMethod("newHelper",
Class.class, String.class, Activity.class, Object.class)
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
(ISafetyNetHelper.Callback) code ->
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() < Const.SNET_EXT_VER) {
throw new Exception();
}
}
@Override
protected Void doInBackground(Void... voids) {
try {
try {
dyload();
} catch (Exception e) {
// If dynamic load failed, try re-downloading and reload
dlSnet();
dyload();
}
// Run attestation
helper.attest();
} catch (Exception e) {
e.printStackTrace();
Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
}
return null;
}
}

View File

@ -1,13 +1,12 @@
package com.topjohnwu.magisk.asyncs;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.JSONObjectRequestListener;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
import org.json.JSONException;
import org.json.JSONObject;
@ -57,14 +56,14 @@ public class CheckUpdates {
default:
return;
}
AndroidNetworking.get(url).build().getAsJSONObject(new UpdateListener(cb));
Networking.get(url).getAsJSONObject(new UpdateListener(cb));
}
public static void check() {
check(null);
}
private static class UpdateListener implements JSONObjectRequestListener {
private static class UpdateListener implements ResponseListener<JSONObject> {
private Runnable cb;
@ -100,8 +99,5 @@ public class CheckUpdates {
}
Topic.publish(Topic.UPDATE_CHECK_DONE);
}
@Override
public void onError(ANError anError) {}
}
}

View File

@ -12,18 +12,15 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@ -40,12 +37,10 @@ public class DownloadModule {
ProgressNotification progress = new ProgressNotification(output.getName());
try {
MagiskManager mm = Data.MM();
HttpURLConnection conn = WebService.mustRequest(repo.getZipUrl());
ProgressInputStream pis = new ProgressInputStream(conn.getInputStream(),
conn.getContentLength(), progress);
removeTopFolder(new BufferedInputStream(pis),
new BufferedOutputStream(new FileOutputStream(output)));
conn.disconnect();
InputStream in = Networking.get(repo.getZipUrl())
.setDownloadProgressListener(progress)
.execForInputStream().getResult();
removeTopFolder(in, new BufferedOutputStream(new FileOutputStream(output)));
if (install) {
progress.dismiss();
Intent intent = new Intent(mm, Data.classMap.get(FlashActivity.class));
@ -81,45 +76,4 @@ public class DownloadModule {
}
}
private static class ProgressInputStream extends FilterInputStream {
private long totalBytes;
private long bytesDownloaded;
private ProgressNotification progress;
protected ProgressInputStream(InputStream in, long size, ProgressNotification p) {
super(in);
totalBytes = size;
progress = p;
}
private void updateProgress() {
progress.onProgress(bytesDownloaded, totalBytes);
}
@Override
public int read() throws IOException {
int b = super.read();
if (b >= 0) {
bytesDownloaded++;
updateProgress();
}
return b;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int sz = super.read(b, off, len);
if (sz > 0) {
bytesDownloaded += sz;
updateProgress();
}
return sz;
}
}
}

View File

@ -15,8 +15,9 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.TarEntry;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.magisk.utils.ZipUtils;
import com.topjohnwu.net.DownloadProgressListener;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.internal.NOPList;
@ -34,16 +35,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.List;
import androidx.annotation.NonNull;
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private static final int PATCH_MODE = 0;
@ -86,69 +83,40 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
}
}
private class ProgressStream extends FilterInputStream {
class ProgressLog implements DownloadProgressListener {
private int prev = -1;
private int progress = 0;
private int total;
private int location;
private ProgressStream(HttpURLConnection conn) throws IOException {
super(conn.getInputStream());
total = conn.getContentLength();
console.add("... 0%");
}
private void update(int step) {
progress += step;
int curr = (int) (100 * (double) progress / total);
@Override
public void onProgress(long bytesDownloaded, long totalBytes) {
if (prev < 0) {
location = console.size();
console.add("... 0%");
}
int curr = (int) (100 * bytesDownloaded / totalBytes);
if (prev != curr) {
prev = curr;
console.set(console.size() - 1, "... " + prev + "%");
console.set(location, "... " + prev + "%");
}
}
@Override
public int read() throws IOException {
int b = super.read();
if (b > 0)
update(1);
return b;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(@NonNull byte[] b, int off, int len) throws IOException {
int step = super.read(b, off, len);
if (step > 0)
update(step);
return step;
}
}
private void extractFiles(String arch) throws IOException {
File zip = new File(mm.getFilesDir(), "magisk.zip");
BufferedInputStream buf;
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
console.add("- Downloading zip");
HttpURLConnection conn = WebService.mustRequest(Data.magiskLink);
buf = new BufferedInputStream(new ProgressStream(conn), conn.getContentLength());
buf.mark(conn.getContentLength() + 1);
try (OutputStream out = new FileOutputStream(zip)) {
ShellUtils.pump(buf, out);
}
buf.reset();
conn.disconnect();
Networking.get(Data.magiskLink)
.setDownloadProgressListener(new ProgressLog())
.execForFile(zip);
} else {
console.add("- Existing zip found");
buf = new BufferedInputStream(new FileInputStream(zip), (int) zip.length());
buf.mark((int) zip.length() + 1);
}
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(zip), (int) zip.length());
buf.mark((int) zip.length() + 1);
console.add("- Extracting files");
try (InputStream in = buf) {
ZipUtils.unzip(in, installDir, arch + "/", true);

View File

@ -0,0 +1,51 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import dalvik.system.DexClassLoader;
public class SafetyNet {
public static final File EXT_APK =
new File(Data.MM().getFilesDir().getParent() + "/snet", "snet.apk");
private static void dyRun(Activity activity) throws Exception {
DexClassLoader loader = new DexClassLoader(EXT_APK.getPath(), EXT_APK.getParent(),
null, ISafetyNetHelper.class.getClassLoader());
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.Snet");
ISafetyNetHelper helper = (ISafetyNetHelper) clazz.getMethod("newHelper",
Class.class, String.class, Activity.class, Object.class)
.invoke(null, ISafetyNetHelper.class, EXT_APK.getPath(), activity,
(ISafetyNetHelper.Callback) code ->
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() < Const.SNET_EXT_VER)
throw new Exception();
helper.attest();
}
public static void check(Activity activity) {
try {
dyRun(activity);
} catch (Exception ignored) {
Shell.sh("rm -rf " + EXT_APK.getParent()).exec();
EXT_APK.getParentFile().mkdir();
Networking.get(Const.Url.SNET_URL).getAsFile(f -> {
try {
dyRun(activity);
} catch (Exception e) {
e.printStackTrace();
Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
}
}, EXT_APK);
}
}
}

View File

@ -3,15 +3,15 @@ package com.topjohnwu.magisk.asyncs;
import android.database.Cursor;
import android.os.AsyncTask;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.common.ANRequest;
import com.androidnetworking.common.ANResponse;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.Request;
import org.json.JSONArray;
import org.json.JSONException;
@ -84,22 +84,23 @@ public class UpdateRepos {
* first page is updated to determine whether the online repo database is changed
*/
private boolean loadPage(int page) {
ANRequest.GetRequestBuilder req = AndroidNetworking.get(Const.Url.REPO_URL)
.addQueryParameter("page", String.valueOf(page + 1));
Request req = Networking.get(Utils.fmt(Const.Url.REPO_URL, page + 1));
if (page == 0) {
String etag = mm.prefs.getString(Const.Key.ETAG_KEY, null);
if (etag != null)
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
}
ANResponse<JSONArray> res = req.build().executeForJSONArray();
// Network drop
if (res.getOkHttpResponse() == null)
return true;
Request.Result<JSONArray> res = req.execForJSONArray();
// JSON not updated
if (res.getOkHttpResponse().code() == HttpURLConnection.HTTP_NOT_MODIFIED)
if (res.getCode() == HttpURLConnection.HTTP_NOT_MODIFIED)
return false;
// Network error
if (res.getResult() == null) {
cached.clear();
return true;
}
// Current page is the last page
if (res.getResult() == null || res.getResult().length() == 0)
if (res.getResult().length() == 0)
return true;
try {
@ -111,14 +112,14 @@ public class UpdateRepos {
// Update ETAG
if (page == 0) {
String etag = res.getOkHttpResponse().header(Const.Key.ETAG_KEY);
String etag = res.getConnection().getHeaderField(Const.Key.ETAG_KEY);
if (etag != null) {
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
}
}
String links = res.getOkHttpResponse().header(Const.Key.LINK_KEY);
String links = res.getConnection().getHeaderField(Const.Key.LINK_KEY);
return links == null || !links.contains("next") || loadPage(page + 1);
}

View File

@ -5,16 +5,15 @@ import android.app.Activity;
import android.content.Intent;
import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.DownloadListener;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import java.io.File;
import java.util.List;
import androidx.appcompat.app.AlertDialog;
@ -67,25 +66,17 @@ class InstallMethodDialog extends AlertDialog.Builder {
a.runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
File zip = new File(Const.EXTERNAL_PATH, filename);
ProgressNotification progress = new ProgressNotification(filename);
AndroidNetworking
.download(Data.magiskLink, Const.EXTERNAL_PATH.getPath(), filename)
.build()
Networking.get(Data.magiskLink)
.setDownloadProgressListener(progress)
.startDownload(new DownloadListener() {
@Override
public void onDownloadComplete() {
progress.dlDone();
SnackbarMaker.make(a,
.setErrorHandler(((conn, e) -> progress.dlFail()))
.getAsFile(f -> {
progress.dlDone();
SnackbarMaker.make(a,
a.getString(R.string.internal_storage, "/Download/" + filename),
Snackbar.LENGTH_LONG).show();
}
@Override
public void onError(ANError anError) {
progress.dlFail();
}
});
}, zip);
});
}

View File

@ -2,17 +2,16 @@ package com.topjohnwu.magisk.components;
import android.widget.Toast;
import com.androidnetworking.interfaces.DownloadProgressListener;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.DownloadProgressListener;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
public class ProgressNotification implements DownloadProgressListener {
private NotificationManagerCompat mgr;
private NotificationCompat.Builder builder;
private long prevTime;

View File

@ -7,14 +7,12 @@ import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.DownloadListener;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import java.io.File;
@ -44,25 +42,17 @@ public class UninstallDialog extends CustomAlertDialog {
setPositiveButton(R.string.complete_uninstall, (d, i) -> {
File zip = new File(activity.getFilesDir(), "uninstaller.zip");
ProgressNotification progress = new ProgressNotification(zip.getName());
AndroidNetworking.download(Data.uninstallerLink, zip.getParent(), zip.getName())
.build()
Networking.get(Data.uninstallerLink)
.setDownloadProgressListener(progress)
.startDownload(new DownloadListener() {
@Override
public void onDownloadComplete() {
progress.dismiss();
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.fromFile(zip))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
activity.startActivity(intent);
}
@Override
public void onError(ANError anError) {
progress.dlFail();
}
});
.setErrorHandler(((conn, e) -> progress.dlFail()))
.getAsFile(f -> {
progress.dismiss();
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.fromFile(f))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
activity.startActivity(intent);
}, zip);
});
}
}

View File

@ -20,8 +20,8 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.SafetyNet;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.CustomAlertDialog;
@ -91,10 +91,10 @@ public class MagiskFragment extends BaseFragment
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetRefreshIcon.setVisibility(View.GONE);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
new CheckSafetyNet(requireActivity()).exec();
SafetyNet.check(requireActivity());
collapse();
};
if (!CheckSafetyNet.dexPath.exists()) {
if (!SafetyNet.EXT_APK.exists()) {
// Show dialog
new CustomAlertDialog(requireActivity())
.setTitle(R.string.proprietary_title)

View File

@ -5,8 +5,6 @@ import com.topjohnwu.utils.SignBoot;
import java.io.FileInputStream;
import java.io.InputStream;
import androidx.annotation.Keep;
public class BootSigner {
public static void main(String[] args) throws Exception {

View File

@ -2,15 +2,14 @@ package com.topjohnwu.magisk.utils;
import android.os.AsyncTask;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.DownloadListener;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK;
@ -31,42 +30,35 @@ public class DlInstallManager {
dlInstall(name, new RestoreManager());
}
public static void dlInstall(String name, ManagerDownloadListener listener) {
private static void dlInstall(String name, ManagerDownloadListener listener) {
MagiskManager mm = Data.MM();
File apk = new File(mm.getFilesDir(), "manager.apk");
ProgressNotification progress = new ProgressNotification(name);
listener.setInstances(apk, progress);
AndroidNetworking
.download(Data.managerLink, apk.getParent(), apk.getName())
listener.setProgressNotification(progress);
Networking.get(Data.managerLink)
.setExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.build()
.setDownloadProgressListener(progress)
.startDownload(listener);
.setErrorHandler((conn, e) -> progress.dlFail())
.getAsFile(listener, apk);
}
public abstract static class ManagerDownloadListener implements DownloadListener {
private File apk;
abstract static class ManagerDownloadListener implements ResponseListener<File> {
private ProgressNotification progress;
private void setInstances(File apk, ProgressNotification progress) {
this.apk = apk;
private void setProgressNotification(ProgressNotification progress) {
this.progress = progress;
}
public abstract void onDownloadComplete(File apk, ProgressNotification progress);
@Override
public final void onDownloadComplete() {
public void onResponse(File apk) {
onDownloadComplete(apk, progress);
}
@Override
public void onError(ANError anError) {
progress.dlFail();
}
}
private static class PatchPackageName extends ManagerDownloadListener {
static class PatchPackageName extends ManagerDownloadListener {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
@ -92,7 +84,7 @@ public class DlInstallManager {
}
}
private static class RestoreManager extends ManagerDownloadListener {
static class RestoreManager extends ManagerDownloadListener {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {

View File

@ -16,7 +16,6 @@ import android.os.AsyncTask;
import android.provider.OpenableColumns;
import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
@ -24,6 +23,7 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
@ -152,7 +152,7 @@ public class Utils {
}
public static String dlString(String url) {
String s = (String) AndroidNetworking.get(url).build().executeForString().getResult();
String s = Networking.get(url).execForString().getResult();
return s == null ? "" : s;
}
}

View File

@ -1,30 +0,0 @@
package com.topjohnwu.magisk.utils;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class WebService {
public static HttpURLConnection request(String address) throws IOException {
URL url = new URL(address);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setConnectTimeout(15000);
conn.connect();
return conn;
}
public static HttpURLConnection mustRequest(String address) throws IOException {
HttpURLConnection conn;
do {
conn = WebService.request(address);
int total = conn.getContentLength();
if (total < 0)
conn.disconnect();
else
break;
} while (true);
return conn;
}
}

View File

@ -3,26 +3,17 @@ package com.topjohnwu.magisk;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.os.AsyncTask;
import android.os.Bundle;
import com.topjohnwu.magisk.utils.APKInstall;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
public class MainActivity extends Activity {
@ -33,65 +24,18 @@ public class MainActivity extends Activity {
private void dlAPK() {
Application app = getApplication();
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
try {
HttpURLConnection conn = WebService.request(apkLink);
File apk = new File(getFilesDir(), "manager.apk");
try (InputStream in = new BufferedInputStream(conn.getInputStream());
OutputStream out = new BufferedOutputStream(new FileOutputStream(apk))) {
int len;
byte[] buf = new byte[4096];
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
conn.disconnect();
APKInstall.install(app, apk);
} catch (IOException e) {
e.printStackTrace();
}
});
Networking.get(apkLink)
.getAsFile(apk -> APKInstall.install(app, apk), new File(getFilesDir(), "manager.apk"));
finish();
}
private void dlJSON() throws IOException, JSONException {
HttpURLConnection conn = WebService.request(URL);
StringBuilder builder = new StringBuilder();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
int len;
char buf[] = new char[4096];
while ((len = br.read(buf)) != -1) {
builder.append(buf, 0, len);
}
}
}
conn.disconnect();
JSONObject json = new JSONObject(builder.toString());
JSONObject manager = json.getJSONObject("app");
apkLink = manager.getString("link");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Download.checkNetworkStatus(this)) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
try {
dlJSON();
runOnUiThread(() -> {
new AlertDialog.Builder(this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT)
.setCancelable(false)
.setTitle(R.string.app_name)
.setMessage(R.string.upgrade_msg)
.setPositiveButton(R.string.yes, (d, w) -> dlAPK())
.setNegativeButton(R.string.no_thanks, (d, w) -> finish())
.show();
});
} catch (JSONException | IOException e) {
finish();
}
});
Networking.get(URL)
.setErrorHandler(((conn, e) -> finish()))
.getAsJSONObject(new JSONLoader());
} else {
new AlertDialog.Builder(this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT)
.setCancelable(false)
@ -100,6 +44,25 @@ public class MainActivity extends Activity {
.setNegativeButton(R.string.ok, (d, w) -> finish())
.show();
}
}
class JSONLoader implements ResponseListener<JSONObject> {
@Override
public void onResponse(JSONObject json) {
try {
JSONObject manager = json.getJSONObject("app");
apkLink = manager.getString("link");
new AlertDialog.Builder(MainActivity.this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT)
.setCancelable(false)
.setTitle(R.string.app_name)
.setMessage(R.string.upgrade_msg)
.setPositiveButton(R.string.yes, (d, w) -> dlAPK())
.setNegativeButton(R.string.no_thanks, (d, w) -> finish())
.show();
} catch (JSONException e) {
finish();
}
}
}
}

1
net/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

29
net/build.gradle Normal file
View File

@ -0,0 +1,29 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}

21
net/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.topjohnwu.net" />

View File

@ -0,0 +1,5 @@
package com.topjohnwu.net;
public interface DownloadProgressListener {
void onProgress(long bytesDownloaded, long totalBytes);
}

View File

@ -0,0 +1,7 @@
package com.topjohnwu.net;
import java.net.HttpURLConnection;
public interface ErrorHandler {
void onError(HttpURLConnection conn, Exception e);
}

View File

@ -0,0 +1,33 @@
package com.topjohnwu.net;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
public class Networking {
private static final int READ_TIMEOUT = 15000;
private static final int CONNECT_TIMEOUT = 15000;
static Handler mainHandler = new Handler(Looper.getMainLooper());
private static Request request(String url, String method) {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod(method);
conn.setReadTimeout(READ_TIMEOUT);
conn.setConnectTimeout(CONNECT_TIMEOUT);
return new Request(conn);
} catch (IOException e) {
return new StubRequest();
}
}
public static Request get(String url) {
return request(url, "GET");
}
}

View File

@ -0,0 +1,47 @@
package com.topjohnwu.net;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ProgressInputStream extends FilterInputStream {
private long totalBytes;
private long bytesDownloaded;
public ProgressInputStream(InputStream in, long total) {
super(in);
totalBytes = total;
}
protected void updateProgress(long bytesDownloaded, long totalBytes) {}
private void update() {
Networking.mainHandler.post(() -> updateProgress(bytesDownloaded, totalBytes));
}
@Override
public int read() throws IOException {
int b = super.read();
if (b >= 0) {
bytesDownloaded++;
update();
}
return b;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int sz = super.read(b, off, len);
if (sz > 0) {
bytesDownloaded += sz;
update();
}
return sz;
}
}

View File

@ -0,0 +1,193 @@
package com.topjohnwu.net;
import android.os.AsyncTask;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.util.concurrent.Executor;
public class Request {
private HttpURLConnection conn;
private Executor executor = null;
private DownloadProgressListener progress = null;
private ErrorHandler err = null;
private int code;
private interface Requestor<T> {
T request() throws Exception;
}
public class Result<T> {
T result;
public T getResult() {
return result;
}
public int getCode() {
return code;
}
public HttpURLConnection getConnection() {
return conn;
}
}
Request(HttpURLConnection c) {
conn = c;
}
public Request addHeaders(String key, String value) {
conn.setRequestProperty(key, value);
return this;
}
public Request setDownloadProgressListener(DownloadProgressListener listener) {
progress = listener;
return this;
}
public Request setErrorHandler(ErrorHandler handler) {
err = handler;
return this;
}
public Request setExecutor(Executor e) {
executor = e;
return this;
}
public Result<InputStream> execForInputStream() {
return exec(this::getInputStream);
}
public void getAsFile(ResponseListener<File> rs, File out) {
submit(() -> dlFile(out), rs);
}
public void execForFile(File out) {
exec(() -> dlFile(out));
}
public void getAsString(ResponseListener<String> rs) {
submit(this::dlString, rs);
}
public Result<String> execForString() {
return exec(this::dlString);
}
public void getAsJSONObject(ResponseListener<JSONObject> rs) {
submit(this::dlJSONObject, rs);
}
public Result<JSONObject> execForJSONObject() {
return exec(this::dlJSONObject);
}
public void getAsJSONArray(ResponseListener<JSONArray> rs) {
submit(this::dlJSONArray, rs);
}
public Result<JSONArray> execForJSONArray() {
return exec(this::dlJSONArray);
}
private <T> Result<T> exec(Requestor<T> req) {
Result<T> res = new Result<>();
try {
res.result = req.request();
} catch (Exception ignored) {}
return res;
}
private <T> void submit(Requestor<T> req, ResponseListener<T> rs) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
try {
T t = req.request();
Runnable cb = () -> rs.onResponse(t);
if (executor == null)
Networking.mainHandler.post(cb);
else
executor.execute(cb);
} catch (Exception e) {
if (err != null)
err.onError(conn, e);
}
});
}
private BufferedInputStream getInputStream() throws IOException {
conn.connect();
code = conn.getResponseCode();
InputStream in = conn.getInputStream();
if (progress != null) {
in = new ProgressInputStream(in, conn.getContentLength()) {
@Override
protected void updateProgress(long bytesDownloaded, long totalBytes) {
progress.onProgress(bytesDownloaded, totalBytes);
}
@Override
public void close() throws IOException {
super.close();
conn.disconnect();
}
};
} else {
in = new FilterInputStream(in) {
@Override
public void close() throws IOException {
super.close();
conn.disconnect();
}
};
}
return new BufferedInputStream(in);
}
private String dlString() throws IOException {
StringBuilder builder = new StringBuilder();
try (Reader reader = new InputStreamReader(getInputStream())) {
int len;
char buf[] = new char[4096];
while ((len = reader.read(buf)) != -1) {
builder.append(buf, 0, len);
}
}
return builder.toString();
}
private JSONObject dlJSONObject() throws IOException, JSONException {
return new JSONObject(dlString());
}
private JSONArray dlJSONArray() throws IOException, JSONException {
return new JSONArray(dlString());
}
private File dlFile(File f) throws IOException {
try (InputStream in = getInputStream();
OutputStream out = new BufferedOutputStream(new FileOutputStream(f))) {
int len;
byte buf[] = new byte[4096];
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
return f;
}
}

View File

@ -0,0 +1,5 @@
package com.topjohnwu.net;
public interface ResponseListener<T> {
void onResponse(T response);
}

View File

@ -0,0 +1,49 @@
package com.topjohnwu.net;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.Executor;
class StubRequest extends Request {
StubRequest() { super(null); }
@Override
public Request addHeaders(String key, String value) { return this; }
@Override
public Request setDownloadProgressListener(DownloadProgressListener listener) { return this; }
@Override
public Request setErrorHandler(ErrorHandler handler) { return this; }
@Override
public Request setExecutor(Executor e) { return this; }
@Override
public Result<InputStream> execForInputStream() { return new Result<>(); }
@Override
public void getAsFile(ResponseListener<File> rs, File out) {}
@Override
public void getAsString(ResponseListener<String> rs) {}
@Override
public Result<String> execForString() { return new Result<>(); }
@Override
public void getAsJSONObject(ResponseListener<JSONObject> rs){}
@Override
public Result<JSONObject> execForJSONObject() { return new Result<>(); }
@Override
public void getAsJSONArray(ResponseListener<JSONArray> rs){}
@Override
public Result<JSONArray> execForJSONArray() { return new Result<>(); }
}

View File

@ -1 +1 @@
include ':app', ':native', ':utils', ':snet'
include ':app', ':native', ':utils', ':snet', ':net'