Home | History | Annotate | Download | only in installapp
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
      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 package com.motorola.studio.android.wizards.installapp;
     17 
     18 import java.io.IOException;
     19 import java.util.ArrayList;
     20 import java.util.Enumeration;
     21 import java.util.List;
     22 import java.util.jar.JarEntry;
     23 import java.util.jar.JarFile;
     24 
     25 import org.eclipse.core.runtime.Path;
     26 import org.eclipse.jface.dialogs.DialogPage;
     27 import org.eclipse.jface.wizard.WizardPage;
     28 import org.eclipse.swt.SWT;
     29 import org.eclipse.swt.events.ModifyEvent;
     30 import org.eclipse.swt.events.ModifyListener;
     31 import org.eclipse.swt.events.SelectionAdapter;
     32 import org.eclipse.swt.events.SelectionEvent;
     33 import org.eclipse.swt.layout.GridData;
     34 import org.eclipse.swt.layout.GridLayout;
     35 import org.eclipse.swt.widgets.Button;
     36 import org.eclipse.swt.widgets.Composite;
     37 import org.eclipse.swt.widgets.Group;
     38 import org.eclipse.ui.PlatformUI;
     39 
     40 import com.motorola.studio.android.AndroidPlugin;
     41 import com.motorola.studio.android.common.log.StudioLogger;
     42 import com.motorola.studio.android.i18n.AndroidNLS;
     43 import com.motorola.studio.android.wizards.elements.FileChooser;
     44 import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
     45 
     46 /**
     47  * Wizard Page used by Deploy Wizard
     48  */
     49 public class DeployWizardPage extends WizardPage
     50 {
     51     private FileChooser fileChooser = null;
     52 
     53     private String initialPackagePath = null;
     54 
     55     private String packageSelectionMessage = null;
     56 
     57     private String packagetext = null;
     58 
     59     private final String packageExtension = "apk";
     60 
     61     private Button overwiteRadio = null;
     62 
     63     private Button uninstallRadio = null;
     64 
     65     private Button doNothingRadio = null;
     66 
     67     private static final String contextId = AndroidPlugin.PLUGIN_ID + ".install_app";
     68 
     69     private static INSTALL_TYPE installType;
     70 
     71     private final String DSA_FILE_EXTENSION = ".DSA";
     72 
     73     private final String RSA_FILE_EXTENSION = ".RSA";
     74 
     75     private final String SF_FILE_EXTENSION = ".SF";
     76 
     77     private static String lastUsedPackage = null;
     78 
     79     /**
     80      * Constructor
     81      *
     82      * @param initialPackagePath
     83      * @param selectPCKMessage
     84      *            Message asking for package selection
     85      */
     86     public DeployWizardPage(String initialPackagePath, String selectPCKMessage,
     87             String browseButtonText, String packagetext)
     88     {
     89         super("");
     90 
     91         if ((browseButtonText == null) || (packagetext == null) || (selectPCKMessage == null))
     92         {
     93             throw new IllegalArgumentException("Could not create Deploy Wizard: null argument");
     94         }
     95 
     96         if (initialPackagePath == null)
     97         {
     98             this.initialPackagePath = lastUsedPackage != null ? lastUsedPackage : "";
     99         }
    100         else
    101         {
    102             this.initialPackagePath = initialPackagePath;
    103         }
    104 
    105         this.packagetext = packagetext;
    106         packageSelectionMessage = selectPCKMessage;
    107 
    108     }
    109 
    110     /*
    111      * (non-Javadoc)
    112      *
    113      * @see
    114      * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
    115      * .Composite)
    116      */
    117     public void createControl(Composite parent)
    118     {
    119         // Main composite for the UI
    120         Composite mainComposite = new Composite(parent, SWT.FILL);
    121 
    122         PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, contextId);
    123 
    124         mainComposite.setLayout(new GridLayout());
    125         mainComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL, GridData.FILL_VERTICAL,
    126                 true, true));
    127 
    128         // Package group
    129         Group packageGroup = new Group(mainComposite, SWT.NONE);
    130         packageGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    131         packageGroup.setLayout(new GridLayout(3, false));
    132         packageGroup.setText(packagetext);
    133 
    134         fileChooser = new FileChooser(packageGroup, SWT.NONE, null);
    135         fileChooser.setFilterExtensions(new String[]
    136         {
    137             "*." + packageExtension
    138         });
    139         fileChooser.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
    140         fileChooser.addModifyListener(new ModifyListener()
    141         {
    142             public void modifyText(ModifyEvent e)
    143             {
    144                 validateSelection();
    145             }
    146         });
    147         createOptionButtons(mainComposite);
    148 
    149         mainComposite.pack();
    150 
    151         setPageComplete(false);
    152         loadInitialValues();
    153 
    154         setControl(mainComposite);
    155     }
    156 
    157     private void createOptionButtons(Composite mainComposite)
    158     {
    159 
    160         GridData data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
    161         overwiteRadio = new Button(mainComposite, SWT.RADIO);
    162         overwiteRadio.setText(AndroidNLS.UI_DeployWizardPage_ReplaceApp);
    163         overwiteRadio.setSelection(true);
    164         overwiteRadio.setLayoutData(data);
    165         overwiteRadio.setData(INSTALL_TYPE.OVERWRITE);
    166         overwiteRadio.addSelectionListener(new SelectionAdapter()
    167         {
    168             /*
    169              * (non-Javadoc)
    170              *
    171              * @see
    172              * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
    173              * .swt.events.SelectionEvent)
    174              */
    175             @Override
    176             public void widgetSelected(SelectionEvent e)
    177             {
    178                 installType = (INSTALL_TYPE) overwiteRadio.getData();
    179             }
    180 
    181         });
    182 
    183         uninstallRadio = new Button(mainComposite, SWT.RADIO);
    184         uninstallRadio.setText(AndroidNLS.UI_DeployWizardPage_UninstallApp);
    185         uninstallRadio.setSelection(true);
    186         uninstallRadio.setLayoutData(data);
    187         uninstallRadio.setData(INSTALL_TYPE.UNINSTALL);
    188         uninstallRadio.addSelectionListener(new SelectionAdapter()
    189         {
    190             /*
    191              * (non-Javadoc)
    192              *
    193              * @see
    194              * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
    195              * .swt.events.SelectionEvent)
    196              */
    197             @Override
    198             public void widgetSelected(SelectionEvent e)
    199             {
    200                 installType = (INSTALL_TYPE) uninstallRadio.getData();
    201             }
    202 
    203         });
    204 
    205         doNothingRadio = new Button(mainComposite, SWT.RADIO);
    206         doNothingRadio.setText(AndroidNLS.UI_DeployWizardPage_DoNothingApp);
    207         doNothingRadio.setSelection(true);
    208         doNothingRadio.setLayoutData(data);
    209         doNothingRadio.setData(INSTALL_TYPE.DO_NOTHING);
    210         doNothingRadio.addSelectionListener(new SelectionAdapter()
    211         {
    212             /*
    213              * (non-Javadoc)
    214              *
    215              * @see
    216              * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
    217              * .swt.events.SelectionEvent)
    218              */
    219             @Override
    220             public void widgetSelected(SelectionEvent e)
    221             {
    222                 installType = (INSTALL_TYPE) doNothingRadio.getData();
    223             }
    224 
    225         });
    226         overwiteRadio.setSelection(true);
    227         uninstallRadio.setSelection(false);
    228         doNothingRadio.setSelection(false);
    229         installType = (INSTALL_TYPE.OVERWRITE);
    230 
    231     }
    232 
    233     /**
    234      * Load the initial values to be filled in the wizard
    235      */
    236     private void loadInitialValues()
    237     {
    238         if ((initialPackagePath != null) && (initialPackagePath.length() != 0))
    239         {
    240             fileChooser.setText(initialPackagePath);
    241             validateSelection();
    242         }
    243         else
    244         {
    245             setMessage(packageSelectionMessage, DialogPage.NONE);
    246         }
    247     }
    248 
    249     /**
    250      * Validates the selected package and device instance setting the
    251      * appropriated messages and errors
    252      */
    253     private synchronized void validateSelection()
    254     {
    255         String packagePath = fileChooser.getText();
    256 
    257         if (isValidPackage(packagePath))
    258         {
    259             setErrorMessage(null);
    260             setPageComplete(true);
    261         }
    262         else
    263         {
    264             setPageComplete(false);
    265         }
    266     }
    267 
    268     /**
    269      * Verify if a package is valid MPKG
    270      *
    271      * @param packagePath
    272      * @return TRUE if the package is valid or FALSE otherwise
    273      */
    274     private boolean isValidPackage(String packagePath)
    275     {
    276         boolean result = false;
    277         Path path = new Path(packagePath);
    278         String extension = path.getFileExtension();
    279 
    280         // testing if the entered path is a folder
    281         result = path.toFile().isFile();
    282         if (!result)
    283         {
    284             setErrorMessage(AndroidNLS.UI_DeployWizardPage_PackageIsAFolder);
    285         }
    286         else
    287         {
    288             result =
    289                     ((extension != null) && extension.equals(packageExtension) && path
    290                             .isValidPath(path.toString()));
    291 
    292             if (!result)
    293             {
    294                 setErrorMessage(AndroidNLS.UI_DeployWizardPage_InvalidPath);
    295             }
    296             else
    297             {
    298                 // Test if file exists
    299                 result = path.toFile().exists();
    300                 if (!result)
    301                 {
    302                     setErrorMessage(AndroidNLS.UI_DeployWizardPage_FileDoesNotExist);
    303                 }
    304             }
    305         }
    306 
    307         if (result)
    308         {
    309             setMessage("", DialogPage.NONE);
    310 
    311             //Test if the package is valid
    312             result = isPackageSigned(packagePath);
    313             if (!result)
    314             {
    315                 setErrorMessage(AndroidNLS.UI_DeployWizardPage_NotSignedMessage);
    316             }
    317         }
    318 
    319         return result;
    320     }
    321 
    322     /**
    323      * Verify if the package is signed based on the
    324      * existence of an .SF file and a corresponding
    325      * DSA or RSA file.
    326      *
    327      * @param packagePath
    328      * @return TRUE if the package is signed
    329      */
    330     private synchronized boolean isPackageSigned(String packagePath)
    331     {
    332         // Temporary placeholders for the package entries
    333         List<String> SFFiles = new ArrayList<String>();
    334         List<String> RSAFiles = new ArrayList<String>();
    335         List<String> DSAFiles = new ArrayList<String>();
    336 
    337         //Temporary result
    338         boolean result = false;
    339         JarFile jar = null;
    340         try
    341         {
    342             jar = new JarFile(packagePath, false);
    343             Enumeration<JarEntry> enu = jar.entries();
    344 
    345             //interact over the elements of the package
    346             while (enu.hasMoreElements())
    347             {
    348                 JarEntry entry = enu.nextElement();
    349                 if (entry.getName().toUpperCase().endsWith(SF_FILE_EXTENSION))
    350                 {
    351                     // Mounts the list of SF files
    352                     SFFiles.add(entry.getName().toUpperCase());
    353                 }
    354                 else if (entry.getName().toUpperCase().endsWith(RSA_FILE_EXTENSION))
    355                 {
    356                     // Mounts the list of RSA files
    357                     RSAFiles.add(entry.getName().toUpperCase());
    358                 }
    359                 else if (entry.getName().toUpperCase().endsWith(DSA_FILE_EXTENSION))
    360                 {
    361                     // Mounts the list of DSA files
    362                     DSAFiles.add(entry.getName().toUpperCase());
    363                 }
    364             }
    365 
    366             if (!SFFiles.isEmpty())
    367             {
    368                 for (String sfFile : SFFiles)
    369                 {
    370                     // Interacts over the list of SF files until it ends or until a correspondent DSA or RSA is found
    371                     Path p = new Path(sfFile);
    372                     sfFile = p.removeFileExtension().toString();
    373                     result =
    374                             (DSAFiles.contains(sfFile + DSA_FILE_EXTENSION) || RSAFiles
    375                                     .contains(sfFile + RSA_FILE_EXTENSION));
    376                 }
    377             }
    378 
    379         }
    380         catch (Exception e)
    381         {
    382             // Could not read the jar file
    383             StudioLogger.error(DeployWizardPage.class, "Deploy: Could not verify file "
    384                     + packagePath, e);
    385         }
    386         finally
    387         {
    388             if (jar != null)
    389             {
    390                 try
    391                 {
    392                     jar.close();
    393                 }
    394                 catch (IOException e)
    395                 {
    396                     StudioLogger.error(DeployWizardPage.class,
    397                             "Error closing package after verification", e);
    398                 }
    399             }
    400         }
    401 
    402         return result;
    403 
    404     }
    405 
    406     /**
    407      * Gets the selected package path
    408      *
    409      * @return the package Path
    410      */
    411     public String getPackagePath()
    412     {
    413         String packagePath = fileChooser.getText();
    414         if (isValidPackage(packagePath))
    415         {
    416             lastUsedPackage = packagePath;
    417         }
    418         return packagePath;
    419     }
    420 
    421     /**
    422      * Return true if the application should be replaced in the case it is
    423      * already installed on the device
    424      *
    425      */
    426     public INSTALL_TYPE canOverwrite()
    427     {
    428         return installType;
    429     }
    430 }