Fix Bluetooth share without breaking others

This commit is contained in:
William Faulk 2015-04-29 19:59:32 -04:00
parent 291f95db5a
commit b06e7cd737
4 changed files with 100 additions and 9 deletions

View File

@ -40,6 +40,8 @@ import org.sufficientlysecure.keychain.util.PRNGFixes;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.File;
import java.io.FilenameFilter;
import java.security.Security;
import java.util.HashMap;
@ -88,6 +90,17 @@ public class KeychainApplication extends Application {
}
}
// Clean up leftover Bluetooth Share files
for (File toDelete : this.getExternalCacheDir().listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
if (filename.matches("^key-[0-9a-fA-F]{8}\\.pgp\\.asc$")) {
return true;
}
return false;
}
})) { toDelete.delete(); }
brandGlowEffect(getApplicationContext(),
getApplicationContext().getResources().getColor(R.color.primary));

View File

@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.FileObserver;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@ -54,6 +55,9 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@ -175,11 +179,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
boolean toClipboard) {
try {
String content;
if (fingerprintOnly) {
byte[] data = (byte[]) providerHelper.getGenericData(
byte[] fingerprintData = (byte[]) providerHelper.getGenericData(
KeyRings.buildUnifiedKeyRingUri(dataUri),
Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(data);
if (fingerprintOnly) {
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintData);
if (!toClipboard) {
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else {
@ -213,13 +217,62 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, content);
sendIntent.setType("text/plain");
String title;
if (fingerprintOnly) {
title = getResources().getString(R.string.title_share_fingerprint_with);
} else {
title = getResources().getString(R.string.title_share_key);
}
startActivity(Intent.createChooser(sendIntent, title));
Intent shareChooser = Intent.createChooser(sendIntent, title);
// Bluetooth Share will convert text/plain sent via EXTRA_TEXT to HTML
// Add replacement extra to send a text/plain file instead.
try {
final File contentFile = new File(getActivity().getExternalCacheDir(),
"key-" + KeyFormattingUtils.getShortKeyIdAsHexFromFingerprint(fingerprintData, false) +
".pgp.asc");
FileWriter contentFileWriter = new FileWriter(contentFile, false);
BufferedWriter contentWriter = new BufferedWriter(contentFileWriter);
contentWriter.write(content);
contentWriter.close();
Uri contentUri = Uri.fromFile(contentFile);
final Runnable deleteContentFile = new Runnable() {
public void run() {
contentFile.delete();
}
};
// delete the file after Bluetooth Share closes the file
FileObserver tempFileObserver = new FileObserver(contentFile.getAbsolutePath(),
FileObserver.CLOSE_NOWRITE) {
@Override
public void onEvent(int event, String path) {
// Hopefully it will only be opened and then closed by the share process once
getContainer().post(deleteContentFile);
}
};
tempFileObserver.startWatching();
// If it's not complete in 1m, the file was not used; delete it
getContainer().postDelayed(deleteContentFile, 1 * 60 * 1000);
// create replacement extras inside try{}:
// if file creation fails, just don't add the replacements
Bundle replacements = new Bundle();
shareChooser.putExtra(Intent.EXTRA_REPLACEMENT_EXTRAS, replacements);
Bundle bluetoothExtra = new Bundle(sendIntent.getExtras());
replacements.putBundle("com.android.bluetooth", bluetoothExtra);
bluetoothExtra.putParcelable(Intent.EXTRA_STREAM, contentUri);
} catch (IOException e) {
Log.e(Constants.TAG, "error creating temporary Bluetooth key share file!", e);
Notify.create(getActivity(), R.string.error_bluetooth_file, Notify.Style.ERROR).show();
}
startActivity(shareChooser);
}
} catch (PgpGeneralException | IOException e) {
Log.e(Constants.TAG, "error processing key!", e);

View File

@ -227,6 +227,14 @@ public class KeyFormattingUtils {
return buf.getLong();
}
public static int getShortKeyIdFromFingerprint(byte[] fingerprint) {
ByteBuffer buf = ByteBuffer.wrap(fingerprint);
// skip first 16 bytes of the fingerprint
buf.position(16);
// the last four bytes are the short key id (big endian, which is default order in ByteBuffer)
return buf.getInt();
}
/**
* Convert key id from long to 64 bit hex string
* <p/>
@ -238,16 +246,24 @@ public class KeyFormattingUtils {
* @return
*/
public static String convertKeyIdToHex(long keyId) {
return convertKeyIdToHex(keyId, true);
}
public static String convertKeyIdToHex(long keyId, boolean header) {
long upper = keyId >> 32;
if (upper == 0) {
// this is a short key id
return convertKeyIdToHexShort(keyId);
return convertKeyIdToHexShort(keyId, header);
}
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
return header?"0x":"" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
}
public static String convertKeyIdToHexShort(long keyId) {
return "0x" + convertKeyIdToHex32bit(keyId);
return convertKeyIdToHexShort(keyId, true);
}
public static String convertKeyIdToHexShort(long keyId, boolean header) {
return header?"0x":"" + convertKeyIdToHex32bit(keyId);
}
private static String convertKeyIdToHex32bit(long keyId) {
@ -258,6 +274,14 @@ public class KeyFormattingUtils {
return hexString;
}
public static String getKeyIdAsHexFromFingerprint(byte[] fingerprint, boolean header) {
return convertKeyIdToHex(getKeyIdFromFingerprint(fingerprint), header);
}
public static String getShortKeyIdAsHexFromFingerprint(byte[] fingerprint, boolean header) {
return convertKeyIdToHex(getShortKeyIdFromFingerprint(fingerprint), header);
}
/**
* Makes a human-readable version of a key ID, which is usually 64 bits: lower-case, no
* leading 0x, space-separated quartets (for keys whose length in hex is divisible by 4)

View File

@ -1289,5 +1289,6 @@
<string name="snack_yubi_other">Different key stored on YubiKey!</string>
<string name="error_nfc">"NFC Error: %s"</string>
<string name="error_pin_nodefault">Default PIN was rejected!</string>
<string name="error_bluetooth_file">Error creating temporary file. Bluetooth sharing will fail.</string>
</resources>