Remove compatibility code for pre-API 19 versions

This commit is contained in:
cketti 2018-12-16 02:23:35 +01:00
parent ea13abb02d
commit b5588a8d02
16 changed files with 16 additions and 402 deletions

View file

@ -14,7 +14,6 @@ import com.fsck.k9.crypto.openPgpModule
import com.fsck.k9.job.K9JobManager
import com.fsck.k9.job.jobModule
import com.fsck.k9.mail.internet.BinaryTempFileBody
import com.fsck.k9.mail.ssl.LocalKeyStore
import com.fsck.k9.mailstore.mailStoreModule
import com.fsck.k9.message.extractors.extractorModule
import com.fsck.k9.message.html.htmlModule
@ -59,8 +58,6 @@ object Core : KoinComponent {
StrictMode.enableDefaults()
}
PRNGFixes.apply()
val packageName = context.packageName
K9.Intents.init(packageName)
}

View file

@ -1,290 +0,0 @@
/*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will Google be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, as long as the origin is not misrepresented.
*/
package com.fsck.k9;
import android.os.Build;
import android.os.Process;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
/**
* Fixes for the output of the default PRNG having low entropy.
*
* The fixes need to be applied via {@link #apply()} before any use of Java
* Cryptography Architecture primitives. A good place to invoke them is in the
* application's {@code onCreate}.
*/
public final class PRNGFixes {
private static final int VERSION_CODE_JELLY_BEAN = 16;
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
getBuildFingerprintAndDeviceSerial();
/** Hidden constructor to prevent instantiation. */
private PRNGFixes() {}
/**
* Applies all fixes.
*
* @throws SecurityException if a fix is needed but could not be applied.
*/
public static void apply() {
applyOpenSSLFix();
installLinuxPRNGSecureRandom();
}
/**
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
* fix is not needed.
*
* @throws SecurityException if the fix is needed but could not be applied.
*/
private static void applyOpenSSLFix() throws SecurityException {
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
// No need to apply the fix
return;
}
try {
// Mix in the device- and invocation-specific seed.
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, (Object) generateSeed());
// Mix output of Linux PRNG into OpenSSL's PRNG
int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_load_file", String.class, long.class)
.invoke(null, "/dev/urandom", 1024);
if (bytesRead != 1024) {
throw new IOException(
"Unexpected number of bytes read from Linux PRNG: "
+ bytesRead);
}
} catch (Exception e) {
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
}
}
/**
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
* default. Does nothing if the implementation is already the default or if
* there is not need to install the implementation.
*
* @throws SecurityException if the fix is needed but could not be applied.
*/
private static void installLinuxPRNGSecureRandom()
throws SecurityException {
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
// No need to apply the fix
return;
}
// Install a Linux PRNG-based SecureRandom implementation as the
// default, if not yet installed.
Provider[] secureRandomProviders =
Security.getProviders("SecureRandom.SHA1PRNG");
if ((secureRandomProviders == null)
|| (secureRandomProviders.length < 1)
|| (!LinuxPRNGSecureRandomProvider.class.equals(
secureRandomProviders[0].getClass()))) {
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
}
// Assert that new SecureRandom() and
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
// by the Linux PRNG-based SecureRandom implementation.
SecureRandom rng1 = new SecureRandom();
if (!LinuxPRNGSecureRandomProvider.class.equals(
rng1.getProvider().getClass())) {
throw new SecurityException(
"new SecureRandom() backed by wrong Provider: "
+ rng1.getProvider().getClass());
}
SecureRandom rng2;
try {
rng2 = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException("SHA1PRNG not available", e);
}
if (!LinuxPRNGSecureRandomProvider.class.equals(
rng2.getProvider().getClass())) {
throw new SecurityException(
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+ " Provider: " + rng2.getProvider().getClass());
}
}
/**
* {@code Provider} of {@code SecureRandom} engines which pass through
* all requests to the Linux PRNG.
*/
private static class LinuxPRNGSecureRandomProvider extends Provider {
private static final long serialVersionUID = 6538669771360998378L;
public LinuxPRNGSecureRandomProvider() {
super("LinuxPRNG",
1.0,
"A Linux-specific random number provider that uses"
+ " /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some apps
// explicitly request a SHA1PRNG SecureRandom and we thus need to
// prevent them from getting the default implementation whose output
// may have low entropy.
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}
/**
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
* ({@code /dev/urandom}).
*/
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/*
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
* are passed through to the Linux PRNG (/dev/urandom). Instances of
* this class seed themselves by mixing in the current time, PID, UID,
* build fingerprint, and hardware serial number (where available) into
* Linux PRNG.
*
* Concurrency: Read requests to the underlying Linux PRNG are
* serialized (on sLock) to ensure that multiple threads do not get
* duplicated PRNG output.
*/
private static final long serialVersionUID = 628140755730642770L;
private static final File URANDOM_FILE = new File("/dev/urandom");
private static final Object sLock = new Object();
/**
* Input stream for reading from Linux PRNG or {@code null} if not yet
* opened.
*
* @GuardedBy("sLock")
*/
private static DataInputStream sUrandomIn;
@Override
protected void engineSetSeed(byte[] bytes) {
// We use /dev/urandom so we don't need to worry about seeding
}
@Override
protected void engineNextBytes(byte[] bytes) {
try {
DataInputStream in;
synchronized (sLock) {
in = getUrandomInputStream();
}
synchronized (in) {
in.readFully(bytes);
}
} catch (IOException e) {
throw new SecurityException(
"Failed to read from " + URANDOM_FILE, e);
}
}
@Override
protected byte[] engineGenerateSeed(int size) {
byte[] seed = new byte[size];
engineNextBytes(seed);
return seed;
}
private DataInputStream getUrandomInputStream() {
synchronized (sLock) {
if (sUrandomIn == null) {
// NOTE: Consider inserting a BufferedInputStream between
// DataInputStream and FileInputStream if you need higher
// PRNG output performance and can live with future PRNG
// output being pulled into this process prematurely.
try {
sUrandomIn = new DataInputStream(
new FileInputStream(URANDOM_FILE));
} catch (IOException e) {
throw new SecurityException("Failed to open "
+ URANDOM_FILE + " for reading", e);
}
}
return sUrandomIn;
}
}
}
/**
* Generates a device- and invocation-specific seed to be mixed into the
* Linux PRNG.
*/
private static byte[] generateSeed() {
try {
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
DataOutputStream seedBufferOut =
new DataOutputStream(seedBuffer);
seedBufferOut.writeLong(System.currentTimeMillis());
seedBufferOut.writeLong(System.nanoTime());
seedBufferOut.writeInt(Process.myPid());
seedBufferOut.writeInt(Process.myUid());
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
seedBufferOut.close();
return seedBuffer.toByteArray();
} catch (IOException e) {
throw new SecurityException("Failed to generate seed", e);
}
}
/**
* Gets the hardware serial number of this device.
*
* @return serial number or {@code null} if not available.
*/
private static String getDeviceSerialNumber() {
// We're using the Reflection API because Build.SERIAL is only available
// since API Level 9 (Gingerbread, Android 2.3).
try {
return (String) Build.class.getField("SERIAL").get(null);
} catch (Exception ignored) {
return null;
}
}
private static byte[] getBuildFingerprintAndDeviceSerial() {
StringBuilder result = new StringBuilder();
String fingerprint = Build.FINGERPRINT;
if (fingerprint != null) {
result.append(fingerprint);
}
String serial = getDeviceSerialNumber();
if (serial != null) {
result.append(serial);
}
return result.toString().getBytes(Charset.forName("UTF-8"));
}
}

View file

@ -1,8 +1,6 @@
package com.fsck.k9.helper;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@ -54,7 +52,7 @@ public class RetainFragment<T> extends Fragment {
data = null;
cleared = true;
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1 && fm.isDestroyed()) {
if (fm.isDestroyed()) {
return;
}

View file

@ -1,20 +1,19 @@
package com.fsck.k9.mailstore;
import java.io.File;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import android.annotation.TargetApi;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Build;
import timber.log.Timber;
import com.fsck.k9.K9;
import com.fsck.k9.helper.FileHelper;
import com.fsck.k9.mail.MessagingException;
import timber.log.Timber;
import static java.lang.System.currentTimeMillis;
@ -502,15 +501,8 @@ public class LockableDatabase {
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void deleteDatabase(File database) {
boolean deleted;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
deleted = SQLiteDatabase.deleteDatabase(database);
} else {
deleted = database.delete();
deleted |= new File(database.getPath() + "-journal").delete();
}
boolean deleted = SQLiteDatabase.deleteDatabase(database);
if (!deleted) {
Timber.i("LockableDatabase: deleteDatabase(): No files deleted.");
}

View file

@ -20,7 +20,6 @@ import com.fsck.k9.NotificationSetting;
import com.fsck.k9.controller.MessageReference;
import static com.fsck.k9.notification.NotificationHelper.NOTIFICATION_LED_BLINK_SLOW;
import static com.fsck.k9.notification.NotificationController.platformSupportsExtendedNotifications;
class DeviceNotifications extends BaseNotifications {
@ -41,7 +40,7 @@ class DeviceNotifications extends BaseNotifications {
int unreadMessageCount = notificationData.getUnreadMessageCount();
NotificationCompat.Builder builder;
if (isPrivacyModeActive() || !platformSupportsExtendedNotifications()) {
if (isPrivacyModeActive()) {
builder = createSimpleSummaryNotification(account, unreadMessageCount);
} else if (notificationData.isSingleMessageNotification()) {
NotificationHolder holder = notificationData.getHolderForLatestNotification();

View file

@ -17,10 +17,6 @@ public class NotificationController {
private final NewMailNotifications newMailNotifications;
public static boolean platformSupportsExtendedNotifications() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
public static boolean platformSupportsLockScreenNotifications() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}

View file

@ -18,7 +18,6 @@
-dontwarn com.squareup.moshi.**
# Project specific rules
-dontnote com.fsck.k9.PRNGFixes
-dontnote com.fsck.k9.ui.messageview.**
-dontnote com.fsck.k9.view.**

View file

@ -13,7 +13,6 @@ import android.content.IntentSender.SendIntentException;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.FragmentManager;
@ -973,9 +972,6 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
} else if (id == R.id.copy || id == R.id.refile_copy) {
messageViewFragment.onCopy();
return true;
} else if (id == R.id.select_text) {
messageViewFragment.onSelectText();
return true;
} else if (id == R.id.show_headers || id == R.id.hide_headers) {
messageViewFragment.onToggleAllHeadersView();
updateMenu();
@ -1052,7 +1048,6 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
menu.findItem(R.id.spam).setVisible(false);
menu.findItem(R.id.refile).setVisible(false);
menu.findItem(R.id.toggle_unread).setVisible(false);
menu.findItem(R.id.select_text).setVisible(false);
menu.findItem(R.id.toggle_message_view_theme).setVisible(false);
menu.findItem(R.id.show_headers).setVisible(false);
menu.findItem(R.id.hide_headers).setVisible(false);
@ -1103,9 +1098,6 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
menu.findItem(R.id.toggle_unread).setIcon(ta.getDrawable(0));
ta.recycle();
// Jellybean has built-in long press selection support
menu.findItem(R.id.select_text).setVisible(Build.VERSION.SDK_INT < 16);
menu.findItem(R.id.delete).setVisible(K9.isMessageViewDeleteActionVisible());
/*

View file

@ -5,13 +5,11 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.LoaderManager;
@ -338,20 +336,17 @@ public class AttachmentPresenter {
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void addAttachmentsFromResultIntent(Intent data) {
// TODO draftNeedsSaving = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
ClipData clipData = data.getClipData();
if (clipData != null) {
for (int i = 0, end = clipData.getItemCount(); i < end; i++) {
Uri uri = clipData.getItemAt(i).getUri();
if (uri != null) {
addAttachment(uri);
}
ClipData clipData = data.getClipData();
if (clipData != null) {
for (int i = 0, end = clipData.getItemCount(); i < end; i++) {
Uri uri = clipData.getItemAt(i).getUri();
if (uri != null) {
addAttachment(uri);
}
return;
}
return;
}
Uri uri = data.getData();

View file

@ -9,7 +9,6 @@ import android.content.Intent;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
@ -25,7 +24,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.Toast;
import com.fsck.k9.ui.R;
import com.fsck.k9.mail.Address;
@ -179,13 +177,9 @@ public class ContactBadge extends ImageView implements OnClickListener {
* @param address the address to look for a contact for.
*/
public void setContact(Address address) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Bundle extraContactInfo = new Bundle();
extraContactInfo.putString(ContactsContract.Intents.Insert.NAME, address.getPersonal());
assignContactFromEmail(address.getAddress(), true, extraContactInfo);
} else {
assignContactFromEmail(address.getAddress(), true);
}
Bundle extraContactInfo = new Bundle();
extraContactInfo.putString(ContactsContract.Intents.Insert.NAME, address.getPersonal());
assignContactFromEmail(address.getAddress(), true, extraContactInfo);
}

View file

@ -415,11 +415,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
onRefile(mAccount.getSpamFolder());
}
public void onSelectText() {
// FIXME
// mMessageView.beginSelectingText();
}
private void startRefileActivity(int activity) {
Intent intent = new Intent(getActivity(), ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount.getUuid());

View file

@ -1,11 +1,9 @@
package com.fsck.k9.ui.settings.general
import android.os.Bundle
import android.support.v14.preference.MultiSelectListPreference
import com.fsck.k9.ui.R
import com.fsck.k9.notification.NotificationController
import com.fsck.k9.ui.settings.remove
import com.fsck.k9.ui.settings.removeEntry
import com.fsck.k9.ui.withArguments
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
import org.koin.android.ext.android.inject
@ -20,9 +18,7 @@ class GeneralSettingsFragment : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.general_settings, rootKey)
initializeStartInUnifiedInbox()
initializeConfirmActions()
initializeLockScreenNotificationVisibility()
initializeNotificationQuickDelete()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -39,15 +35,6 @@ class GeneralSettingsFragment : PreferenceFragmentCompat() {
}
}
private fun initializeConfirmActions() {
val notificationActionsSupported = NotificationController.platformSupportsExtendedNotifications()
if (!notificationActionsSupported) {
(findPreference(PREFERENCE_CONFIRM_ACTIONS) as? MultiSelectListPreference)?.apply {
removeEntry(CONFIRM_ACTION_DELETE_FROM_NOTIFICATION)
}
}
}
private fun initializeLockScreenNotificationVisibility() {
val lockScreenNotificationsSupported = NotificationController.platformSupportsLockScreenNotifications()
if (!lockScreenNotificationsSupported) {
@ -55,23 +42,13 @@ class GeneralSettingsFragment : PreferenceFragmentCompat() {
}
}
private fun initializeNotificationQuickDelete() {
val notificationActionsSupported = NotificationController.platformSupportsExtendedNotifications()
if (!notificationActionsSupported) {
findPreference(PREFERENCE_NOTIFICATION_QUICK_DELETE)?.apply { remove() }
}
}
private fun hideSpecialAccounts() = dataStore.getBoolean(PREFERENCE_HIDE_SPECIAL_ACCOUNTS, false)
companion object {
private const val PREFERENCE_START_IN_UNIFIED_INBOX = "start_integrated_inbox"
private const val PREFERENCE_HIDE_SPECIAL_ACCOUNTS = "hide_special_accounts"
private const val PREFERENCE_CONFIRM_ACTIONS = "confirm_actions"
private const val PREFERENCE_LOCK_SCREEN_NOTIFICATION_VISIBILITY = "lock_screen_notification_visibility"
private const val PREFERENCE_NOTIFICATION_QUICK_DELETE = "notification_quick_delete"
private const val CONFIRM_ACTION_DELETE_FROM_NOTIFICATION = "delete_notif"
fun create(rootKey: String? = null) = GeneralSettingsFragment().withArguments(
PreferenceFragmentCompat.ARG_PREFERENCE_ROOT to rootKey)

View file

@ -227,12 +227,6 @@
app:showAsAction="never"
android:title="@string/expunge_action"/>
<!-- MessageView -->
<item
android:id="@+id/select_text"
app:showAsAction="never"
android:title="@string/select_text_action"/>
<!-- MessageView -->
<item
android:id="@+id/toggle_message_view_theme"

View file

@ -159,7 +159,6 @@ Please submit bug reports, contribute new features and ask questions at
<string name="flag_action">Add star</string>
<string name="unflag_action">Remove star</string>
<string name="copy_action">Copy</string>
<string name="select_text_action">Select text</string>
<string name="show_headers_action">Show headers</string>
<string name="hide_headers_action">Hide headers</string>
<plurals name="copy_address_to_clipboard">

View file

@ -218,8 +218,7 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
}
public static void setSniHost(SSLSocketFactory factory, SSLSocket socket, String hostname) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1 &&
factory instanceof android.net.SSLCertificateSocketFactory) {
if (factory instanceof android.net.SSLCertificateSocketFactory) {
SSLCertificateSocketFactory sslCertificateSocketFactory = (SSLCertificateSocketFactory) factory;
sslCertificateSocketFactory.setHostname(socket, hostname);
} else {

View file

@ -15,7 +15,6 @@ import javax.net.ssl.X509ExtendedKeyManager;
import javax.security.auth.x500.X500Principal;
import android.content.Context;
import android.os.Build;
import android.security.KeyChain;
import android.security.KeyChainException;
@ -31,17 +30,6 @@ import static com.fsck.k9.mail.CertificateValidationException.Reason.RetrievalFa
* during the TLS handshake using the Android 4.0 KeyChain API.
*/
class KeyChainKeyManager extends X509ExtendedKeyManager {
private static PrivateKey sClientCertificateReferenceWorkaround;
private static synchronized void savePrivateKeyReference(PrivateKey privateKey) {
if (sClientCertificateReferenceWorkaround == null) {
sClientCertificateReferenceWorkaround = privateKey;
}
}
private final String mAlias;
private final X509Certificate[] mChain;
private final PrivateKey mPrivateKey;
@ -93,16 +81,6 @@ class KeyChainKeyManager extends X509ExtendedKeyManager {
throw new MessagingException("No private key found for: " + alias);
}
/*
* We need to keep reference to the first private key retrieved so
* it won't get garbage collected. If it will then the whole app
* will crash on Android < 4.2 with "Fatal signal 11 code=1". See
* https://code.google.com/p/android/issues/detail?id=62319
*/
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
savePrivateKeyReference(privateKey);
}
return privateKey;
}