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 }