Seperate Google proprietary code

This commit is contained in:
topjohnwu
2017-10-07 17:12:36 +08:00
parent d4798b02ac
commit 42a6e0dd10
33 changed files with 361 additions and 190 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -102,7 +102,7 @@ public class MainActivity extends Activity
}
@Override
public void onTopicPublished(Topic topic) {
public void onTopicPublished(Topic topic, Object result) {
recreate();
}

View File

@ -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();
}

View File

@ -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);

View File

@ -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"));
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<Void, Void, Exception> {
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);
}
}

View File

@ -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;

View File

@ -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<Void, Void, Void> {

View File

@ -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;

View File

@ -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<BaseModule> {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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> 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();
}
}

View File

@ -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();