• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BuildPreferencePage()64     public BuildPreferencePage() {
65         super(GRID);
66         mPrefStore = AdtPlugin.getDefault().getPreferenceStore();
67         setPreferenceStore(mPrefStore);
68         setDescription(Messages.BuildPreferencePage_Title);
69     }
70 
71     @Override
createFieldEditors()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 
Fingerprints(String md5Val, String sha1Val)154         Fingerprints(String md5Val, String sha1Val) {
155             md5 = md5Val;
156             sha1 = sha1Val;
157         }
158     }
159 
getFingerprints(String keystorePath)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
init(IWorkbench workbench)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
performDefaults()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 
ReadOnlyFieldEditor(String name, String labelText, Composite parent)216         public ReadOnlyFieldEditor(String name, String labelText, Composite parent) {
217             super(name, labelText, parent);
218         }
219 
220         @Override
createControl(Composite parent)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 
LabelField(String labelText, String value, Composite parent)235         public LabelField(String labelText, String value, Composite parent) {
236             super("", labelText, parent);
237             text = value;
238         }
239 
240         @Override
createControl(Composite parent)241         protected void createControl(Composite parent) {
242             super.createControl(parent);
243 
244             Text control = getTextControl();
245             control.setEditable(false);
246         }
247 
248         @Override
doLoad()249         protected void doLoad() {
250             setStringValue(text);
251         }
252 
253         @Override
doStore()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 
setFingerprintMd5Field(StringFieldEditor field)266         public void setFingerprintMd5Field(StringFieldEditor field) {
267             fingerprintMd5 = field;
268         }
269 
setFingerprintSha1Field(StringFieldEditor field)270         public void setFingerprintSha1Field(StringFieldEditor field) {
271             fingerprintSha1 = field;
272         }
273 
KeystoreFieldEditor(String name, String label, Composite parent)274         public KeystoreFieldEditor(String name, String label, Composite parent) {
275             super(name, label, parent);
276             setValidateStrategy(VALIDATE_ON_KEY_STROKE);
277         }
278 
279         @Override
checkState()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
getTextControl(Composite parent)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          */
handleException(Throwable t)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