diff --git a/app/build.gradle b/app/build.gradle
index 371ec0d86..b0d0668be 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -63,6 +63,5 @@ dependencies {
implementation 'org.bouncycastle:bcprov-jdk15on:1.57'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.57'
implementation 'org.kamranzafar:jtar:2.3'
- implementation 'com.google.android.gms:play-services-safetynet:9.0.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f179bf238..32d9847fa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -90,9 +90,10 @@
android:resource="@xml/file_paths" />
+
+ android:value="11400000" />
diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
index 19921fee0..314d90132 100644
--- a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
+++ b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
@@ -4,6 +4,7 @@ import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.CardView;
@@ -19,7 +20,9 @@ import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
+import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
+import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
@@ -39,6 +42,13 @@ import butterknife.Unbinder;
public class MagiskFragment extends Fragment
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
+ public static final int CAUSE_SERVICE_DISCONNECTED = 0x00001;
+ public static final int CAUSE_NETWORK_LOST = 0x00010;
+ public static final int RESPONSE_ERR = 0x00100;
+
+ public static final int BASIC_PASS = 0x01000;
+ public static final int CTS_PASS = 0x10000;
+
private Container expandableContainer = new Container();
private MagiskManager mm;
@@ -85,11 +95,28 @@ public class MagiskFragment extends Fragment
@OnClick(R.id.safetyNet_title)
void safetyNet() {
- safetyNetProgress.setVisibility(View.VISIBLE);
- safetyNetRefreshIcon.setVisibility(View.GONE);
- safetyNetStatusText.setText(R.string.checking_safetyNet_status);
- Utils.checkSafetyNet(getActivity());
- collapse();
+ Runnable task = () -> {
+ mm.snet_version = CheckSafetyNet.SNET_VER;
+ mm.prefs.edit().putInt("snet_version", CheckSafetyNet.SNET_VER).apply();
+ safetyNetProgress.setVisibility(View.VISIBLE);
+ safetyNetRefreshIcon.setVisibility(View.GONE);
+ safetyNetStatusText.setText(R.string.checking_safetyNet_status);
+ new CheckSafetyNet(getActivity()).exec();
+ collapse();
+ };
+ if (mm.snet_version < 0) {
+ // Show dialog
+ new AlertDialogBuilder(getActivity())
+ .setTitle(R.string.proprietary_title)
+ .setMessage(R.string.proprietary_notice)
+ .setCancelable(true)
+ .setPositiveButton(R.string.yes, (d, i) -> task.run())
+ .setNegativeButton(R.string.no_thanks, null)
+ .show();
+ } else {
+ task.run();
+ }
+
}
@OnClick(R.id.install_button)
@@ -158,11 +185,11 @@ public class MagiskFragment extends Fragment
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
if (topic == mm.updateCheckDone) {
updateCheckUI();
} else if (topic == mm.safetyNetDone) {
- updateSafetyNetUI();
+ updateSafetyNetUI((int) result);
}
}
@@ -302,37 +329,41 @@ public class MagiskFragment extends Fragment
mSwipeRefreshLayout.setRefreshing(false);
}
- private void updateSafetyNetUI() {
- int image, color;
+ private void updateSafetyNetUI(int response) {
safetyNetProgress.setVisibility(View.GONE);
safetyNetRefreshIcon.setVisibility(View.VISIBLE);
- if (mm.SNCheckResult.failed) {
- safetyNetStatusText.setText(mm.SNCheckResult.errmsg);
- collapse();
- } else {
+ if (response < 0) {
+ safetyNetStatusText.setText(R.string.safetyNet_api_error);
+ } else if ((response & 0x111) == 0) {
safetyNetStatusText.setText(R.string.safetyNet_check_success);
- if (mm.SNCheckResult.ctsProfile) {
- color = colorOK;
- image = R.drawable.ic_check_circle;
- } else {
- color = colorBad;
- image = R.drawable.ic_cancel;
- }
- ctsStatusText.setText("ctsProfile: " + mm.SNCheckResult.ctsProfile);
- ctsStatusIcon.setImageResource(image);
- ctsStatusIcon.setColorFilter(color);
- if (mm.SNCheckResult.basicIntegrity) {
- color = colorOK;
- image = R.drawable.ic_check_circle;
- } else {
- color = colorBad;
- image = R.drawable.ic_cancel;
- }
- basicStatusText.setText("basicIntegrity: " + mm.SNCheckResult.basicIntegrity);
- basicStatusIcon.setImageResource(image);
- basicStatusIcon.setColorFilter(color);
+ boolean b;
+ b = (response & CTS_PASS) != 0;
+ ctsStatusText.setText("ctsProfile: " + b);
+ ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
+ ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
+
+ b = (response & BASIC_PASS) != 0;
+ basicStatusText.setText("basicIntegrity: " + b);
+ basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
+ basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
+
expand();
+ } else {
+ @StringRes int resid;
+ switch (response) {
+ case CAUSE_SERVICE_DISCONNECTED:
+ resid = R.string.safetyNet_network_loss;
+ break;
+ case CAUSE_NETWORK_LOST:
+ resid = R.string.safetyNet_service_disconnected;
+ break;
+ case RESPONSE_ERR:
+ default:
+ resid = R.string.safetyNet_res_invalid;
+ break;
+ }
+ safetyNetStatusText.setText(resid);
}
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java
index 336e63862..85d565a75 100644
--- a/app/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java
+++ b/app/src/main/java/com/topjohnwu/magisk/MagiskHideFragment.java
@@ -84,7 +84,7 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
mSwipeRefreshLayout.setRefreshing(false);
appAdapter.filter(lastFilter);
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java b/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
index 5dad66674..349e8d0b2 100644
--- a/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
+++ b/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
@@ -20,13 +20,12 @@ import com.topjohnwu.magisk.asyncs.DownloadBusybox;
import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
+import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
-import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.magisk.superuser.SuReceiver;
import com.topjohnwu.magisk.superuser.SuRequestActivity;
-import com.topjohnwu.magisk.utils.SafetyNetHelper;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
@@ -72,7 +71,6 @@ public class MagiskManager extends Application {
public String remoteManagerVersionString;
public int remoteManagerVersionCode = -1;
public String managerLink;
- public SafetyNetHelper.Result SNCheckResult;
public String bootBlock = null;
public boolean isSuClient = false;
public String suVersion = null;
@@ -103,6 +101,7 @@ public class MagiskManager extends Application {
public String localeConfig;
public int updateChannel;
public String bootFormat;
+ public int snet_version;
// Global resources
public SharedPreferences prefs;
@@ -184,6 +183,7 @@ public class MagiskManager extends Application {
updateNotification = prefs.getBoolean("notification", true);
updateChannel = Utils.getPrefsInt(prefs, "update_channel", CheckUpdates.STABLE_CHANNEL);
bootFormat = prefs.getString("boot_format", ".img");
+ snet_version = prefs.getInt("snet_version", -1);
}
public void toast(String msg, int duration) {
diff --git a/app/src/main/java/com/topjohnwu/magisk/MainActivity.java b/app/src/main/java/com/topjohnwu/magisk/MainActivity.java
index 5b0fe444b..5190601ee 100644
--- a/app/src/main/java/com/topjohnwu/magisk/MainActivity.java
+++ b/app/src/main/java/com/topjohnwu/magisk/MainActivity.java
@@ -102,7 +102,7 @@ public class MainActivity extends Activity
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
recreate();
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java
index 35b090517..e654cc1ca 100644
--- a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java
+++ b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java
@@ -72,7 +72,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
Logger.dev("ModulesFragment: UI refresh triggered");
updateUI();
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java
index c7ad8e4fb..fca67b71c 100644
--- a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java
+++ b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java
@@ -69,7 +69,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
mSwipeRefreshLayout.setRefreshing(false);
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java
index b4263bd5c..198d4395d 100644
--- a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java
+++ b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java
@@ -62,7 +62,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
recreate();
}
@@ -273,7 +273,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
}
@Override
- public void onTopicPublished(Topic topic) {
+ public void onTopicPublished(Topic topic, Object result) {
setLocalePreference((ListPreference) findPreference("locale"));
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java
index da9a1def5..d3283128b 100644
--- a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java
+++ b/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java
@@ -15,8 +15,8 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
-import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.container.Policy;
+import com.topjohnwu.magisk.database.SuDatabaseHelper;
import java.util.HashSet;
import java.util.List;
diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java
index 714fc3d54..e5937f7b8 100644
--- a/app/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java
+++ b/app/src/main/java/com/topjohnwu/magisk/adapters/ReposAdapter.java
@@ -17,9 +17,9 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
-import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.Repo;
+import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList;
diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java
index 5a31cd569..a90d04706 100644
--- a/app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java
+++ b/app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java
@@ -12,8 +12,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ExpandableView;
-import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.container.SuLogEntry;
+import com.topjohnwu.magisk.database.SuDatabaseHelper;
import java.util.Collections;
import java.util.HashSet;
diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java
new file mode 100644
index 000000000..ff81a9677
--- /dev/null
+++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/CheckSafetyNet.java
@@ -0,0 +1,81 @@
+package com.topjohnwu.magisk.asyncs;
+
+import android.support.v4.app.FragmentActivity;
+
+import com.topjohnwu.magisk.container.ByteArrayStream;
+import com.topjohnwu.magisk.utils.WebService;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Proxy;
+import java.net.HttpURLConnection;
+
+import dalvik.system.DexClassLoader;
+
+public class CheckSafetyNet extends ParallelTask {
+
+ public static final int SNET_VER = 1;
+
+ // Test URL, will switch to proper URL
+ private static final String SNET_URL = "https://www.dropbox.com/s/i5vvbl5eenmag5q/snet-release-unsigned.apk?dl=1";
+ private static final String PKG = "com.topjohnwu.snet";
+
+ private File dexPath;
+ private DexClassLoader loader;
+
+ public CheckSafetyNet(FragmentActivity activity) {
+ super(activity);
+ dexPath = new File(activity.getCacheDir().getParent() + "/snet", "snet.apk");
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (getMagiskManager().snet_version < CheckSafetyNet.SNET_VER) {
+ getShell().sh("rm -rf " + dexPath.getParent());
+ }
+ }
+
+ @Override
+ protected Exception doInBackground(Void... voids) {
+ try {
+ if (!dexPath.exists()) {
+ HttpURLConnection conn = WebService.request(SNET_URL, null);
+ ByteArrayStream bas = new ByteArrayStream();
+ bas.readFrom(conn.getInputStream());
+ conn.disconnect();
+ dexPath.getParentFile().mkdir();
+ try (OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath))) {
+ bas.writeTo(out);
+ out.flush();
+ }
+ }
+ loader = new DexClassLoader(dexPath.toString(), dexPath.getParent(),
+ null, ClassLoader.getSystemClassLoader());
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Exception err) {
+ try {
+ if (err != null) throw err;
+ Class> helperClazz = loader.loadClass(PKG + ".SafetyNetHelper");
+ Class> callbackClazz = loader.loadClass(PKG + ".SafetyNetCallback");
+ Object helper = helperClazz.getConstructors()[0].newInstance(
+ getActivity(), Proxy.newProxyInstance(
+ loader, new Class[] { callbackClazz }, (proxy, method, args) -> {
+ getMagiskManager().safetyNetDone.publish(false, args[0]);
+ return null;
+ }));
+ helperClazz.getMethod("requestTest").invoke(helper);
+ } catch (Exception e) {
+ e.printStackTrace();
+ getMagiskManager().safetyNetDone.publish(false, -1);
+ }
+ super.onPostExecute(err);
+ }
+}
diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
index ec78613b0..ce3d81bef 100644
--- a/app/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
+++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
@@ -8,8 +8,8 @@ import android.text.TextUtils;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.AdaptiveList;
-import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.container.TarEntry;
+import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java
index ef8a2f470..2c1f2d4ec 100644
--- a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java
+++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java
@@ -3,11 +3,10 @@ package com.topjohnwu.magisk.asyncs;
import android.content.Context;
import com.topjohnwu.magisk.MagiskManager;
-import com.topjohnwu.magisk.container.BaseModule;
import com.topjohnwu.magisk.container.Module;
+import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
-import com.topjohnwu.magisk.container.ValueSortedMap;
public class LoadModules extends ParallelTask {
diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java
index 7746d18d0..3afc8ac77 100644
--- a/app/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java
+++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java
@@ -5,7 +5,6 @@ import android.content.SharedPreferences;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.ReposFragment;
-import com.topjohnwu.magisk.container.BaseModule;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.WebService;
diff --git a/app/src/main/java/com/topjohnwu/magisk/container/BaseModule.java b/app/src/main/java/com/topjohnwu/magisk/container/BaseModule.java
index c843999d7..f4e2a811a 100644
--- a/app/src/main/java/com/topjohnwu/magisk/container/BaseModule.java
+++ b/app/src/main/java/com/topjohnwu/magisk/container/BaseModule.java
@@ -5,8 +5,6 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;
-import com.topjohnwu.magisk.utils.Logger;
-
import java.util.List;
public abstract class BaseModule implements Comparable {
diff --git a/app/src/main/java/com/topjohnwu/magisk/container/JarMap.java b/app/src/main/java/com/topjohnwu/magisk/container/JarMap.java
index f66d40b34..81a931316 100644
--- a/app/src/main/java/com/topjohnwu/magisk/container/JarMap.java
+++ b/app/src/main/java/com/topjohnwu/magisk/container/JarMap.java
@@ -1,8 +1,5 @@
package com.topjohnwu.magisk.container;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
diff --git a/app/src/main/java/com/topjohnwu/magisk/container/Module.java b/app/src/main/java/com/topjohnwu/magisk/container/Module.java
index 229e3267a..cbb71a478 100644
--- a/app/src/main/java/com/topjohnwu/magisk/container/Module.java
+++ b/app/src/main/java/com/topjohnwu/magisk/container/Module.java
@@ -1,6 +1,5 @@
package com.topjohnwu.magisk.container;
-import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
diff --git a/app/src/main/java/com/topjohnwu/magisk/container/Repo.java b/app/src/main/java/com/topjohnwu/magisk/container/Repo.java
index 4583e401f..2dd460127 100644
--- a/app/src/main/java/com/topjohnwu/magisk/container/Repo.java
+++ b/app/src/main/java/com/topjohnwu/magisk/container/Repo.java
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
-import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.WebService;
import java.util.Date;
diff --git a/app/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java b/app/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java
index 35a01ff64..4b3518468 100644
--- a/app/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java
+++ b/app/src/main/java/com/topjohnwu/magisk/container/SuLogEntry.java
@@ -4,7 +4,6 @@ import android.content.ContentValues;
import android.database.Cursor;
import com.topjohnwu.magisk.MagiskManager;
-import com.topjohnwu.magisk.container.Policy;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java b/app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java
deleted file mode 100644
index 726fcdcc0..000000000
--- a/app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.topjohnwu.magisk.utils;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.FragmentActivity;
-import android.util.Base64;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.Status;
-import com.google.android.gms.safetynet.SafetyNet;
-import com.topjohnwu.magisk.R;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.security.SecureRandom;
-
-public abstract class SafetyNetHelper
- implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
-
- private static boolean isRunning = false;
-
- private GoogleApiClient mGoogleApiClient;
- private Result ret;
- protected FragmentActivity mActivity;
-
- public SafetyNetHelper(FragmentActivity activity) {
- ret = new Result();
- mActivity = activity;
- }
-
- // Entry point to start test
- public void requestTest() {
- if (isRunning)
- return;
- // Connect Google Service
- mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
- .enableAutoManage(mActivity, this)
- .addApi(SafetyNet.API)
- .addConnectionCallbacks(this)
- .build();
- mGoogleApiClient.connect();
- isRunning = true;
- }
-
- @Override
- public void onConnectionFailed(@NonNull ConnectionResult result) {
- Logger.dev("SN: Google API fail");
- ret.errmsg = result.getErrorMessage();
- handleResults(ret);
- }
-
- @Override
- public void onConnectionSuspended(int i) {
- Logger.dev("SN: Google API Suspended");
- switch (i) {
- case CAUSE_NETWORK_LOST:
- ret.errmsg = mActivity.getString(R.string.safetyNet_network_loss);
- break;
- case CAUSE_SERVICE_DISCONNECTED:
- ret.errmsg = mActivity.getString(R.string.safetyNet_service_disconnected);
- break;
- }
- handleResults(ret);
- }
-
- @Override
- public void onConnected(@Nullable Bundle bundle) {
- Logger.dev("SN: Google API Connected");
- // Create nonce
- byte[] nonce = new byte[24];
- new SecureRandom().nextBytes(nonce);
-
- Logger.dev("SN: Check with nonce: " + Base64.encodeToString(nonce, Base64.DEFAULT));
-
- // Call SafetyNet
- SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
- .setResultCallback(result -> {
- Status status = result.getStatus();
- if (status.isSuccess()) {
- String json = new String(Base64.decode(result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
- Logger.dev("SN: Response: " + json);
- try {
- JSONObject decoded = new JSONObject(json);
- ret.ctsProfile = decoded.getBoolean("ctsProfileMatch");
- ret.basicIntegrity = decoded.getBoolean("basicIntegrity");
- ret.failed = false;
- } catch (JSONException e) {
- ret.errmsg = mActivity.getString(R.string.safetyNet_res_invalid);
- }
- } else {
- Logger.dev("SN: No response");
- ret.errmsg = mActivity.getString(R.string.safetyNet_no_response);
- }
- // Disconnect
- mGoogleApiClient.stopAutoManage(mActivity);
- mGoogleApiClient.disconnect();
- isRunning = false;
- handleResults(ret);
- });
- }
-
- // Callback function to save the results
- public abstract void handleResults(Result result);
-
- public static class Result {
- public boolean failed = true;
- public String errmsg;
- public boolean ctsProfile = false;
- public boolean basicIntegrity = false;
- }
-}
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Topic.java b/app/src/main/java/com/topjohnwu/magisk/utils/Topic.java
index 4864a63b8..66e8e2960 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/Topic.java
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/Topic.java
@@ -31,15 +31,19 @@ public class Topic {
}
public void publish() {
- publish(true);
+ publish(true, null);
}
public void publish(boolean record) {
+ publish(record, null);
+ }
+
+ public void publish(boolean record, Object result) {
hasPublished = record;
if (subscribers != null) {
for (WeakReference subscriber : subscribers) {
if (subscriber.get() != null)
- subscriber.get().onTopicPublished(this);
+ subscriber.get().onTopicPublished(this, result);
}
}
}
@@ -60,9 +64,12 @@ public class Topic {
}
}
default void onTopicPublished() {
- onTopicPublished(null);
+ onTopicPublished(null, null);
}
- void onTopicPublished(Topic topic);
+ default void onTopicPublished(Topic topic) {
+ onTopicPublished(topic, null);
+ }
+ void onTopicPublished(Topic topic, Object result);
Topic[] getSubscription();
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
index c3c566eb5..b049f988b 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
@@ -22,7 +22,6 @@ import android.provider.OpenableColumns;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.FragmentActivity;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
@@ -150,16 +149,6 @@ public class Utils {
return (MagiskManager) context.getApplicationContext();
}
- public static void checkSafetyNet(FragmentActivity activity) {
- new SafetyNetHelper(activity) {
- @Override
- public void handleResults(Result result) {
- getMagiskManager(mActivity).SNCheckResult = result;
- getMagiskManager(mActivity).safetyNetDone.publish(false);
- }
- }.requestTest();
- }
-
public static void clearRepoCache(Context context) {
MagiskManager mm = getMagiskManager(context);
mm.prefs.edit().remove(UpdateRepos.ETAG_KEY).apply();
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 89871cbd2..54e858d2f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,7 +22,8 @@
Not rooted
Tap to start SafetyNet check
Checking SafetyNet status…
- SafetyNet Check Was Successful
+ SafetyNet Check Success
+ SafetyNet API Error
Cannot verify SafetyNet, no internet?
Network connection unavailable
Service has been killed
@@ -123,6 +124,8 @@
Restoration done!
Stock backup does not exist!
Uninstalling Magisk Manager in 5 seconds, please manually reboot afterwards
+ Download Proprietary Code
+ Magisk Manager is FOSS so doesn\'t contain Google\'s proprietary SafetyNet API code.\n\nDo you allow Magisk Manager to download an extension (contains GoogleApiClient) for SafetyNet checks?
General
diff --git a/build.gradle b/build.gradle
index 998838f50..c84f2b629 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,6 +5,7 @@ buildscript {
jcenter()
mavenCentral()
maven { url "https://maven.google.com" }
+ google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-beta7'
@@ -18,6 +19,7 @@ allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
+ google()
}
}
diff --git a/settings.gradle b/settings.gradle
index 1cf7f1e5e..3c11aabee 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':unhide', ':resource'
\ No newline at end of file
+include ':app', ':unhide', ':resource', ':snet'
\ No newline at end of file
diff --git a/snet/.gitignore b/snet/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/snet/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/snet/build.gradle b/snet/build.gradle
new file mode 100644
index 000000000..e41372b04
--- /dev/null
+++ b/snet/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 26
+ buildToolsVersion "26.0.2"
+
+ defaultConfig {
+ applicationId "com.topjohnwu.sn"
+ minSdkVersion 21
+ targetSdkVersion 26
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.google.android.gms:play-services-safetynet:11.4.2'
+}
diff --git a/snet/proguard-rules.pro b/snet/proguard-rules.pro
new file mode 100644
index 000000000..d6e59f6f5
--- /dev/null
+++ b/snet/proguard-rules.pro
@@ -0,0 +1,24 @@
+# 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
+
+-keep class com.topjohnwu.snet.SafetyNet* { *; }
+-dontwarn java.lang.invoke**
diff --git a/snet/src/main/AndroidManifest.xml b/snet/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..4fa18362e
--- /dev/null
+++ b/snet/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/snet/src/main/java/com/topjohnwu/snet/SafetyNetCallback.java b/snet/src/main/java/com/topjohnwu/snet/SafetyNetCallback.java
new file mode 100644
index 000000000..fa680422d
--- /dev/null
+++ b/snet/src/main/java/com/topjohnwu/snet/SafetyNetCallback.java
@@ -0,0 +1,5 @@
+package com.topjohnwu.snet;
+
+public interface SafetyNetCallback {
+ void onResponse(int responseCode);
+}
diff --git a/snet/src/main/java/com/topjohnwu/snet/SafetyNetHelper.java b/snet/src/main/java/com/topjohnwu/snet/SafetyNetHelper.java
new file mode 100644
index 000000000..6cc35a192
--- /dev/null
+++ b/snet/src/main/java/com/topjohnwu/snet/SafetyNetHelper.java
@@ -0,0 +1,118 @@
+package com.topjohnwu.snet;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Base64;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.safetynet.SafetyNet;
+import com.google.android.gms.safetynet.SafetyNetApi;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.reflect.Method;
+import java.security.SecureRandom;
+
+public class SafetyNetHelper
+ implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+ public static final int CONNECTION_FAIL = -1;
+
+ public static final int CAUSE_SERVICE_DISCONNECTED = 0x00001;
+ public static final int CAUSE_NETWORK_LOST = 0x00010;
+ public static final int RESPONSE_ERR = 0x00100;
+
+ public static final int BASIC_PASS = 0x01000;
+ public static final int CTS_PASS = 0x10000;
+
+ private GoogleApiClient mGoogleApiClient;
+ private Context mActivity;
+ private int responseCode;
+ private SafetyNetCallback cb;
+
+ public SafetyNetHelper(Context context, SafetyNetCallback cb) {
+ mActivity = context;
+ this.cb = cb;
+ responseCode = 0;
+ }
+
+ // Entry point to start test
+ public void requestTest() {
+ // Connect Google Service
+ GoogleApiClient.Builder builder = new GoogleApiClient.Builder(mActivity);
+ try {
+ // Use reflection to workaround FragmentActivity crap
+ Class> clazz = Class.forName("com.google.android.gms.common.api.GoogleApiClient$Builder");
+ for (Method m : clazz.getMethods()) {
+ if (m.getName().equals("enableAutoManage")) {
+ m.invoke(builder, mActivity, this);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ mGoogleApiClient = builder.addApi(SafetyNet.API).addConnectionCallbacks(this).build();
+ mGoogleApiClient.connect();
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+ cb.onResponse(i);
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ cb.onResponse(CONNECTION_FAIL);
+ }
+
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+ // Create nonce
+ byte[] nonce = new byte[24];
+ new SecureRandom().nextBytes(nonce);
+
+ // Call SafetyNet
+ SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
+ .setResultCallback(new ResultCallback() {
+ @Override
+ public void onResult(@NonNull SafetyNetApi.AttestationResult result) {
+ Status status = result.getStatus();
+ try {
+ if (!status.isSuccess()) throw new JSONException("");
+ String json = new String(Base64.decode(
+ result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
+ JSONObject decoded = new JSONObject(json);
+ responseCode |= decoded.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
+ responseCode |= decoded.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
+ } catch (JSONException e) {
+ responseCode |= RESPONSE_ERR;
+ return;
+ }
+ // Disconnect
+ try {
+ // Use reflection to workaround FragmentActivity crap
+ Class> clazz = Class.forName("com.google.android.gms.common.api.GoogleApiClient");
+ for (Method m : clazz.getMethods()) {
+ if (m.getName().equals("stopAutoManage")) {
+ m.invoke(mGoogleApiClient, mActivity, this);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ mGoogleApiClient.disconnect();
+
+ // Return results
+ cb.onResponse(responseCode);
+ }
+ });
+ }
+}