diff --git a/app/build.gradle b/app/build.gradle index bf6e15e12..882747298 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { defaultConfig { applicationId "com.topjohnwu.magisk" - minSdkVersion 21 targetSdkVersion rootProject.ext.compileSdkVersion + vectorDrawables.useSupportLibrary = true } signingConfigs { @@ -41,6 +41,7 @@ android { productFlavors { full { + minSdkVersion 21 versionName configProps['appVersion'] versionCode configProps['appVersionCode'] as Integer javaCompileOptions { @@ -50,6 +51,7 @@ android { } } stub { + minSdkVersion 17 versionCode 1 versionName "stub" } diff --git a/app/src/full/java/com/topjohnwu/magisk/MainActivity.java b/app/src/full/java/com/topjohnwu/magisk/MainActivity.java index 344eb0f37..df17e2d2a 100644 --- a/app/src/full/java/com/topjohnwu/magisk/MainActivity.java +++ b/app/src/full/java/com/topjohnwu/magisk/MainActivity.java @@ -20,7 +20,7 @@ import com.topjohnwu.magisk.fragments.ModulesFragment; import com.topjohnwu.magisk.fragments.ReposFragment; import com.topjohnwu.magisk.fragments.SettingsFragment; import com.topjohnwu.magisk.fragments.SuperuserFragment; -import com.topjohnwu.magisk.utils.Download; +import com.topjohnwu.net.Networking; import com.topjohnwu.superuser.Shell; import androidx.annotation.NonNull; @@ -124,7 +124,7 @@ public class MainActivity extends BaseActivity menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() && app.prefs.getBoolean(Const.Key.MAGISKHIDE, false)); menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0); - menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this) + menu.findItem(R.id.downloads).setVisible(Networking.checkNetworkStatus(this) && Shell.rootAccess() && Data.magiskVersionCode >= 0); menu.findItem(R.id.log).setVisible(Shell.rootAccess()); menu.findItem(R.id.superuser).setVisible(Utils.showSuperUser()); diff --git a/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java b/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java index fb8e7cac7..efeca53b2 100644 --- a/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java +++ b/app/src/full/java/com/topjohnwu/magisk/SplashActivity.java @@ -15,7 +15,7 @@ import com.topjohnwu.magisk.components.BaseActivity; import com.topjohnwu.magisk.components.Notifications; import com.topjohnwu.magisk.receivers.ShortcutReceiver; import com.topjohnwu.magisk.utils.AppUtils; -import com.topjohnwu.magisk.utils.Download; +import com.topjohnwu.net.Networking; import com.topjohnwu.superuser.Shell; public class SplashActivity extends BaseActivity { @@ -56,7 +56,7 @@ public class SplashActivity extends BaseActivity { // Setup shortcuts sendBroadcast(new Intent(this, ClassMap.get(ShortcutReceiver.class))); - if (Download.checkNetworkStatus(this)) { + if (Networking.checkNetworkStatus(this)) { // Fire update check CheckUpdates.check(); // Repo update check diff --git a/app/src/full/java/com/topjohnwu/magisk/fragments/MagiskFragment.java b/app/src/full/java/com/topjohnwu/magisk/fragments/MagiskFragment.java index 2104f1d42..5f45eabdb 100644 --- a/app/src/full/java/com/topjohnwu/magisk/fragments/MagiskFragment.java +++ b/app/src/full/java/com/topjohnwu/magisk/fragments/MagiskFragment.java @@ -32,7 +32,7 @@ import com.topjohnwu.magisk.components.ExpandableView; import com.topjohnwu.magisk.components.MagiskInstallDialog; import com.topjohnwu.magisk.components.ManagerInstallDialog; import com.topjohnwu.magisk.components.UninstallDialog; -import com.topjohnwu.magisk.utils.Download; +import com.topjohnwu.net.Networking; import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.ShellUtils; @@ -169,7 +169,7 @@ public class MagiskFragment extends BaseFragment shownDialog = false; // Trigger state check - if (Download.checkNetworkStatus(app)) { + if (Networking.checkNetworkStatus(app)) { CheckUpdates.check(); } else { mSwipeRefreshLayout.setRefreshing(false); @@ -212,7 +212,7 @@ public class MagiskFragment extends BaseFragment private void updateUI() { ((MainActivity) requireActivity()).checkHideSection(); - boolean hasNetwork = Download.checkNetworkStatus(app); + boolean hasNetwork = Networking.checkNetworkStatus(app); boolean hasRoot = Shell.rootAccess(); magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE); diff --git a/app/src/full/java/com/topjohnwu/magisk/fragments/SettingsFragment.java b/app/src/full/java/com/topjohnwu/magisk/fragments/SettingsFragment.java index 137b9310c..84ef33fd4 100644 --- a/app/src/full/java/com/topjohnwu/magisk/fragments/SettingsFragment.java +++ b/app/src/full/java/com/topjohnwu/magisk/fragments/SettingsFragment.java @@ -20,10 +20,10 @@ import com.topjohnwu.core.utils.Utils; import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.utils.AppUtils; -import com.topjohnwu.magisk.utils.Download; import com.topjohnwu.magisk.utils.DownloadApp; import com.topjohnwu.magisk.utils.FingerprintHelper; import com.topjohnwu.magisk.utils.PatchAPK; +import com.topjohnwu.net.Networking; import com.topjohnwu.superuser.Shell; import java.io.IOException; @@ -155,7 +155,7 @@ public class SettingsFragment extends PreferenceFragmentCompat if (app.getPackageName().equals(BuildConfig.APPLICATION_ID)) { generalCatagory.removePreference(restoreManager); } else { - if (!Download.checkNetworkStatus(app)) + if (!Networking.checkNetworkStatus(app)) generalCatagory.removePreference(restoreManager); generalCatagory.removePreference(hideManager); } diff --git a/app/src/main/res/drawable-v26/ic_launcher.xml b/app/src/full/res/drawable-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/drawable-v26/ic_launcher.xml rename to app/src/full/res/drawable-v26/ic_launcher.xml diff --git a/app/src/main/res/drawable/ic_magisk_padded.xml b/app/src/full/res/drawable/ic_magisk_padded.xml similarity index 100% rename from app/src/main/res/drawable/ic_magisk_padded.xml rename to app/src/full/res/drawable/ic_magisk_padded.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 79f8345f9..17f5675a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,8 +4,6 @@ package="com.topjohnwu.magisk"> - - diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Download.java b/app/src/main/java/com/topjohnwu/magisk/utils/Download.java deleted file mode 100644 index 91002ad8c..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Download.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.topjohnwu.magisk.utils; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; - -public class Download { - - public static boolean checkNetworkStatus(Context context) { - ConnectivityManager manager = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = manager.getActiveNetworkInfo(); - return networkInfo != null && networkInfo.isConnected(); - } -} diff --git a/app/src/main/res/values/drawables.xml b/app/src/main/res/values-v21/drawables.xml similarity index 100% rename from app/src/main/res/values/drawables.xml rename to app/src/main/res/values-v21/drawables.xml diff --git a/app/src/stub/java/com/topjohnwu/magisk/MainActivity.java b/app/src/stub/java/com/topjohnwu/magisk/MainActivity.java index 64b081d06..93ee2ec1f 100644 --- a/app/src/stub/java/com/topjohnwu/magisk/MainActivity.java +++ b/app/src/stub/java/com/topjohnwu/magisk/MainActivity.java @@ -6,7 +6,6 @@ import android.app.Application; import android.os.Bundle; import com.topjohnwu.magisk.utils.APKInstall; -import com.topjohnwu.magisk.utils.Download; import com.topjohnwu.net.Networking; import com.topjohnwu.net.ResponseListener; @@ -32,7 +31,8 @@ public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Download.checkNetworkStatus(this)) { + Networking.init(this); + if (Networking.checkNetworkStatus(this)) { Networking.get(URL) .setErrorHandler(((conn, e) -> finish())) .getAsJSONObject(new JSONLoader()); diff --git a/app/src/stub/res/values/drawables.xml b/app/src/stub/res/values/drawables.xml new file mode 100644 index 000000000..5242b5d7d --- /dev/null +++ b/app/src/stub/res/values/drawables.xml @@ -0,0 +1,4 @@ + + + @android:drawable/sym_def_app_icon + \ No newline at end of file diff --git a/core/src/main/java/com/topjohnwu/core/App.java b/core/src/main/java/com/topjohnwu/core/App.java index b133768f4..a07d7274e 100644 --- a/core/src/main/java/com/topjohnwu/core/App.java +++ b/core/src/main/java/com/topjohnwu/core/App.java @@ -12,6 +12,7 @@ import com.topjohnwu.core.database.MagiskDB; import com.topjohnwu.core.database.RepoDatabaseHelper; import com.topjohnwu.core.utils.LocaleManager; import com.topjohnwu.core.utils.RootUtils; +import com.topjohnwu.net.Networking; import com.topjohnwu.superuser.ContainerApp; import com.topjohnwu.superuser.Shell; @@ -46,6 +47,7 @@ public class App extends ContainerApp { mDB = new MagiskDB(this); repoDB = new RepoDatabaseHelper(this); + Networking.init(this); LocaleManager.setLocale(this); Data.loadConfig(); } diff --git a/net/src/main/AndroidManifest.xml b/net/src/main/AndroidManifest.xml index b8128f247..8a07fd122 100644 --- a/net/src/main/AndroidManifest.xml +++ b/net/src/main/AndroidManifest.xml @@ -1,2 +1,5 @@ + package="com.topjohnwu.net"> + + + diff --git a/net/src/main/java/com/topjohnwu/net/Networking.java b/net/src/main/java/com/topjohnwu/net/Networking.java index fb4a7630e..d55f80eb5 100644 --- a/net/src/main/java/com/topjohnwu/net/Networking.java +++ b/net/src/main/java/com/topjohnwu/net/Networking.java @@ -1,12 +1,17 @@ package com.topjohnwu.net; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build; 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; + +import javax.net.ssl.HttpsURLConnection; public class Networking { @@ -30,4 +35,26 @@ public class Networking { return request(url, "GET"); } + public static void init(Context context) { + try { + // Try installing new SSL provider from Google Play Service + Context gms = context.createPackageContext("com.google.android.gms", + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + gms.getClassLoader() + .loadClass("com.google.android.gms.common.security.ProviderInstallerImpl") + .getMethod("insertProvider", Context.class) + .invoke(null, context); + } catch (Exception e) { + // Failed to update SSL provider, use NoSSLv3SocketFactory on SDK < 21 + if (Build.VERSION.SDK_INT < 21) + HttpsURLConnection.setDefaultSSLSocketFactory(new NoSSLv3SocketFactory()); + } + } + + public static boolean checkNetworkStatus(Context context) { + ConnectivityManager manager = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = manager.getActiveNetworkInfo(); + return networkInfo != null && networkInfo.isConnected(); + } } diff --git a/net/src/main/java/com/topjohnwu/net/NoSSLv3SocketFactory.java b/net/src/main/java/com/topjohnwu/net/NoSSLv3SocketFactory.java new file mode 100644 index 000000000..5af965938 --- /dev/null +++ b/net/src/main/java/com/topjohnwu/net/NoSSLv3SocketFactory.java @@ -0,0 +1,70 @@ +package com.topjohnwu.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +class NoSSLv3SocketFactory extends SSLSocketFactory { + + private final static SSLSocketFactory base = HttpsURLConnection.getDefaultSSLSocketFactory(); + + @Override + public String[] getDefaultCipherSuites() { + return base.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return base.getSupportedCipherSuites(); + } + + private Socket createSafeSocket(Socket socket) { + if (socket instanceof SSLSocket) + return new SSLSocketWrapper((SSLSocket) socket) { + @Override + public void setEnabledProtocols(String[] protocols) { + List proto = new ArrayList<>(Arrays.asList(getSupportedProtocols())); + proto.remove("SSLv3"); + super.setEnabledProtocols(proto.toArray(new String[0])); + } + }; + return socket; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return createSafeSocket(base.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket() throws IOException { + return createSafeSocket(base.createSocket()); + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + return createSafeSocket(base.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + return createSafeSocket(base.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return createSafeSocket(base.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return createSafeSocket(base.createSocket(address, port, localAddress, localPort)); + } +} diff --git a/net/src/main/java/com/topjohnwu/net/SSLSocketWrapper.java b/net/src/main/java/com/topjohnwu/net/SSLSocketWrapper.java new file mode 100644 index 000000000..a86759c17 --- /dev/null +++ b/net/src/main/java/com/topjohnwu/net/SSLSocketWrapper.java @@ -0,0 +1,333 @@ +package com.topjohnwu.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +class SSLSocketWrapper extends SSLSocket { + + private SSLSocket mBase; + + SSLSocketWrapper(SSLSocket socket) { + mBase = socket; + } + + @Override + public String[] getSupportedCipherSuites() { + return mBase.getSupportedCipherSuites(); + } + + @Override + public String[] getEnabledCipherSuites() { + return mBase.getEnabledCipherSuites(); + } + + @Override + public void setEnabledCipherSuites(String[] suites) { + mBase.setEnabledCipherSuites(suites); + } + + @Override + public String[] getSupportedProtocols() { + return mBase.getSupportedProtocols(); + } + + @Override + public String[] getEnabledProtocols() { + return mBase.getEnabledProtocols(); + } + + @Override + public void setEnabledProtocols(String[] protocols) { + mBase.setEnabledProtocols(protocols); + } + + @Override + public SSLSession getSession() { + return mBase.getSession(); + } + + @Override + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + @Override + public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { + mBase.addHandshakeCompletedListener(listener); + } + + @Override + public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { + mBase.removeHandshakeCompletedListener(listener); + } + + @Override + public void startHandshake() throws IOException { + mBase.startHandshake(); + } + + @Override + public void setUseClientMode(boolean mode) { + mBase.setUseClientMode(mode); + } + + @Override + public boolean getUseClientMode() { + return mBase.getUseClientMode(); + } + + @Override + public void setNeedClientAuth(boolean need) { + mBase.setNeedClientAuth(need); + } + + @Override + public boolean getNeedClientAuth() { + return mBase.getNeedClientAuth(); + } + + @Override + public void setWantClientAuth(boolean want) { + mBase.setWantClientAuth(want); + } + + @Override + public boolean getWantClientAuth() { + return mBase.getWantClientAuth(); + } + + @Override + public void setEnableSessionCreation(boolean flag) { + mBase.setEnableSessionCreation(flag); + } + + @Override + public boolean getEnableSessionCreation() { + return mBase.getEnableSessionCreation(); + } + + @Override + public SSLParameters getSSLParameters() { + return mBase.getSSLParameters(); + } + + @Override + public void setSSLParameters(SSLParameters params) { + mBase.setSSLParameters(params); + } + + @Override + public String toString() { + return mBase.toString(); + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + mBase.connect(endpoint); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + mBase.connect(endpoint, timeout); + } + + @Override + public void bind(SocketAddress bindpoint) throws IOException { + mBase.bind(bindpoint); + } + + @Override + public InetAddress getInetAddress() { + return mBase.getInetAddress(); + } + + @Override + public InetAddress getLocalAddress() { + return mBase.getLocalAddress(); + } + + @Override + public int getPort() { + return mBase.getPort(); + } + + @Override + public int getLocalPort() { + return mBase.getLocalPort(); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return mBase.getRemoteSocketAddress(); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return mBase.getLocalSocketAddress(); + } + + @Override + public SocketChannel getChannel() { + return mBase.getChannel(); + } + + @Override + public InputStream getInputStream() throws IOException { + return mBase.getInputStream(); + } + + @Override + public OutputStream getOutputStream() throws IOException { + return mBase.getOutputStream(); + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + mBase.setTcpNoDelay(on); + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + return mBase.getTcpNoDelay(); + } + + @Override + public void setSoLinger(boolean on, int linger) throws SocketException { + mBase.setSoLinger(on, linger); + } + + @Override + public int getSoLinger() throws SocketException { + return mBase.getSoLinger(); + } + + @Override + public void sendUrgentData(int data) throws IOException { + mBase.sendUrgentData(data); + } + + @Override + public void setOOBInline(boolean on) throws SocketException { + mBase.setOOBInline(on); + } + + @Override + public boolean getOOBInline() throws SocketException { + return mBase.getOOBInline(); + } + + @Override + public void setSoTimeout(int timeout) throws SocketException { + mBase.setSoTimeout(timeout); + } + + @Override + public int getSoTimeout() throws SocketException { + return mBase.getSoTimeout(); + } + + @Override + public void setSendBufferSize(int size) throws SocketException { + mBase.setSendBufferSize(size); + } + + @Override + public int getSendBufferSize() throws SocketException { + return mBase.getSendBufferSize(); + } + + @Override + public void setReceiveBufferSize(int size) throws SocketException { + mBase.setReceiveBufferSize(size); + } + + @Override + public int getReceiveBufferSize() throws SocketException { + return mBase.getReceiveBufferSize(); + } + + @Override + public void setKeepAlive(boolean on) throws SocketException { + mBase.setKeepAlive(on); + } + + @Override + public boolean getKeepAlive() throws SocketException { + return mBase.getKeepAlive(); + } + + @Override + public void setTrafficClass(int tc) throws SocketException { + mBase.setTrafficClass(tc); + } + + @Override + public int getTrafficClass() throws SocketException { + return mBase.getTrafficClass(); + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + mBase.setReuseAddress(on); + } + + @Override + public boolean getReuseAddress() throws SocketException { + return mBase.getReuseAddress(); + } + + @Override + public void close() throws IOException { + mBase.close(); + } + + @Override + public void shutdownInput() throws IOException { + mBase.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + mBase.shutdownOutput(); + } + + @Override + public boolean isConnected() { + return mBase.isConnected(); + } + + @Override + public boolean isBound() { + return mBase.isBound(); + } + + @Override + public boolean isClosed() { + return mBase.isClosed(); + } + + @Override + public boolean isInputShutdown() { + return mBase.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return mBase.isOutputShutdown(); + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + mBase.setPerformancePreferences(connectionTime, latency, bandwidth); + } +}