Home | History | Annotate | Download | only in preferences
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.preferences;
     18 
     19 import com.android.ide.eclipse.adt.AdtPlugin;
     20 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
     21 import com.android.ide.eclipse.adt.internal.utils.FingerprintUtils;
     22 import com.android.prefs.AndroidLocation.AndroidLocationException;
     23 import com.android.sdklib.internal.build.DebugKeyProvider;
     24 import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
     25 
     26 import org.eclipse.jface.preference.BooleanFieldEditor;
     27 import org.eclipse.jface.preference.FieldEditorPreferencePage;
     28 import org.eclipse.jface.preference.FileFieldEditor;
     29 import org.eclipse.jface.preference.IPreferenceStore;
     30 import org.eclipse.jface.preference.RadioGroupFieldEditor;
     31 import org.eclipse.jface.preference.StringFieldEditor;
     32 import org.eclipse.swt.widgets.Composite;
     33 import org.eclipse.swt.widgets.Text;
     34 import org.eclipse.ui.IWorkbench;
     35 import org.eclipse.ui.IWorkbenchPreferencePage;
     36 
     37 import java.io.File;
     38 import java.io.IOException;
     39 import java.security.GeneralSecurityException;
     40 import java.security.PrivateKey;
     41 import java.security.cert.Certificate;
     42 import java.security.cert.X509Certificate;
     43 import java.util.Date;
     44 
     45 /**
     46  * Preference page for build options.
     47  *
     48  */
     49 public class BuildPreferencePage extends FieldEditorPreferencePage implements
     50         IWorkbenchPreferencePage {
     51 
     52     private IPreferenceStore mPrefStore = null;
     53 
     54     // default key store
     55     private ReadOnlyFieldEditor mDefaultKeyStore = null;
     56     private LabelField mDefaultFingerprintMd5 = null;
     57     private LabelField mDefaultFingerprintSha1 = null;
     58 
     59     // custom key store
     60     private KeystoreFieldEditor mCustomKeyStore = null;
     61     private LabelField mCustomFingerprintMd5 = null;
     62     private LabelField mCustomFingerprintSha1 = null;
     63 
     64     public BuildPreferencePage() {
     65         super(GRID);
     66         mPrefStore = AdtPlugin.getDefault().getPreferenceStore();
     67         setPreferenceStore(mPrefStore);
     68         setDescription(Messages.BuildPreferencePage_Title);
     69     }
     70 
     71     @Override
     72     protected void createFieldEditors() {
     73         addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_RES_AUTO_REFRESH,
     74                 Messages.BuildPreferencePage_Auto_Refresh_Resources_on_Build,
     75                 getFieldEditorParent()));
     76 
     77         addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_FORCE_ERROR_ON_NATIVELIB_IN_JAR,
     78                 "Force error when external jars contain native libraries",
     79                 getFieldEditorParent()));
     80 
     81         addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_SKIP_POST_COMPILE_ON_FILE_SAVE,
     82                 "Skip packaging and dexing until export or launch. (Speeds up automatic builds on file save)",
     83                 getFieldEditorParent()));
     84 
     85         RadioGroupFieldEditor rgfe = new RadioGroupFieldEditor(
     86                 AdtPrefs.PREFS_BUILD_VERBOSITY,
     87                 Messages.BuildPreferencePage_Build_Output, 1, new String[][] {
     88                     { Messages.BuildPreferencePage_Silent, BuildVerbosity.ALWAYS.name() },
     89                     { Messages.BuildPreferencePage_Normal, BuildVerbosity.NORMAL.name() },
     90                     { Messages.BuildPreferencePage_Verbose, BuildVerbosity.VERBOSE.name() }
     91                     },
     92                 getFieldEditorParent(), true);
     93         addField(rgfe);
     94 
     95         // default debug keystore fingerprints
     96         Fingerprints defaultFingerprints = getFingerprints(
     97                 mPrefStore.getString(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE));
     98 
     99         // default debug key store fields
    100         mDefaultKeyStore = new ReadOnlyFieldEditor(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE,
    101                 Messages.BuildPreferencePage_Default_KeyStore, getFieldEditorParent());
    102         mDefaultFingerprintMd5 = new LabelField(
    103                 Messages.BuildPreferencePage_Default_Certificate_Fingerprint_MD5,
    104                 defaultFingerprints != null ? defaultFingerprints.md5 : "",
    105                 getFieldEditorParent());
    106         mDefaultFingerprintSha1 = new LabelField(
    107                 Messages.BuildPreferencePage_Default_Certificate_Fingerprint_SHA1,
    108                 defaultFingerprints != null ? defaultFingerprints.sha1 : "",
    109                 getFieldEditorParent());
    110 
    111         addField(mDefaultKeyStore);
    112         addField(mDefaultFingerprintMd5);
    113         addField(mDefaultFingerprintSha1);
    114 
    115         // custom debug keystore fingerprints
    116         Fingerprints customFingerprints = null;
    117 
    118         String customDebugKeystorePath = mPrefStore.getString(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE);
    119         if (new File(customDebugKeystorePath).isFile()) {
    120             customFingerprints = getFingerprints(customDebugKeystorePath);
    121         } else {
    122             // file does not exist.
    123             setErrorMessage("Not a valid keystore path.");
    124         }
    125 
    126         // custom debug key store fields
    127         mCustomKeyStore = new KeystoreFieldEditor(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE,
    128                 Messages.BuildPreferencePage_Custom_Keystore, getFieldEditorParent());
    129         mCustomFingerprintMd5 = new LabelField(
    130                 Messages.BuildPreferencePage_Default_Certificate_Fingerprint_MD5,
    131                 customFingerprints != null ? customFingerprints.md5 : "",
    132                 getFieldEditorParent());
    133         mCustomFingerprintSha1 = new LabelField(
    134                 Messages.BuildPreferencePage_Default_Certificate_Fingerprint_SHA1,
    135                 customFingerprints != null ? customFingerprints.sha1 : "",
    136                 getFieldEditorParent());
    137 
    138         // set fingerprint fields
    139         mCustomKeyStore.setFingerprintMd5Field(mCustomFingerprintMd5);
    140         mCustomKeyStore.setFingerprintSha1Field(mCustomFingerprintSha1);
    141 
    142         addField(mCustomKeyStore);
    143         addField(mCustomFingerprintMd5);
    144         addField(mCustomFingerprintSha1);
    145     }
    146 
    147     /**
    148      * MD5 & SHA1 fingerprints.
    149      */
    150     private static class Fingerprints {
    151         final String md5;
    152         final String sha1;
    153 
    154         Fingerprints(String md5Val, String sha1Val) {
    155             md5 = md5Val;
    156             sha1 = sha1Val;
    157         }
    158     }
    159 
    160     private Fingerprints getFingerprints(String keystorePath) {
    161         // attempt to load the debug key.
    162         try {
    163             DebugKeyProvider keyProvider = new DebugKeyProvider(keystorePath,
    164                     null /* storeType */, null /* key gen output */);
    165 
    166             return new Fingerprints(
    167                     FingerprintUtils.getFingerprint(keyProvider.getCertificate(), "MD5"),
    168                     FingerprintUtils.getFingerprint(keyProvider.getCertificate(), "SHA1"));
    169         } catch (GeneralSecurityException e) {
    170             setErrorMessage(e.getMessage());
    171         } catch (IOException e) {
    172             setErrorMessage(e.getMessage());
    173         } catch (KeytoolException e) {
    174             setErrorMessage(e.getMessage());
    175         } catch (AndroidLocationException e) {
    176             setErrorMessage(e.getMessage());
    177         }
    178         return null;
    179     }
    180 
    181     /*
    182      * (non-Javadoc)
    183      *
    184      * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
    185      */
    186     @Override
    187     public void init(IWorkbench workbench) {
    188     }
    189 
    190     /*
    191      * (non-Javadoc)
    192      *
    193      * @see org.eclipse.jface.preference.FieldEditorPreferencePage#performDefaults
    194      * (org.eclipse.jface.preference.PreferencePage#performDefaults)
    195      */
    196     @Override
    197     protected void performDefaults() {
    198         super.performDefaults();
    199 
    200         // restore the default key store fingerprints
    201         Fingerprints defaultFingerprints = getFingerprints(mPrefStore
    202                 .getString(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE));
    203         mDefaultFingerprintMd5.setStringValue(defaultFingerprints.md5);
    204         mDefaultFingerprintSha1.setStringValue(defaultFingerprints.sha1);
    205 
    206         // set custom fingerprint fields to blank
    207         mCustomFingerprintMd5.setStringValue("");
    208         mCustomFingerprintSha1.setStringValue("");
    209     }
    210 
    211     /**
    212      * A read-only string field editor.
    213      */
    214     private static class ReadOnlyFieldEditor extends StringFieldEditor {
    215 
    216         public ReadOnlyFieldEditor(String name, String labelText, Composite parent) {
    217             super(name, labelText, parent);
    218         }
    219 
    220         @Override
    221         protected void createControl(Composite parent) {
    222             super.createControl(parent);
    223 
    224             Text control = getTextControl();
    225             control.setEditable(false);
    226         }
    227     }
    228 
    229     /**
    230      * A read-only string field.
    231      */
    232     private static class LabelField extends StringFieldEditor {
    233         private String text;
    234 
    235         public LabelField(String labelText, String value, Composite parent) {
    236             super("", labelText, parent);
    237             text = value;
    238         }
    239 
    240         @Override
    241         protected void createControl(Composite parent) {
    242             super.createControl(parent);
    243 
    244             Text control = getTextControl();
    245             control.setEditable(false);
    246         }
    247 
    248         @Override
    249         protected void doLoad() {
    250             setStringValue(text);
    251         }
    252 
    253         @Override
    254         protected void doStore() {
    255             // Do nothing
    256         }
    257     }
    258 
    259     /**
    260      * Custom {@link FileFieldEditor} that checks that the keystore is valid.
    261      */
    262     private static class KeystoreFieldEditor extends FileFieldEditor {
    263         private StringFieldEditor fingerprintMd5 = null;
    264         private StringFieldEditor fingerprintSha1 = null;
    265 
    266         public void setFingerprintMd5Field(StringFieldEditor field) {
    267             fingerprintMd5 = field;
    268         }
    269 
    270         public void setFingerprintSha1Field(StringFieldEditor field) {
    271             fingerprintSha1 = field;
    272         }
    273 
    274         public KeystoreFieldEditor(String name, String label, Composite parent) {
    275             super(name, label, parent);
    276             setValidateStrategy(VALIDATE_ON_KEY_STROKE);
    277         }
    278 
    279         @Override
    280         protected boolean checkState() {
    281             String fileName = getTextControl().getText();
    282             fileName = fileName.trim();
    283 
    284             if (fingerprintMd5 != null) {
    285                 fingerprintMd5.setStringValue("");
    286             }
    287 
    288             if (fingerprintSha1 != null) {
    289                 fingerprintSha1.setStringValue("");
    290             }
    291 
    292             // empty values are considered ok.
    293             if (fileName.length() > 0) {
    294                 File file = new File(fileName);
    295                 if (file.isFile()) {
    296                     // attempt to load the debug key.
    297                     try {
    298                         DebugKeyProvider provider = new DebugKeyProvider(fileName,
    299                                 null /* storeType */, null /* key gen output */);
    300                         PrivateKey key = provider.getDebugKey();
    301                         X509Certificate certificate = (X509Certificate)provider.getCertificate();
    302 
    303                         if (key == null || certificate == null) {
    304                             showErrorMessage("Unable to find debug key in keystore!");
    305                             return false;
    306                         }
    307 
    308                         if (fingerprintMd5 != null) {
    309                             fingerprintMd5.setStringValue(
    310                                     FingerprintUtils.getFingerprint(certificate, "MD5"));
    311                         }
    312 
    313                         if (fingerprintSha1 != null) {
    314                             fingerprintSha1.setStringValue(
    315                                     FingerprintUtils.getFingerprint(certificate, "SHA1"));
    316                         }
    317 
    318                         Date today = new Date();
    319                         if (certificate.getNotAfter().compareTo(today) < 0) {
    320                             showErrorMessage("Certificate is expired!");
    321                             return false;
    322                         }
    323 
    324                         if (certificate.getNotBefore().compareTo(today) > 0) {
    325                             showErrorMessage("Certificate validity is in the future!");
    326                             return false;
    327                         }
    328 
    329                         // we're good!
    330                         clearErrorMessage();
    331                         return true;
    332                     } catch (GeneralSecurityException e) {
    333                         handleException(e);
    334                         return false;
    335                     } catch (IOException e) {
    336                         handleException(e);
    337                         return false;
    338                     } catch (KeytoolException e) {
    339                         handleException(e);
    340                         return false;
    341                     } catch (AndroidLocationException e) {
    342                         handleException(e);
    343                         return false;
    344                     }
    345 
    346 
    347                 } else {
    348                     // file does not exist.
    349                     showErrorMessage("Not a valid keystore path.");
    350                     return false;  // Apply/OK must be disabled
    351                 }
    352             }
    353 
    354             clearErrorMessage();
    355             return true;
    356         }
    357 
    358         @Override
    359         public Text getTextControl(Composite parent) {
    360             setValidateStrategy(VALIDATE_ON_KEY_STROKE);
    361             return super.getTextControl(parent);
    362         }
    363 
    364         /**
    365          * Set the error message from a {@link Throwable}. If the exception has no message, try
    366          * to get the message from the cause.
    367          * @param t the Throwable.
    368          */
    369         private void handleException(Throwable t) {
    370             String msg = t.getMessage();
    371             if (msg == null) {
    372                 Throwable cause = t.getCause();
    373                 if (cause != null) {
    374                     handleException(cause);
    375                 } else {
    376                     setErrorMessage("Uknown error when getting the debug key!");
    377                 }
    378 
    379                 return;
    380             }
    381 
    382             // valid text, display it.
    383             showErrorMessage(msg);
    384         }
    385     }
    386 }
    387