/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.motorola.studio.android.adt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.NullSdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdkuilib.internal.widgets.MessageBoxLog;
import com.motorola.studio.android.AndroidPlugin;
import com.motorola.studio.android.common.IAndroidConstants;
import com.motorola.studio.android.common.exception.AndroidException;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.common.utilities.FileUtil;
import com.motorola.studio.android.i18n.AndroidNLS;
import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
import com.motorola.studio.android.model.manifest.AndroidManifestFile;
import com.motorola.studio.android.model.manifest.dom.AndroidManifestNode;
import com.motorola.studio.android.model.manifest.dom.UsesSDKNode;
/**
* DESCRIPTION: This class provides utility methods related to
* the Android SDK.
* <br>
* USAGE: See public methods
*/
public class SdkUtils
{
public static final int API_LEVEL_FOR_PLATFORM_VERSION_3_0_0 = 11;
public static final String VM_CONFIG_FILENAME = "config.ini"; //$NON-NLS-1$
public static final String USERIMAGE_FILENAME = "userdata-qemu.img"; //$NON-NLS-1$
public static final String[] STATEDATA_FILENAMES =
{
"cache.img", "userdata.img", "emulator-user.ini" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
};
public static final String EMU_CONFIG_SKIN_NAME_PROPERTY = "skin.name"; //$NON-NLS-1$
/**
* Gets the current SDK object
*/
public static Sdk getCurrentSdk()
{
return Sdk.getCurrent();
}
/**
* Gets the directory where the configured SDK is located
*/
public static String getSdkPath()
{
String sdkDir = null;
Sdk sdk = getCurrentSdk();
if (sdk != null)
{
sdkDir = sdk.getSdkLocation();
}
return sdkDir;
}
/**
* Gets the path to the "tools" folder of the SDK
* @return
*/
public static String getSdkToolsPath()
{
return AdtPlugin.getOsSdkToolsFolder();
}
public static IAndroidTarget getTargetByAPILevel(Integer apiLevel)
{
IAndroidTarget returnTarget = null;
for (IAndroidTarget target : getAllTargets())
{
if (target.getVersion().getApiLevel() == apiLevel)
{
returnTarget = target;
}
}
return returnTarget;
}
/**
* Get the AAPT application path from an android target
* if null is passed, try to get some aapt
* @param target
* @return
*/
public static String getTargetAAPTPath(IAndroidTarget target)
{
IAndroidTarget realTarget = null;
if (target == null)
{
StudioLogger.warn(SdkUtils.class, "Trying to find a suitable aapt application to use"); //$NON-NLS-1$
IAndroidTarget[] allTargets = Sdk.getCurrent().getTargets();
if (allTargets.length > 0)
{
realTarget = allTargets[0];
}
}
else
{
realTarget = target;
}
while ((realTarget != null) && !realTarget.isPlatform())
{
realTarget = realTarget.getParent();
}
if (realTarget == null)
{
StudioLogger.warn(SdkUtils.class,
"No aapt executable found: do you have an Android platform installed?"); //$NON-NLS-1$
}
return realTarget != null ? realTarget.getPath(IAndroidTarget.AAPT) : null;
}
/**
* Gets the path to the "adb" executable of the SDK
* @return
*/
public static String getAdbPath()
{
return AdtPlugin.getOsAbsoluteAdb();
}
/**
* Reloads the recognized AVD list
*/
public static void reloadAvds()
{
try
{
getVmManager().reloadAvds(NullSdkLog.getLogger());
}
catch (Exception e)
{
StudioLogger.error(SdkUtils.class, "Error while reloading AVDs"); //$NON-NLS-1$
}
}
/**
* Gets the VmManager object
*/
public static AvdManager getVmManager()
{
AvdManager vmManager = null;
Sdk sdk = getCurrentSdk();
if (sdk != null)
{
vmManager = sdk.getAvdManager();
}
return vmManager;
}
/**
* Gets all available Targets
*/
public static IAndroidTarget[] getAllTargets()
{
IAndroidTarget[] allTargets = null;
Sdk sdk = getCurrentSdk();
if (sdk != null)
{
allTargets = sdk.getTargets();
}
return allTargets;
}
/**
* Gets a Target by name
*
* @param name the target name
*/
public static IAndroidTarget getTargetByName(String name)
{
IAndroidTarget ret = null;
if ((name != null) && (name.length() > 0))
{
IAndroidTarget[] allTargets = getAllTargets();
for (int i = 0; i < allTargets.length; i++)
{
if (name.equals(allTargets[i].getName()))
{
ret = allTargets[i];
break;
}
}
}
return ret;
}
/**
* Gets all VMs
*/
public static AvdInfo[] getAllVms()
{
AvdInfo[] allVmInfo = null;
AvdManager vmManager = getVmManager();
if (vmManager != null)
{
allVmInfo = vmManager.getAllAvds();
}
return allVmInfo;
}
/**
* Gets all valid VMs
*/
public static AvdInfo[] getAllValidVms()
{
AvdInfo[] validVmInfo = null;
AvdManager vmManager = getVmManager();
if (vmManager != null)
{
validVmInfo = vmManager.getValidAvds();
}
return validVmInfo;
}
/**
* Gets the name of all VMs that are recognized by the configured SDK.
*/
public static Collection<String> getAllVmNames()
{
Collection<String> vmNames = new LinkedList<String>();
for (AvdInfo vm : getAllVms())
{
vmNames.add(vm.getName());
}
return vmNames;
}
/**
* Gets the name of all VMs that are recognized by the configured SDK.
*/
public static Collection<String> getAllValidVmNames()
{
Collection<String> vmNames = new LinkedList<String>();
AvdInfo[] allAvds = getAllValidVms();
if (allAvds != null)
{
for (AvdInfo vm : allAvds)
{
vmNames.add(vm.getName());
}
}
return vmNames;
}
/**
* Gets a skin name
*
* @param vmInfo the VM to get the skin
*/
public static String getSkin(AvdInfo vmInfo)
{
String skin = ""; //$NON-NLS-1$
File configFile = vmInfo.getConfigFile();
Properties p = new Properties();
InputStream configFileStream = null;
try
{
configFileStream = new FileInputStream(configFile);
p.load(configFileStream);
skin = p.getProperty(EMU_CONFIG_SKIN_NAME_PROPERTY);
}
catch (FileNotFoundException e)
{
StudioLogger
.error(SdkUtils.class,
"Error getting VM skin definition. Could not find file " + configFile.getAbsolutePath(), e); //$NON-NLS-1$
}
catch (IOException e)
{
StudioLogger
.error(SdkUtils.class,
"Error getting VM skin definition. Could not access file " + configFile.getAbsolutePath(), e); //$NON-NLS-1$
}
finally
{
if (configFileStream != null)
{
try
{
configFileStream.close();
}
catch (IOException e)
{
//nothing to do
}
}
}
return skin;
}
/**
* Gets a VM by name.
*
* @param vmName The VM name
*/
public static AvdInfo getValidVm(String vmName)
{
AvdInfo vmInfo = null;
AvdManager vmManager = getVmManager();
if (vmManager != null)
{
vmInfo = vmManager.getAvd(vmName, true);
}
return vmInfo;
}
/**
* Gets a VM by name.
*
* @param vmName The VM name
*/
public static AvdInfo getVm(String vmName)
{
AvdInfo vmInfo = null;
AvdManager vmManager = getVmManager();
if (vmManager != null)
{
vmInfo = vmManager.getAvd(vmName, false);
}
return vmInfo;
}
/**
* Creates a new VM instance.
*
* @param folder Folder where the VM files will be stored
* @param name VM Name
* @param target VM Target represented by the IAndroidTarget object
* @param skinName VM Skin name from the VM Target
*
* @throws CoreException
*/
public static AvdInfo createVm(String folder, String name, IAndroidTarget target,
String abiType, String skinName, String useSnapshot, String sdCard)
throws CoreException
{
AvdInfo vmInfo;
AvdManager vmManager = SdkUtils.getVmManager();
// get the abi type
if (abiType == null)
{
abiType = SdkConstants.ABI_ARMEABI;
}
/*
* public VmInfo createVm(String parentFolder, String name, IAndroidTarget target,
* String skinName, String sdcard, Map hardwareConfig)
*/
vmInfo =
vmManager.createAvd(new File(folder), name, target, abiType, skinName, sdCard,
null, Boolean.parseBoolean(useSnapshot), true, false,
NullSdkLog.getLogger());
if (vmInfo == null)
{
String errMsg = NLS.bind(AndroidNLS.EXC_SdkUtils_CannotCreateTheVMInstance, name);
IStatus status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, errMsg);
throw new CoreException(status);
}
return vmInfo;
}
/**
* Deletes a VM instance.
*
* @param name VM Name
*/
public static void deleteVm(String name)
{
AvdManager vmManager = SdkUtils.getVmManager();
AvdInfo avdToDelete = vmManager != null ? vmManager.getAvd(name, false) : null;
if (avdToDelete != null)
{
try
{
if ((avdToDelete.getIniFile() != null) && avdToDelete.getIniFile().exists())
{
avdToDelete.getIniFile().delete();
}
String path = avdToDelete.getDataFolderPath();
if (path != null)
{
File avdDir = new File(path);
if (avdDir.exists())
{
FileUtil.deleteDirRecursively(avdDir);
}
}
vmManager.removeAvd(avdToDelete);
}
catch (Exception e)
{
StudioLogger.error("Could not delete AVD: " + e.getMessage()); //$NON-NLS-1$
}
}
}
/**
* Get the reference to the File that points to the filesystem location of the directory
* where the user data files of the VM with the given name are stored.
* @param vmName name of the VM whose userdata directory is to be retrieved.
* @return the File object that references the filesystem location of the directory where
* the userdata files of the given VM will be stored. Returns a null reference if SDK is not configured
* or if there is no VM with the given name.
*/
public static File getUserdataDir(String vmName)
{
AvdInfo vminfo = SdkUtils.getValidVm(vmName);
File userdataDir = null;
if (vminfo != null)
{
String vmpath = vminfo.getDataFolderPath();
userdataDir = new File(vmpath);
}
return userdataDir;
}
/**
* Get the reference to the File that points to the filesystem location where the
* user data file of the VM with the given name is.
*
* @param vmName name of the VM whose userdata file is to be retrieved.
* @return the File object that references the filesystem location where the userdata
* of the given VM should be. Returns a null reference if SDK is not configured
* or if there is no VM with the given name.
*/
public static File getUserdataFile(String vmName)
{
File userdataDir = getUserdataDir(vmName);
File userdataFile = null;
if (userdataDir != null)
{
userdataFile = new File(userdataDir, USERIMAGE_FILENAME);
}
return userdataFile;
}
/**
* Get the reference to the Files that point to the filesystem location where the
* state data files of the VM with the given name are.
*
* @param vmName name of the VM whose state data files is to be retrieved.
* @return the File objects that reference the filesystem location where the state data
* files of the given VM should be. Returns a null reference if SDK is not configured
* or if there is no VM with the given name.
*/
public static List<File> getStateDataFiles(String vmName)
{
File userdataDir = getUserdataDir(vmName);
List<File> stateDataFiles = null;
if (userdataDir != null)
{
stateDataFiles = new ArrayList<File>();
for (int i = 0; i < STATEDATA_FILENAMES.length; i++)
{
stateDataFiles.add(new File(userdataDir, STATEDATA_FILENAMES[i]));
}
}
return stateDataFiles;
}
/**
* Retrieves all sample applications from a target
*
* @param target The target
* @return all sample applications from a target
*/
public static Object[] getSamples(IAndroidTarget target)
{
List<Sample> samples = new ArrayList<Sample>();
File samplesFolder = new File(target.getPath(IAndroidTarget.SAMPLES));
samples = findSamples(samplesFolder, target);
return samples.toArray();
}
/**
* Find the samples inside an specific directory (recursively)
*
* @param folder the folder that can contain samples
* @param target the target of the samples in the folder
* @return a list of samples
*/
private static List<Sample> findSamples(File folder, IAndroidTarget target)
{
List<Sample> samples = new ArrayList<Sample>();
if (folder.exists() && folder.isDirectory())
{
for (File sampleFolder : folder.listFiles())
{
if (sampleFolder.isDirectory())
{
if (Sample.isSample(sampleFolder))
{
samples.add(new Sample(sampleFolder, target));
}
else
{
samples.addAll(findSamples(sampleFolder, target));
}
}
}
}
return samples;
}
/**
* Retrieves all targets for a given SDK
*
* @param sdk The sdk
*
* @return all targets for the given SDK
*/
public static Object[] getTargets(Sdk sdk)
{
Object[] targets = null;
if (sdk != null)
{
targets = sdk.getTargets();
}
return targets;
}
/**
* Associates a project to a target
*
* @param project The project
* @param target The target
*/
public static void associate(IProject project, IAndroidTarget target)
{
try
{
Sdk.getCurrent().initProject(project, target);
}
catch (Exception e)
{
StudioLogger.error(SdkUtils.class, "Error associating project " + project.getName() //$NON-NLS-1$
+ " with target " + target.getName()); //$NON-NLS-1$
}
}
/**
* Retrieves the target for a project
*
* @param project the project
*
* @return the target for the project
*/
public static IAndroidTarget getTarget(IProject project)
{
IAndroidTarget target = null;
if (project != null)
{
target = Sdk.getCurrent().getTarget(project);
}
return target;
}
/**
* Retrieves the target for a project
*
* @param project the project
*
* @return the target for the project
*/
public static String getMinSdkVersion(IProject project)
{
String minSdkVersion = "";
try
{
AndroidManifestFile androidManifestFile =
AndroidProjectManifestFile.getFromProject(project);
UsesSDKNode usesSdkNode =
(UsesSDKNode) androidManifestFile.getNode(AndroidManifestNode.NodeType.UsesSdk);
if (usesSdkNode != null)
{
minSdkVersion = usesSdkNode.getMinSdkVersion();
}
}
catch (AndroidException e)
{
StudioLogger.error("Error getting min sdk version. " + e.getMessage());
}
catch (CoreException e)
{
StudioLogger.error("Error getting min sdk version. " + e.getMessage());
}
return minSdkVersion;
}
/**
* Retrieves all Activity Actions for a project.
*
* @param project The project
*
* @return all Activity Actions for the project.
*/
public static String[] getActivityActions(IProject project)
{
String[] attributeValues = new String[0];
if ((project != null) && project.isOpen())
{
IAndroidTarget target = SdkUtils.getTarget(project);
AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
if (targetData != null)
{
attributeValues =
targetData.getAttributeValues("action", "android:name", "activity"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
return attributeValues;
}
/**
* Retrieves all Service Actions for a project.
*
* @param project The project
*
* @return all Service Actions for the project.
*/
public static String[] getServiceActions(IProject project)
{
String[] attributeValues = new String[0];
if ((project != null) && project.isOpen())
{
IAndroidTarget target = SdkUtils.getTarget(project);
AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
if (targetData != null)
{
attributeValues =
targetData.getAttributeValues("action", "android:name", "service"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
return attributeValues;
}
/**
* Retrieves all Broadcast Receiver Actions for a project.
*
* @param project The project
*
* @return all Broadcast Receiver Actions for the project.
*/
public static String[] getReceiverActions(IProject project)
{
String[] attributeValues = new String[0];
if ((project != null) && project.isOpen())
{
IAndroidTarget target = SdkUtils.getTarget(project);
AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
if (targetData != null)
{
attributeValues =
targetData.getAttributeValues("action", "android:name", "receiver"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
return attributeValues;
}
/**
* Retrieves all Intent Filter Actions for a project.
*
* @param project The project
*
* @return all Intent Filter Actions for the project.
*/
public static String[] getIntentFilterCategories(IProject project)
{
String[] attributeValues = new String[0];
if ((project != null) && project.isOpen())
{
IAndroidTarget target = SdkUtils.getTarget(project);
AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
if (targetData != null)
{
attributeValues = targetData.getAttributeValues("category", "android:name"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return attributeValues;
}
/**
* Get the api version number for a given project
* @param project: the project
* @return the api version number or 0 if some error occurs
*/
public static int getApiVersionNumberForProject(IProject project)
{
int api = 0;
IAndroidTarget target = SdkUtils.getTarget(project);
if (target != null)
{
AndroidVersion version = target.getVersion();
if (version != null)
{
api = version.getApiLevel();
}
}
return api;
}
public static String getTargetNameForProject(IProject project)
{
IAndroidTarget target = getTarget(project);
return target != null ? target.getName() : ""; //$NON-NLS-1$
}
public static boolean isPlatformTarget(String avdName)
{
IAndroidTarget target = getValidVm(avdName).getTarget();
return target != null ? target.isPlatform() : false;
}
public static boolean isProjectTargetAPlatform(IProject project)
{
IAndroidTarget target = getTarget(project);
return target != null ? target.isPlatform() : false;
}
public static boolean isPlatformTarget(IAndroidTarget target)
{
return target != null ? target.isPlatform() : false;
}
/**
* Retrieves the APK configurations of a project
*
* @param project the project
* @return the APK configurations
*/
public static Map<String, String> getAPKConfigurationsForProject(IProject project)
{
Map<String, String> apkConfigurations = null;
if ((project != null) && project.isOpen())
{
Sdk.getCurrent();
//This is not supported on ADT 14 preview so let's comment it for now.
// apkConfigurations = Sdk.getProjectState(project).getApkSettings().getLocaleFilters();
apkConfigurations = new HashMap<String, String>(0);
}
return apkConfigurations;
}
public static String getBaseTarget(String name)
{
IAndroidTarget target = getValidVm(name).getTarget();
while (!target.isPlatform())
{
target = target.getParent();
}
return target.getName();
}
/**
* Check if an SDK is an OPhone Sdk
* @return
*/
public static boolean isOphoneSDK()
{
boolean result = false;
// check if the folder contains the oms jar
FilenameFilter omsFilenameFilter = new FilenameFilter()
{
public boolean accept(File arg0, String arg1)
{
return arg1.equals(IAndroidConstants.OPHONE_JAR);
}
};
Sdk sdk = getCurrentSdk();
IAndroidTarget[] targets = sdk.getTargets();
for (IAndroidTarget target : targets)
{
File folder = new File(target.getLocation());
if (folder.list(omsFilenameFilter).length > 0)
{
result = true;
break;
}
}
return result;
}
/**
* Check if an SDK is an JIL sdk
* @return
*/
public static boolean isJILSdk()
{
boolean result = false;
// check if the folder contains the oms jar
FilenameFilter jilFilenameFilter = new FilenameFilter()
{
public boolean accept(File arg0, String arg1)
{
return arg1.equals(IAndroidConstants.JIL_JAR);
}
};
Sdk sdk = getCurrentSdk();
if (sdk != null)
{
IAndroidTarget[] targets = sdk.getTargets();
for (IAndroidTarget target : targets)
{
File folder = new File(target.getLocation());
if (folder.list(jilFilenameFilter).length > 0)
{
result = true;
break;
}
}
}
return result;
}
public static String getEmulatorWindowName(String avdName, int port)
{
String windowName = ""; //$NON-NLS-1$
if (isJILSdk())
{
windowName = "JIL Emulator (" + avdName + ":" + port + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
else if (isOphoneSDK())
{
windowName = "OPhone Emulator (" + avdName + ":" + port + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
else
{
windowName = port + ":" + avdName; //$NON-NLS-1$
}
return windowName;
}
public static boolean isLibraryProject(IProject project)
{
return Sdk.getProjectState(project) != null ? Sdk.getProjectState(project).isLibrary()
: false;
}
/**
* Returns all available permissions
*
* @return String array containing the available permissions
*/
public static String[] getIntentFilterPermissions(IProject project)
{
String[] attributeValues = new String[0];
if ((project != null) && project.isOpen())
{
IAndroidTarget target = SdkUtils.getTarget(project);
AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
if (targetData != null)
{
attributeValues = targetData.getAttributeValues("uses-permission", "android:name"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return attributeValues;
}
/**
* Try to repair an AVD. Currently only avds with wrong image path are repariable.
* Display a message with the changes to the config.ini
* @param avdInfo
* @return Status ERROR if an IO exception occured.
*/
public static IStatus repairAvd(AvdInfo avdInfo)
{
IStatus status = Status.OK_STATUS;
AvdManager avdManager = Sdk.getCurrent().getAvdManager();
Display display = PlatformUI.getWorkbench().getDisplay();
ISdkLog log =
new MessageBoxLog(String.format("Result of updating AVD '%s':", avdInfo.getName()), //$NON-NLS-1$
display, false);
try
{
avdManager.updateAvd(avdInfo, log);
// display the result
if (log instanceof MessageBoxLog)
{
((MessageBoxLog) log).displayResult(true);
}
SdkUtils.reloadAvds();
}
catch (IOException e)
{
status =
new Status(Status.ERROR, AndroidPlugin.PLUGIN_ID,
AndroidNLS.SdkUtils_COULD_NOT_REPAIR_AVD, e);
}
return status;
}
public static String getDefaultSkin(String targetName)
{
IAndroidTarget target = getTargetByName(targetName);
return target != null ? target.getDefaultSkin() : "HVGA";
}
}