unify naming of api package and process

This commit is contained in:
Dominik Schürmann
2013-09-13 10:05:43 +02:00
parent 86c84ddbb3
commit ca8f8e3ff7
14 changed files with 20 additions and 20 deletions

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Id;
public class AppSettings {
private String packageName;
private long keyId = Id.key.none;
private int encryptionAlgorithm;
private int hashAlgorithm;
private int compression;
public AppSettings() {
}
public AppSettings(String packageName) {
super();
this.packageName = packageName;
// defaults:
this.encryptionAlgorithm = PGPEncryptedData.AES_128;
this.hashAlgorithm = HashAlgorithmTags.SHA256;
this.compression = Id.choice.compression.zlib;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public long getKeyId() {
return keyId;
}
public void setKeyId(long scretKeyId) {
this.keyId = scretKeyId;
}
public int getEncryptionAlgorithm() {
return encryptionAlgorithm;
}
public void setEncryptionAlgorithm(int encryptionAlgorithm) {
this.encryptionAlgorithm = encryptionAlgorithm;
}
public int getHashAlgorithm() {
return hashAlgorithm;
}
public void setHashAlgorithm(int hashAlgorithm) {
this.hashAlgorithm = hashAlgorithm;
}
public int getCompression() {
return compression;
}
public void setCompression(int compression) {
this.compression = compression;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
public class AppSettingsActivity extends SherlockFragmentActivity {
private Uri mAppUri;
private AppSettingsFragment settingsFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// "Done"
save();
}
});
setContentView(R.layout.api_app_settings_activity);
settingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
R.id.api_app_settings_fragment);
Intent intent = getIntent();
mAppUri = intent.getData();
if (mAppUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
finish();
return;
} else {
Log.d(Constants.TAG, "uri: " + mAppUri);
loadData(mAppUri);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getSupportMenuInflater().inflate(R.menu.api_app_settings, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_api_settings_revoke:
revokeAccess();
return true;
case R.id.menu_api_settings_cancel:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void loadData(Uri appUri) {
AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
settingsFragment.setAppSettings(settings);
}
private void revokeAccess() {
if (getContentResolver().delete(mAppUri, null, null) <= 0) {
throw new RuntimeException();
}
finish();
}
private void save() {
ProviderHelper.updateApiApp(this, settingsFragment.getAppSettings(), mAppUri);
finish();
}
}

View File

@@ -0,0 +1,304 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import java.util.HashMap;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.PgpHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.SelectSecretKeyActivity;
import org.sufficientlysecure.keychain.util.KeyValueSpinnerAdapter;
import org.sufficientlysecure.keychain.util.Log;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
public class AppSettingsFragment extends Fragment {
// model
private AppSettings appSettings;
// view
private LinearLayout mAdvancedSettingsContainer;
private Button mAdvancedSettingsButton;
private TextView mAppNameView;
private ImageView mAppIconView;
private TextView mKeyUserId;
private TextView mKeyUserIdRest;
private Button mSelectKeyButton;
private Spinner mEncryptionAlgorithm;
private Spinner mHashAlgorithm;
private Spinner mCompression;
KeyValueSpinnerAdapter encryptionAdapter;
KeyValueSpinnerAdapter hashAdapter;
KeyValueSpinnerAdapter compressionAdapter;
public AppSettings getAppSettings() {
return appSettings;
}
public void setAppSettings(AppSettings appSettings) {
this.appSettings = appSettings;
setPackage(appSettings.getPackageName());
updateSelectedKeyView(appSettings.getKeyId());
mEncryptionAlgorithm.setSelection(encryptionAdapter.getPosition(appSettings
.getEncryptionAlgorithm()));
mHashAlgorithm.setSelection(hashAdapter.getPosition(appSettings.getHashAlgorithm()));
mCompression.setSelection(compressionAdapter.getPosition(appSettings.getCompression()));
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
initView(view);
return view;
}
private void initView(View view) {
mAdvancedSettingsButton = (Button) view.findViewById(R.id.api_app_settings_advanced_button);
mAdvancedSettingsContainer = (LinearLayout) view
.findViewById(R.id.api_app_settings_advanced);
mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
mKeyUserId = (TextView) view.findViewById(R.id.api_app_settings_user_id);
mKeyUserIdRest = (TextView) view.findViewById(R.id.api_app_settings_user_id_rest);
mSelectKeyButton = (Button) view.findViewById(R.id.api_app_settings_select_key_button);
mEncryptionAlgorithm = (Spinner) view
.findViewById(R.id.api_app_settings_encryption_algorithm);
mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression);
HashMap<Integer, String> encryptionMap = new HashMap<Integer, String>();
encryptionMap.put(PGPEncryptedData.AES_128, "AES-128");
encryptionMap.put(PGPEncryptedData.AES_192, "AES-192");
encryptionMap.put(PGPEncryptedData.AES_256, "AES-256");
encryptionMap.put(PGPEncryptedData.BLOWFISH, "Blowfish");
encryptionMap.put(PGPEncryptedData.TWOFISH, "Twofish");
encryptionMap.put(PGPEncryptedData.CAST5, "CAST5");
encryptionMap.put(PGPEncryptedData.DES, "DES");
encryptionMap.put(PGPEncryptedData.TRIPLE_DES, "Triple DES");
encryptionMap.put(PGPEncryptedData.IDEA, "IDEA");
encryptionAdapter = new KeyValueSpinnerAdapter(getActivity(), encryptionMap);
mEncryptionAlgorithm.setAdapter(encryptionAdapter);
mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
appSettings.setEncryptionAlgorithm((int) id);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
HashMap<Integer, String> hashMap = new HashMap<Integer, String>();
hashMap.put(HashAlgorithmTags.MD5, "MD5");
hashMap.put(HashAlgorithmTags.RIPEMD160, "RIPEMD-160");
hashMap.put(HashAlgorithmTags.SHA1, "SHA-1");
hashMap.put(HashAlgorithmTags.SHA224, "SHA-224");
hashMap.put(HashAlgorithmTags.SHA256, "SHA-256");
hashMap.put(HashAlgorithmTags.SHA384, "SHA-384");
hashMap.put(HashAlgorithmTags.SHA512, "SHA-512");
hashAdapter = new KeyValueSpinnerAdapter(getActivity(), hashMap);
mHashAlgorithm.setAdapter(hashAdapter);
mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
appSettings.setHashAlgorithm((int) id);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
HashMap<Integer, String> compressionMap = new HashMap<Integer, String>();
compressionMap.put(Id.choice.compression.none, getString(R.string.choice_none) + " ("
+ getString(R.string.fast) + ")");
compressionMap.put(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")");
compressionMap.put(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")");
compressionMap.put(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow)
+ ")");
compressionAdapter = new KeyValueSpinnerAdapter(getActivity(), compressionMap);
mCompression.setAdapter(compressionAdapter);
mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
appSettings.setCompression((int) id);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mSelectKeyButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
selectSecretKey();
}
});
final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f);
visibleAnimation.setDuration(250);
final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f);
invisibleAnimation.setDuration(250);
// TODO: Better: collapse/expand animation
// final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
// Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
// Animation.RELATIVE_TO_SELF, 0.0f);
// animation2.setDuration(150);
mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
mAdvancedSettingsContainer.setVisibility(View.INVISIBLE);
mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced);
} else {
mAdvancedSettingsContainer.startAnimation(visibleAnimation);
mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
mAdvancedSettingsButton.setText(R.string.api_settings_hide_advanced);
}
}
});
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
private void selectSecretKey() {
Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
startActivityForResult(intent, Id.request.secret_keys);
}
private void setPackage(String packageName) {
PackageManager pm = getActivity().getApplicationContext().getPackageManager();
// get application name and icon from package manager
String appName = null;
Drawable appIcon = null;
try {
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
appName = (String) pm.getApplicationLabel(ai);
appIcon = pm.getApplicationIcon(ai);
} catch (final NameNotFoundException e) {
// fallback
appName = packageName;
}
mAppNameView.setText(appName);
mAppIconView.setImageDrawable(appIcon);
}
private void updateSelectedKeyView(long secretKeyId) {
if (secretKeyId == Id.key.none) {
mKeyUserId.setText(R.string.api_settings_no_key);
mKeyUserIdRest.setText("");
} else {
String uid = getResources().getString(R.string.unknownUserId);
String uidExtra = "";
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
getActivity(), secretKeyId);
if (keyRing != null) {
PGPSecretKey key = PgpHelper.getMasterKey(keyRing);
if (key != null) {
String userId = PgpHelper.getMainUserIdSafe(getActivity(), key);
String chunks[] = userId.split(" <", 2);
uid = chunks[0];
if (chunks.length > 1) {
uidExtra = "<" + chunks[1];
}
}
}
mKeyUserId.setText(uid);
mKeyUserIdRest.setText(uidExtra);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(Constants.TAG, "onactivityresult " + requestCode + " " + resultCode);
switch (requestCode) {
case Id.request.secret_keys: {
long secretKeyId;
if (resultCode == Activity.RESULT_OK) {
Bundle bundle = data.getExtras();
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
} else {
secretKeyId = Id.key.none;
}
appSettings.setKeyId(secretKeyId);
updateSelectedKeyView(secretKeyId);
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View File

@@ -0,0 +1,747 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import org.openintents.openpgp.IOpenPgpCallback;
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.PgpMain;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
public class OpenPgpService extends Service {
Context mContext;
final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
// TODO: Are these parameters okay?
PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
TimeUnit.SECONDS, mPoolQueue);
final Object userInputLock = new Object();
private class MyBaseCallback implements Handler.Callback {
public static final int OKAY = 1;
public static final int CANCEL = 0;
@Override
public boolean handleMessage(Message msg) {
return false;
}
}
@Override
public void onCreate() {
super.onCreate();
mContext = this;
Log.d(Constants.TAG, "CryptoService, onCreate()");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(Constants.TAG, "CryptoService, onDestroy()");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private String getCachedPassphrase(long keyId) {
String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
if (passphrase == null) {
Log.d(Constants.TAG, "No passphrase! Activity required!");
// start passphrase dialog
Bundle extras = new Bundle();
extras.putLong(OpenPgpServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
PassphraseActivityCallback callback = new PassphraseActivityCallback();
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_CACHE_PASSPHRASE,
messenger, extras);
if (callback.isSuccess()) {
Log.d(Constants.TAG, "New passphrase entered!");
// get again after it was entered
passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
} else {
Log.d(Constants.TAG, "Passphrase dialog canceled!");
return null;
}
}
return passphrase;
}
public class PassphraseActivityCallback extends MyBaseCallback {
private boolean success = false;
public boolean isSuccess() {
return success;
}
@Override
public boolean handleMessage(Message msg) {
if (msg.arg1 == OKAY) {
success = true;
} else {
success = false;
}
// resume
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
return true;
}
};
/**
* Search database for key ids based on emails.
*
* @param encryptionUserIds
* @return
*/
private long[] getKeyIdsFromEmails(String[] encryptionUserIds, long ownKeyId) {
// find key ids to given emails in database
ArrayList<Long> keyIds = new ArrayList<Long>();
boolean missingUserIdsCheck = false;
boolean dublicateUserIdsCheck = false;
ArrayList<String> missingUserIds = new ArrayList<String>();
ArrayList<String> dublicateUserIds = new ArrayList<String>();
for (String email : encryptionUserIds) {
Uri uri = KeychainContract.KeyRings.buildPublicKeyRingsByEmailsUri(email);
Cursor cur = getContentResolver().query(uri, null, null, null, null);
if (cur.moveToFirst()) {
long id = cur.getLong(cur.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID));
keyIds.add(id);
} else {
missingUserIdsCheck = true;
missingUserIds.add(email);
Log.d(Constants.TAG, "user id missing");
}
if (cur.moveToNext()) {
dublicateUserIdsCheck = true;
dublicateUserIds.add(email);
Log.d(Constants.TAG, "more than one user id with the same email");
}
}
// also encrypt to our self (so that we can decrypt it later!)
keyIds.add(ownKeyId);
// convert to long[]
long[] keyIdsArray = new long[keyIds.size()];
for (int i = 0; i < keyIdsArray.length; i++) {
keyIdsArray[i] = keyIds.get(i);
}
if (missingUserIdsCheck || dublicateUserIdsCheck) {
SelectPubKeysActivityCallback callback = new SelectPubKeysActivityCallback();
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
Bundle extras = new Bundle();
extras.putLongArray(OpenPgpServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_DUBLICATE_USER_IDS,
dublicateUserIds);
pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_SELECT_PUB_KEYS,
messenger, extras);
if (callback.isSuccess()) {
Log.d(Constants.TAG, "New selection of pub keys!");
keyIdsArray = callback.getPubKeyIds();
} else {
Log.d(Constants.TAG, "Pub key selection canceled!");
return null;
}
}
if (keyIdsArray.length == 0) {
return null;
}
return keyIdsArray;
}
public class SelectPubKeysActivityCallback extends MyBaseCallback {
public static final String PUB_KEY_IDS = "pub_key_ids";
private boolean success = false;
private long[] pubKeyIds;
public boolean isSuccess() {
return success;
}
public long[] getPubKeyIds() {
return pubKeyIds;
}
@Override
public boolean handleMessage(Message msg) {
if (msg.arg1 == OKAY) {
success = true;
pubKeyIds = msg.getData().getLongArray(PUB_KEY_IDS);
} else {
success = false;
}
// resume
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
return true;
}
};
private synchronized void encryptAndSignSafe(byte[] inputBytes, String[] encryptionUserIds,
boolean asciiArmor, IOpenPgpCallback callback, AppSettings appSettings, boolean sign)
throws RemoteException {
try {
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputData = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
long[] keyIds = getKeyIdsFromEmails(encryptionUserIds, appSettings.getKeyId());
if (keyIds == null) {
callback.onError(new OpenPgpError(OpenPgpError.ID_NO_USER_IDS, "No user ids!"));
return;
}
if (sign) {
String passphrase = getCachedPassphrase(appSettings.getKeyId());
if (passphrase == null) {
callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE,
"No or wrong passphrase!"));
return;
}
PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor,
appSettings.getCompression(), keyIds, null,
appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
appSettings.getHashAlgorithm(), true, passphrase);
} else {
PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor,
appSettings.getCompression(), keyIds, null,
appSettings.getEncryptionAlgorithm(), Id.key.none,
appSettings.getHashAlgorithm(), true, null);
}
outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
// return over handler on client side
callback.onSuccess(outputBytes, null);
} catch (Exception e) {
Log.e(Constants.TAG, "KeychainService, Exception!", e);
try {
callback.onError(new OpenPgpError(0, e.getMessage()));
} catch (Exception t) {
Log.e(Constants.TAG, "Error returning exception to client", t);
}
}
}
// TODO: asciiArmor?!
private void signSafe(byte[] inputBytes, IOpenPgpCallback callback, AppSettings appSettings)
throws RemoteException {
try {
Log.d(Constants.TAG, "current therad id: " + Thread.currentThread().getId());
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputData = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
String passphrase = getCachedPassphrase(appSettings.getKeyId());
if (passphrase == null) {
callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE,
"No or wrong passphrase!"));
return;
}
PgpMain.signText(this, null, inputData, outputStream, appSettings.getKeyId(),
passphrase, appSettings.getHashAlgorithm(), Preferences.getPreferences(this)
.getForceV3Signatures());
outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
// return over handler on client side
callback.onSuccess(outputBytes, null);
} catch (Exception e) {
Log.e(Constants.TAG, "KeychainService, Exception!", e);
try {
callback.onError(new OpenPgpError(0, e.getMessage()));
} catch (Exception t) {
Log.e(Constants.TAG, "Error returning exception to client", t);
}
}
}
private synchronized void decryptAndVerifySafe(byte[] inputBytes, IOpenPgpCallback callback,
AppSettings appSettings) throws RemoteException {
try {
// TODO: this is not really needed
// checked if it is text with BEGIN and END tags
String message = new String(inputBytes);
Log.d(Constants.TAG, "in: " + message);
boolean signedOnly = false;
Matcher matcher = PgpMain.PGP_MESSAGE.matcher(message);
if (matcher.matches()) {
Log.d(Constants.TAG, "PGP_MESSAGE matched");
message = matcher.group(1);
// replace non breakable spaces
message = message.replaceAll("\\xa0", " ");
// overwrite inputBytes
inputBytes = message.getBytes();
} else {
matcher = PgpMain.PGP_SIGNED_MESSAGE.matcher(message);
if (matcher.matches()) {
signedOnly = true;
Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
message = matcher.group(1);
// replace non breakable spaces
message = message.replaceAll("\\xa0", " ");
// overwrite inputBytes
inputBytes = message.getBytes();
} else {
Log.d(Constants.TAG, "Nothing matched! Binary?");
}
}
// END TODO
Log.d(Constants.TAG, "in: " + new String(inputBytes));
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
// app, Fix this?
// long secretKeyId = PgpMain.getDecryptionKeyId(mContext, inputStream);
// if (secretKeyId == Id.key.none) {
// throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound));
// }
String passphrase = null;
boolean assumeSymmetricEncryption = false;
if (!signedOnly) {
// BEGIN Get key
// TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
// better!
InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
// TODO: duplicates functions from DecryptActivity!
// TODO: we need activity to input symmetric passphrase
long secretKeyId;
try {
if (inputStream2.markSupported()) {
inputStream2.mark(200); // should probably set this to the max size of two
// pgpF
// objects, if it even needs to be anything other
// than
// 0.
}
secretKeyId = PgpMain.getDecryptionKeyId(this, inputStream2);
if (secretKeyId == Id.key.none) {
throw new PgpMain.PgpGeneralException(
getString(R.string.error_noSecretKeyFound));
}
assumeSymmetricEncryption = false;
} catch (PgpMain.NoAsymmetricEncryptionException e) {
if (inputStream2.markSupported()) {
inputStream2.reset();
}
secretKeyId = Id.key.symmetric;
if (!PgpMain.hasSymmetricEncryption(this, inputStream2)) {
throw new PgpMain.PgpGeneralException(
getString(R.string.error_noKnownEncryptionFound));
}
assumeSymmetricEncryption = true;
}
Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
passphrase = getCachedPassphrase(secretKeyId);
if (passphrase == null) {
callback.onError(new OpenPgpError(OpenPgpError.ID_NO_OR_WRONG_PASSPHRASE,
"No or wrong passphrase!"));
return;
}
}
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputData = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
Bundle outputBundle;
if (signedOnly) {
// TODO: download missing keys from keyserver?
outputBundle = PgpMain.verifyText(this, null, inputData, outputStream, false);
} else {
// TODO: assume symmetric: callback to enter symmetric pass
outputBundle = PgpMain.decryptAndVerify(this, null, inputData, outputStream,
passphrase, assumeSymmetricEncryption);
}
outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
// get signature informations from bundle
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE);
long signatureKeyId = outputBundle
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
String signatureUserId = outputBundle
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
boolean signatureSuccess = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS);
boolean signatureUnknown = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN);
OpenPgpSignatureResult sigResult = null;
if (signature) {
sigResult = new OpenPgpSignatureResult(signatureUserId, signatureSuccess,
signatureUnknown);
}
// return over handler on client side
callback.onSuccess(outputBytes, sigResult);
} catch (Exception e) {
Log.e(Constants.TAG, "KeychainService, Exception!", e);
try {
callback.onError(new OpenPgpError(0, e.getMessage()));
} catch (Exception t) {
Log.e(Constants.TAG, "Error returning exception to client", t);
}
}
}
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
@Override
public void encrypt(final byte[] inputBytes, final String[] encryptionUserIds,
final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
try {
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
settings, false);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoService", e);
}
}
};
checkAndEnqueue(r);
}
@Override
public void encryptAndSign(final byte[] inputBytes, final String[] encryptionUserIds,
final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
try {
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
settings, true);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoService", e);
}
}
};
checkAndEnqueue(r);
}
@Override
public void sign(final byte[] inputBytes, boolean asciiArmor,
final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
try {
signSafe(inputBytes, callback, settings);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoService", e);
}
}
};
checkAndEnqueue(r);
}
@Override
public void decryptAndVerify(final byte[] inputBytes, final IOpenPgpCallback callback)
throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
try {
decryptAndVerifySafe(inputBytes, callback, settings);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoService", e);
}
}
};
checkAndEnqueue(r);
}
};
private void checkAndEnqueue(Runnable r) {
if (isCallerAllowed(false)) {
mThreadPool.execute(r);
Log.d(Constants.TAG, "Enqueued runnable…");
} else {
String[] callingPackages = getPackageManager()
.getPackagesForUid(Binder.getCallingUid());
Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!");
Bundle extras = new Bundle();
// TODO: currently simply uses first entry
extras.putString(OpenPgpServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]);
RegisterActivityCallback callback = new RegisterActivityCallback();
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_REGISTER, messenger,
extras);
if (callback.isAllowed()) {
mThreadPool.execute(r);
Log.d(Constants.TAG, "Enqueued runnable…");
} else {
Log.d(Constants.TAG, "User disallowed app!");
}
}
}
public class RegisterActivityCallback extends MyBaseCallback {
public static final String PACKAGE_NAME = "package_name";
private boolean allowed = false;
private String packageName;
public boolean isAllowed() {
return allowed;
}
public String getPackageName() {
return packageName;
}
@Override
public boolean handleMessage(Message msg) {
if (msg.arg1 == OKAY) {
allowed = true;
packageName = msg.getData().getString(PACKAGE_NAME);
// resume threads
if (isPackageAllowed(packageName, false)) {
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
} else {
// Should not happen!
Log.e(Constants.TAG, "Should not happen! Emergency shutdown!");
mThreadPool.shutdownNow();
}
} else {
allowed = false;
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
}
return true;
}
}
/**
* Checks if process that binds to this service (i.e. the package name corresponding to the
* process) is in the list of allowed package names.
*
* @param allowOnlySelf
* allow only Keychain app itself
* @return true if process is allowed to use this service
*/
private boolean isCallerAllowed(boolean allowOnlySelf) {
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
// is calling package allowed to use this service?
for (int i = 0; i < callingPackages.length; i++) {
String currentPkg = callingPackages[i];
if (isPackageAllowed(currentPkg, allowOnlySelf)) {
return true;
}
}
Log.d(Constants.TAG, "Caller is NOT allowed!");
return false;
}
private AppSettings getAppSettings() {
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
// is calling package allowed to use this service?
for (int i = 0; i < callingPackages.length; i++) {
String currentPkg = callingPackages[i];
Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg);
AppSettings settings = ProviderHelper.getApiAppSettings(this, uri);
return settings;
}
return null;
}
/**
* Checks if packageName is a registered app for the API.
*
* @param packageName
* @param allowOnlySelf
* allow only Keychain app itself
* @return
*/
private boolean isPackageAllowed(String packageName, boolean allowOnlySelf) {
Log.d(Constants.TAG, "packageName: " + packageName);
ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(mContext);
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
// check if package is allowed to use our service
if (allowedPkgs.contains(packageName) && (!allowOnlySelf)) {
Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
return true;
} else if (Constants.PACKAGE_NAME.equals(packageName)) {
Log.d(Constants.TAG, "Package is OpenPGP Keychain! -> allowed!");
return true;
}
return false;
}
private void pauseQueueAndStartServiceActivity(String action, Messenger messenger, Bundle extras) {
synchronized (userInputLock) {
mThreadPool.pause();
Log.d(Constants.TAG, "starting activity...");
Intent intent = new Intent(getBaseContext(), OpenPgpServiceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(action);
extras.putParcelable(OpenPgpServiceActivity.EXTRA_MESSENGER, messenger);
intent.putExtras(extras);
startActivity(intent);
// lock current thread for user input
try {
userInputLock.wait();
} catch (InterruptedException e) {
Log.e(Constants.TAG, "CryptoService", e);
}
}
}
}

View File

@@ -0,0 +1,325 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import java.util.ArrayList;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.PgpMain;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
public class OpenPgpServiceActivity extends SherlockFragmentActivity {
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
+ "API_ACTIVITY_CACHE_PASSPHRASE";
public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX
+ "API_ACTIVITY_SELECT_PUB_KEYS";
public static final String EXTRA_MESSENGER = "messenger";
// passphrase action
public static final String EXTRA_SECRET_KEY_ID = "secret_key_id";
// register action
public static final String EXTRA_PACKAGE_NAME = "package_name";
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
public static final String EXTRA_DUBLICATE_USER_IDS = "dublicate_user_ids";
private Messenger mMessenger;
// register view
private AppSettingsFragment mSettingsFragment;
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
// has the user clicked one of the buttons
// or do we need to handle the callback in onStop()
private boolean finishHandled;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleActions(getIntent(), savedInstanceState);
}
@Override
protected void onStop() {
super.onStop();
if (!finishHandled) {
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
}
}
protected void handleActions(Intent intent, Bundle savedInstanceState) {
finishHandled = false;
String action = intent.getAction();
Bundle extras = intent.getExtras();
if (extras == null) {
extras = new Bundle();
}
mMessenger = extras.getParcelable(EXTRA_MESSENGER);
/**
* com.android.crypto actions
*/
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// Allow
// user needs to select a key!
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
Toast.makeText(OpenPgpServiceActivity.this,
R.string.api_register_error_select_key, Toast.LENGTH_LONG)
.show();
} else {
ProviderHelper.insertApiApp(OpenPgpServiceActivity.this,
mSettingsFragment.getAppSettings());
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.RegisterActivityCallback.OKAY;
Bundle data = new Bundle();
data.putString(OpenPgpService.RegisterActivityCallback.PACKAGE_NAME,
packageName);
msg.setData(data);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
}
}
}, R.string.api_register_disallow, new View.OnClickListener() {
@Override
public void onClick(View v) {
// Disallow
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
}
});
setContentView(R.layout.api_app_register_activity);
mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
R.id.api_app_settings_fragment);
AppSettings settings = new AppSettings(packageName);
mSettingsFragment.setAppSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
showPassphraseDialog(secretKeyId);
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
ArrayList<String> missingUserIds = intent
.getStringArrayListExtra(EXTRA_MISSING_USER_IDS);
ArrayList<String> dublicateUserIds = intent
.getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS);
String text = new String();
text += "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
text += "<br/><br/>";
if (missingUserIds != null && missingUserIds.size() > 0) {
text += getString(R.string.api_select_pub_keys_missing_text);
text += "<br/>";
text += "<ul>";
for (String userId : missingUserIds) {
text += "<li>" + userId + "</li>";
}
text += "</ul>";
text += "<br/>";
}
if (dublicateUserIds != null && dublicateUserIds.size() > 0) {
text += getString(R.string.api_select_pub_keys_dublicates_text);
text += "<br/>";
text += "<ul>";
for (String userId : dublicateUserIds) {
text += "<li>" + userId + "</li>";
}
text += "</ul>";
}
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// ok
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.OKAY;
Bundle data = new Bundle();
data.putLongArray(
OpenPgpService.SelectPubKeysActivityCallback.PUB_KEY_IDS,
mSelectFragment.getSelectedMasterKeyIds());
msg.setData(data);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
}
}, R.string.btn_doNotSave, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.CANCEL;
;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
}
});
setContentView(R.layout.api_app_select_pub_keys_activity);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
textView.setHtmlFromString(text);
/* Load select pub keys fragment */
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.api_select_pub_keys_fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create an instance of the fragment
mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds);
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.api_select_pub_keys_fragment_container, mSelectFragment).commit();
}
} else {
Log.e(Constants.TAG, "Wrong action!");
finish();
}
}
/**
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
* for a symmetric passphrase
*/
private void showPassphraseDialog(long secretKeyId) {
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.PassphraseActivityCallback.OKAY;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
} else {
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.PassphraseActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
}
finishHandled = true;
finish();
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
messenger, secretKeyId);
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
} catch (PgpMain.PgpGeneralException e) {
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
// send message to handler to start encryption directly
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class RegisteredAppsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private PackageManager pm;
public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
pm = context.getApplicationContext().getPackageManager();
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
if (packageName != null) {
// get application name
try {
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
text.setText(pm.getApplicationLabel(ai));
icon.setImageDrawable(pm.getApplicationIcon(ai));
} catch (final NameNotFoundException e) {
// fallback
text.setText(packageName);
}
} else {
// fallback
text.setText(packageName);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.MainActivity;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.MenuItem;
import android.content.Intent;
import android.os.Bundle;
public class RegisteredAppsListActivity extends SherlockFragmentActivity {
private ActionBar mActionBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActionBar = getSupportActionBar();
setContentView(R.layout.api_apps_list_activity);
mActionBar.setDisplayShowTitleEnabled(true);
mActionBar.setDisplayHomeAsUpEnabled(true);
}
/**
* Menu Options
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// app icon in Action Bar clicked; go home
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.openpgp_api;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import com.actionbarsherlock.app.SherlockListFragment;
public class RegisteredAppsListFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
RegisteredAppsAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
// edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
startActivity(intent);
}
});
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText(getString(R.string.api_no_apps));
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new RegisteredAppsAdapter(getActivity(), null, 0);
setListAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
// These are the Contacts rows that we will retrieve.
static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID,
ApiApps.PACKAGE_NAME };
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri = ApiApps.CONTENT_URI;
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null,
ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC");
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}