/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.adt.internal.preferences; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; import com.android.ide.eclipse.adt.internal.utils.FingerprintUtils; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.internal.build.DebugKeyProvider; import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.preference.FileFieldEditor; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.RadioGroupFieldEditor; import org.eclipse.jface.preference.StringFieldEditor; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Date; /** * Preference page for build options. * */ public class BuildPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { private IPreferenceStore mPrefStore = null; // default key store private ReadOnlyFieldEditor mDefaultKeyStore = null; private LabelField mDefaultFingerprintMd5 = null; private LabelField mDefaultFingerprintSha1 = null; // custom key store private KeystoreFieldEditor mCustomKeyStore = null; private LabelField mCustomFingerprintMd5 = null; private LabelField mCustomFingerprintSha1 = null; public BuildPreferencePage() { super(GRID); mPrefStore = AdtPlugin.getDefault().getPreferenceStore(); setPreferenceStore(mPrefStore); setDescription(Messages.BuildPreferencePage_Title); } @Override protected void createFieldEditors() { addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_RES_AUTO_REFRESH, Messages.BuildPreferencePage_Auto_Refresh_Resources_on_Build, getFieldEditorParent())); addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_FORCE_ERROR_ON_NATIVELIB_IN_JAR, "Force error when external jars contain native libraries", getFieldEditorParent())); addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_SKIP_POST_COMPILE_ON_FILE_SAVE, "Skip packaging and dexing until export or launch. (Speeds up automatic builds on file save)", getFieldEditorParent())); RadioGroupFieldEditor rgfe = new RadioGroupFieldEditor( AdtPrefs.PREFS_BUILD_VERBOSITY, Messages.BuildPreferencePage_Build_Output, 1, new String[][] { { Messages.BuildPreferencePage_Silent, BuildVerbosity.ALWAYS.name() }, { Messages.BuildPreferencePage_Normal, BuildVerbosity.NORMAL.name() }, { Messages.BuildPreferencePage_Verbose, BuildVerbosity.VERBOSE.name() } }, getFieldEditorParent(), true); addField(rgfe); // default debug keystore fingerprints Fingerprints defaultFingerprints = getFingerprints( mPrefStore.getString(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE)); // default debug key store fields mDefaultKeyStore = new ReadOnlyFieldEditor(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE, Messages.BuildPreferencePage_Default_KeyStore, getFieldEditorParent()); mDefaultFingerprintMd5 = new LabelField( Messages.BuildPreferencePage_Default_Certificate_Fingerprint_MD5, defaultFingerprints != null ? defaultFingerprints.md5 : "", getFieldEditorParent()); mDefaultFingerprintSha1 = new LabelField( Messages.BuildPreferencePage_Default_Certificate_Fingerprint_SHA1, defaultFingerprints != null ? defaultFingerprints.sha1 : "", getFieldEditorParent()); addField(mDefaultKeyStore); addField(mDefaultFingerprintMd5); addField(mDefaultFingerprintSha1); // custom debug keystore fingerprints Fingerprints customFingerprints = null; String customDebugKeystorePath = mPrefStore.getString(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE); if (new File(customDebugKeystorePath).isFile()) { customFingerprints = getFingerprints(customDebugKeystorePath); } else { // file does not exist. setErrorMessage("Not a valid keystore path."); } // custom debug key store fields mCustomKeyStore = new KeystoreFieldEditor(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE, Messages.BuildPreferencePage_Custom_Keystore, getFieldEditorParent()); mCustomFingerprintMd5 = new LabelField( Messages.BuildPreferencePage_Default_Certificate_Fingerprint_MD5, customFingerprints != null ? customFingerprints.md5 : "", getFieldEditorParent()); mCustomFingerprintSha1 = new LabelField( Messages.BuildPreferencePage_Default_Certificate_Fingerprint_SHA1, customFingerprints != null ? customFingerprints.sha1 : "", getFieldEditorParent()); // set fingerprint fields mCustomKeyStore.setFingerprintMd5Field(mCustomFingerprintMd5); mCustomKeyStore.setFingerprintSha1Field(mCustomFingerprintSha1); addField(mCustomKeyStore); addField(mCustomFingerprintMd5); addField(mCustomFingerprintSha1); } /** * MD5 & SHA1 fingerprints. */ private static class Fingerprints { final String md5; final String sha1; Fingerprints(String md5Val, String sha1Val) { md5 = md5Val; sha1 = sha1Val; } } private Fingerprints getFingerprints(String keystorePath) { // attempt to load the debug key. try { DebugKeyProvider keyProvider = new DebugKeyProvider(keystorePath, null /* storeType */, null /* key gen output */); return new Fingerprints( FingerprintUtils.getFingerprint(keyProvider.getCertificate(), "MD5"), FingerprintUtils.getFingerprint(keyProvider.getCertificate(), "SHA1")); } catch (GeneralSecurityException e) { setErrorMessage(e.getMessage()); } catch (IOException e) { setErrorMessage(e.getMessage()); } catch (KeytoolException e) { setErrorMessage(e.getMessage()); } catch (AndroidLocationException e) { setErrorMessage(e.getMessage()); } return null; } /* * (non-Javadoc) * * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) */ @Override public void init(IWorkbench workbench) { } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditorPreferencePage#performDefaults * (org.eclipse.jface.preference.PreferencePage#performDefaults) */ @Override protected void performDefaults() { super.performDefaults(); // restore the default key store fingerprints Fingerprints defaultFingerprints = getFingerprints(mPrefStore .getString(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE)); mDefaultFingerprintMd5.setStringValue(defaultFingerprints.md5); mDefaultFingerprintSha1.setStringValue(defaultFingerprints.sha1); // set custom fingerprint fields to blank mCustomFingerprintMd5.setStringValue(""); mCustomFingerprintSha1.setStringValue(""); } /** * A read-only string field editor. */ private static class ReadOnlyFieldEditor extends StringFieldEditor { public ReadOnlyFieldEditor(String name, String labelText, Composite parent) { super(name, labelText, parent); } @Override protected void createControl(Composite parent) { super.createControl(parent); Text control = getTextControl(); control.setEditable(false); } } /** * A read-only string field. */ private static class LabelField extends StringFieldEditor { private String text; public LabelField(String labelText, String value, Composite parent) { super("", labelText, parent); text = value; } @Override protected void createControl(Composite parent) { super.createControl(parent); Text control = getTextControl(); control.setEditable(false); } @Override protected void doLoad() { setStringValue(text); } @Override protected void doStore() { // Do nothing } } /** * Custom {@link FileFieldEditor} that checks that the keystore is valid. */ private static class KeystoreFieldEditor extends FileFieldEditor { private StringFieldEditor fingerprintMd5 = null; private StringFieldEditor fingerprintSha1 = null; public void setFingerprintMd5Field(StringFieldEditor field) { fingerprintMd5 = field; } public void setFingerprintSha1Field(StringFieldEditor field) { fingerprintSha1 = field; } public KeystoreFieldEditor(String name, String label, Composite parent) { super(name, label, parent); setValidateStrategy(VALIDATE_ON_KEY_STROKE); } @Override protected boolean checkState() { String fileName = getTextControl().getText(); fileName = fileName.trim(); if (fingerprintMd5 != null) { fingerprintMd5.setStringValue(""); } if (fingerprintSha1 != null) { fingerprintSha1.setStringValue(""); } // empty values are considered ok. if (fileName.length() > 0) { File file = new File(fileName); if (file.isFile()) { // attempt to load the debug key. try { DebugKeyProvider provider = new DebugKeyProvider(fileName, null /* storeType */, null /* key gen output */); PrivateKey key = provider.getDebugKey(); X509Certificate certificate = (X509Certificate)provider.getCertificate(); if (key == null || certificate == null) { showErrorMessage("Unable to find debug key in keystore!"); return false; } if (fingerprintMd5 != null) { fingerprintMd5.setStringValue( FingerprintUtils.getFingerprint(certificate, "MD5")); } if (fingerprintSha1 != null) { fingerprintSha1.setStringValue( FingerprintUtils.getFingerprint(certificate, "SHA1")); } Date today = new Date(); if (certificate.getNotAfter().compareTo(today) < 0) { showErrorMessage("Certificate is expired!"); return false; } if (certificate.getNotBefore().compareTo(today) > 0) { showErrorMessage("Certificate validity is in the future!"); return false; } // we're good! clearErrorMessage(); return true; } catch (GeneralSecurityException e) { handleException(e); return false; } catch (IOException e) { handleException(e); return false; } catch (KeytoolException e) { handleException(e); return false; } catch (AndroidLocationException e) { handleException(e); return false; } } else { // file does not exist. showErrorMessage("Not a valid keystore path."); return false; // Apply/OK must be disabled } } clearErrorMessage(); return true; } @Override public Text getTextControl(Composite parent) { setValidateStrategy(VALIDATE_ON_KEY_STROKE); return super.getTextControl(parent); } /** * Set the error message from a {@link Throwable}. If the exception has no message, try * to get the message from the cause. * @param t the Throwable. */ private void handleException(Throwable t) { String msg = t.getMessage(); if (msg == null) { Throwable cause = t.getCause(); if (cause != null) { handleException(cause); } else { setErrorMessage("Uknown error when getting the debug key!"); } return; } // valid text, display it. showErrorMessage(msg); } } }