blob: 938894da3437f036d5504f52972ce3c9783693f5 [file] [log] [blame]
/*
* 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.motorolamobility.preflighting.core.devicespecification.internal;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
import com.motorolamobility.preflighting.core.internal.permissionfeature.PermissionToFeatureMapReader;
import com.motorolamobility.preflighting.core.internal.permissionfeature.PermissionToFeatureMapping;
import com.motorolamobility.preflighting.core.internal.utils.MethodPermissionCSVReader;
import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
import com.motorolamobility.preflighting.core.source.model.Invoke;
import com.motorolamobility.preflighting.core.source.model.PermissionGroups;
/**
* Singleton that contains rules that are specific from the Android Platform
*/
public class PlatformRules
{
/**
* XML from API level to permission
*/
private static final String API_LEVEL_PERMISSION_MAP = "files/apilevel-permission-map.xml"; //$NON-NLS-1$
/**
* XML from feature to API level
*/
private static final String FEATURE_API_LEVEL_MAP = "files/feature-apilevel-map.xml"; //$NON-NLS-1$
/**
* XML from permission to implied features
*/
private static final String PERMISSION_FEATURE_MAP = "files/permission-feature-map.xml"; //$NON-NLS-1$
/**
* Text file from blocked permissions
*/
private static final String BLOCKED_PERMISSIONS_LIST = "files/blocked_permissions.txt"; //$NON-NLS-1$
/**
* Collection of method signature to permission required
*/
private static final String METHOD_PERMISSION_LIST = "files/method_permission_list_4.0.csv"; //$NON-NLS-1$
private static PlatformRules uniqInstance;
/**
* Map from api level (key) to {@link Permission}
*/
private HashMap<Integer, ArrayList<Permission>> permissionsList = null;
// private HashMap<Integer, ArrayList<Feature>> featuresList = null;
/**
* Map from api level (key) to {@link Feature}
*/
private HashMap<Integer, Feature> featuresList = null;
/**
* Map from method signature (key) to permissions required
*/
private Map<String, PermissionGroups> methodToPermissionGroup =
new HashMap<String, PermissionGroups>();
//private final ArrayList<String> permissionsBySignatureList = new ArrayList<String>();
private PermissionToFeatureMapping permissionToFeatureMapping = null;
/**
* List of blocked permissions
*/
private final ArrayList<String> blockedPermissionsList = new ArrayList<String>();
/**
* <li>API level 1 <li>Android platform 1.0
*/
public static final int API_LEVEL_1 = 1;
/**
* <li>API level 2 <li>Android platform 1.1
*/
public static final int API_LEVEL_2 = 2;
/**
* <li>API level 3 <li>Android platform 1.5 (Cupcake) <li>Based on Linux
* Kernel 2.6.27
*/
public static final int API_LEVEL_3 = 3;
/**
* <li>API level 4 <li>Android platform 1.6 (Donut) <li>Based on Linux
* Kernel 2.6.29
*/
public static final int API_LEVEL_4 = 4;
/**
* <li>API level 5 <li>Android platform 2.0 (Eclair) <li>Based on Linux
* Kernel 2.6.29
*/
public static final int API_LEVEL_5 = 5;
/**
* <li>API level 6 <li>Android platform 2.0.1 (Eclair) <li>Based on Linux
* Kernel 2.6.29
*/
public static final int API_LEVEL_6 = 6;
/**
* <li>API level 7 <li>Android platform 2.1 (Eclair) <li>Based on Linux
* Kernel 2.6.29
*/
public static final int API_LEVEL_7 = 7;
/**
* <li>API level 8 <li>Android platform 2.2 (Froyo) <li>Based on Linux
* Kernel 2.6.32
*/
public static final int API_LEVEL_8 = 8;
/**
* Singleton instance.
*/
private PlatformRules()
{
loadAllPermissions();
loadAllFeatures();
loadPermissionToRequiredImpliedFeatures();
loadBlockedPermissions();
loadMethodToPermissionList();
}
/**
* Returns the unique instance of PlatformRules
*
* @return The unique instance of PlatformRules
*/
public static synchronized PlatformRules getInstance()
{
if (uniqInstance == null)
{
uniqInstance = new PlatformRules();
}
return uniqInstance;
}
/**
* Returns a list of permissions related to a given Android API level.
*
* @param apiLevel The API Level. These values are constants of this {@link PlatformRules}
* class. You have {@link PlatformRules#API_LEVEL_1}, {@link PlatformRules#API_LEVEL_2} and so on.
*
* @return An ArrayList of Strings containing all permission of a given
* Android API level -OR- returns a null ArrayList if there are no
* permissions or if the API level number is not valid/supported.
*/
public ArrayList<String> getPermissions(int apiLevel)
{
ArrayList<String> permissions = null;
if ((permissionsList != null) && (isApiLevelSupported(apiLevel))
&& (permissionsList.containsKey(apiLevel)))
{
ArrayList<Permission> permissionsForThisLevel = null;
Set<Entry<Integer, ArrayList<Permission>>> entries = permissionsList.entrySet();
for (Entry<Integer, ArrayList<Permission>> entry : entries)
{
if (apiLevel == entry.getKey().intValue())
{
permissionsForThisLevel = entry.getValue();
}
}
if ((permissionsForThisLevel != null) && (!permissionsForThisLevel.isEmpty()))
{
permissions = new ArrayList<String>();
for (Permission permission : permissionsForThisLevel)
{
permissions.add(permission.getPermissionName());
}
}
}
return permissions;
}
/**
* Returns all features related to a given Android API level.
*
* @param apiLevel The API Level. These values are constants of this {@link PlatformRules}
* class. You have {@link PlatformRules#API_LEVEL_1}, {@link PlatformRules#API_LEVEL_2} and so on.
*
* @return An ArrayList of Strings containing all features of a given
* Android API level -OR- returns a null ArrayList if there are no
* features or if the API level number is not valid/supported.
*/
public ArrayList<String> getFeatures(int apiLevel)
{
ArrayList<String> features = null;
if ((featuresList != null) && (isApiLevelSupported(apiLevel))
&& (featuresList.containsKey(apiLevel)))
{
Set<Entry<Integer, Feature>> entries = featuresList.entrySet();
features = new ArrayList<String>();
for (Entry<Integer, Feature> entry : entries)
{
if (apiLevel == entry.getKey().intValue())
{
features.add(entry.getValue().getFeatureID());
}
}
}
return features;
}
/**
* Returns all permissions related to a given method signature.
*
* @param signature The method's signature. In order to better understand how these
* signatures are created, see {@link Invoke#getQualifiedName()}, {@link SourceFolderElement#getInvokedMethods()} documentation.
* The {@link Invoke#getQualifiedName()} method returns this signature.
*
* @return Given a Method signature, its {@link PermissionGroups}
* are returned.
*
* @see {@link Invoke#getQualifiedName()}, {@link SourceFolderElement#getInvokedMethods()}
*/
public PermissionGroups getPermissionsForMethod(String signature)
{
return methodToPermissionGroup.get(signature);
}
/**
* Verifies if the API level number is valid/supported.
*
* @param apiLevel The API Level. These values are constants of this {@link PlatformRules}
* class. You have {@link PlatformRules#API_LEVEL_1}, {@link PlatformRules#API_LEVEL_2} and so on.
*
* @return Returns <code>true</code> in case the API Level is supported,
* <code>false</code> otherwise.
*/
public boolean isApiLevelSupported(int apiLevel)
{
boolean supported = false;
if ((apiLevel >= API_LEVEL_1) && (apiLevel <= API_LEVEL_8))
{
supported = true;
}
return supported;
}
/**
* Load all permission.
*/
private void loadAllPermissions()
{
ArrayList<Permission> permissions = null;
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = null;
NodeList apiLevelNodes = loadXmlFiles(builderFactory, documentBuilder, "api_level", //$NON-NLS-1$
// "C:\\tmp\\apilevel-permission-map.xml"); // for JUnit purposes. //$NON-NLS-1$
API_LEVEL_PERMISSION_MAP);
int apiLevel = 0;
if ((apiLevelNodes != null) && (apiLevelNodes.getLength() > 0))
{
permissions = new ArrayList<Permission>();
for (int i = 0; i < apiLevelNodes.getLength(); i++)
{
Element apiLevelTag = (Element) apiLevelNodes.item(i);
apiLevel = Integer.parseInt(apiLevelTag.getAttribute("number")); //$NON-NLS-1$
NodeList permissionNodes = apiLevelTag.getElementsByTagName("permission"); //$NON-NLS-1$
for (int j = 0; j < permissionNodes.getLength(); j++)
{
Element permissionTag = (Element) permissionNodes.item(j);
if (permissionTag != null)
{
String name = null;
String description = null;
boolean deprecated = false;
try
{
name = getChildNodes(permissionTag.getElementsByTagName("name")); //$NON-NLS-1$
description =
getChildNodes(permissionTag.getElementsByTagName("description")); //$NON-NLS-1$
deprecated =
Boolean.parseBoolean(getChildNodes(permissionTag
.getElementsByTagName("deprecated"))); //$NON-NLS-1$
}
catch (Exception e)
{
e.printStackTrace();
}
if (name != null)
{
Permission permission =
new Permission(apiLevel, name, description, deprecated);
permissions.add(permission);
}
}
}
}
if ((!permissions.isEmpty()) && (apiLevel != 0))
{
permissionsList = new HashMap<Integer, ArrayList<Permission>>();
permissionsList.put(apiLevel, permissions);
}
}
}
/**
* Load all features.
*/
private void loadAllFeatures()
{
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = null;
NodeList featureApiNodes = loadXmlFiles(builderFactory, documentBuilder, "feature", //$NON-NLS-1$
// "C:\\tmp\\feature-apilevel-map.xml"); // for JUnit purposes. //$NON-NLS-1$
FEATURE_API_LEVEL_MAP);
int apiLevel = 0;
if ((featureApiNodes != null) && (featureApiNodes.getLength() > 0))
{
featuresList = new HashMap<Integer, Feature>();
for (int i = 0; i < featureApiNodes.getLength(); i++)
{
Element featureTag = (Element) featureApiNodes.item(i);
if (featureTag != null)
{
String featureID = null;
int sinceApiLevel = 0;
try
{
featureID = getChildNodes(featureTag.getElementsByTagName("id")); //$NON-NLS-1$
sinceApiLevel =
Integer.parseInt(getChildNodes(featureTag
.getElementsByTagName("since_apilevel"))); //$NON-NLS-1$
}
catch (Exception e)
{
e.printStackTrace();
}
if ((featureID != null) && (isApiLevelSupported(sinceApiLevel)))
{
Feature feature = new Feature(featureID, sinceApiLevel);
featuresList.put(apiLevel, feature);
}
}
}
}
}
/**
* Load XML file into a {@link NodeList} structure. The basis for
* the {@link NodeList} is the tag paramter.
*
* @param builderFactory Builder factory object.
* @param documentBuilder Builder object.
* @param tag Tag which will serve as the basis for bringing
* the {@link NodeList}.
* @param fileName The XML file Name.
*
* @return Returns a {@link NodeList} from a XML file based on
* the tag parameter.
*/
private NodeList loadXmlFiles(DocumentBuilderFactory builderFactory,
DocumentBuilder documentBuilder, String tag, String fileName)
{
try
{
documentBuilder = builderFactory.newDocumentBuilder();
}
catch (ParserConfigurationException e1)
{
e1.printStackTrace();
}
Document doc = null;
try
{
// For JUnit tests purpose
// doc = documentBuilder.parse(new File(fileName));
// -------------------------
doc =
documentBuilder.parse(PreflightingCorePlugin.getContext().getBundle()
.getEntry(fileName).openStream());
}
catch (SAXException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
return doc.getDocumentElement().getElementsByTagName(tag);
}
/**
* Given a {@link NodeList} consider only the first element. Return
* its first child´s value.
*
* @param node {@link NodeList} where only the first child will
* be considered.
*
* @return Returns the value of the {@link NodeList}´s first element´s
* first child.
*
* @throws DOMException Exception thrown in case there are problems
* handling the {@link NodeList}.
*/
private String getChildNodes(NodeList node) throws DOMException
{
if ((node != null) && (node.getLength() > 0))
{
Element nameElement = (Element) node.item(0);
if ((nameElement != null) && (nameElement.hasChildNodes()))
{
Node childTag = nameElement.getFirstChild();
if (childTag != null)
{
return childTag.getNodeValue();
}
}
}
return null;
}
private void loadMethodToPermissionList()
{
InputStream csvStream = null;
InputStreamReader streamReader = null;
try
{
csvStream =
PreflightingCorePlugin.getContext().getBundle()
.getEntry(METHOD_PERMISSION_LIST).openStream();
streamReader = new InputStreamReader(csvStream);
methodToPermissionGroup =
MethodPermissionCSVReader.readMapMethodToPermission(streamReader);
}
catch (IOException e)
{
PreflightingLogger
.error("Problem reading " + METHOD_PERMISSION_LIST + " exception: " + e.getLocalizedMessage()); //$NON-NLS-1$ //$NON-NLS-2$
}
finally
{
if (streamReader != null)
{
try
{
streamReader.close();
}
catch (IOException e)
{
//Do Nothing.
}
}
try
{
if (csvStream != null)
{
csvStream.close();
}
}
catch (IOException e)
{
//do nothing
}
}
}
private void loadBlockedPermissions()
{
InputStream txtStream = null;
BufferedReader br = null;
try
{
txtStream =
PreflightingCorePlugin.getContext().getBundle()
.getEntry(BLOCKED_PERMISSIONS_LIST).openStream();
br = new BufferedReader(new InputStreamReader(txtStream));
String line = br.readLine();
while (line != null)
{
blockedPermissionsList.add(line.trim());
line = br.readLine();
}
}
catch (IOException e)
{
PreflightingLogger.error("Problem reading " + BLOCKED_PERMISSIONS_LIST + " exception: "
+ e.getLocalizedMessage()); //$NON-NLS-1$ //$NON-NLS-2$
}
finally
{
if (br != null)
{
try
{
br.close();
}
catch (IOException e)
{
//Do Nothing.
}
}
try
{
if (txtStream != null)
{
txtStream.close();
}
}
catch (IOException e)
{
//do nothing
}
}
}
/**
* Checks whether permission is blocked.
*
* @param permission Identifier of the permission (as specified in <code>android:name</code> tag inside AndroidManifest.xml)
*
* @return Returns <code>true</code> if blocked (need special system certificate), false if not
*/
public boolean isPermissionBlocked(String permission)
{
return blockedPermissionsList.contains(permission);
}
private void loadPermissionToRequiredImpliedFeatures()
{
InputStream xmlStream;
try
{
xmlStream =
PreflightingCorePlugin.getContext().getBundle()
.getEntry(PERMISSION_FEATURE_MAP).openStream();
PermissionToFeatureMapReader permissionToFeatureReader =
new PermissionToFeatureMapReader(xmlStream);
permissionToFeatureMapping = permissionToFeatureReader.read();
}
catch (Exception e)
{
PreflightingLogger.error("Problem reading " + PERMISSION_FEATURE_MAP + " exception: "
+ e.getLocalizedMessage());
}
}
/**
* Gets the list of required features for a certain permission. In case none
* is required, <code>null</code> is returned.
*
* @param permissionId Permision identifier which the required featured will be
* fetched.
*
* @return Returns the list of required features for a certain permission. In case none
* is required, <code>null</code> is returned.
*/
public Set<com.motorolamobility.preflighting.core.permissionfeature.Feature> getImpliedFeaturesSet(
String permissionId)
{
Set<com.motorolamobility.preflighting.core.permissionfeature.Feature> features =
new HashSet<com.motorolamobility.preflighting.core.permissionfeature.Feature>();
if (permissionToFeatureMapping.getRequiredImpliedFeaturesForPermission(permissionId) != null)
{
features.addAll(permissionToFeatureMapping
.getRequiredImpliedFeaturesForPermission(permissionId));
}
return features;
}
/**
* Given a certain category, all permissions related to it are retrieved.
*
* @param categoryName Category name which the related permissions will be fetched.
*
* @return Returns the list of permissions for a certain category.
*/
public Set<com.motorolamobility.preflighting.core.permissionfeature.Permission> getPermissionsForCategory(
String categoryName)
{
Set<com.motorolamobility.preflighting.core.permissionfeature.Permission> permissions =
new HashSet<com.motorolamobility.preflighting.core.permissionfeature.Permission>();
if (permissionToFeatureMapping.getPermissionsForCategory(categoryName) != null)
{
permissions.addAll(permissionToFeatureMapping.getPermissionsForCategory(categoryName));
}
return permissions;
}
/**
* An inner class to represent a set of permissions for a given API level.
*/
class Permission
{
private int apLevelNumber = 0;
private String permissionName = null;
private String permissionDescription = null;
private boolean deprecated = false;
/**
* Constructor which load a set of parameters.
*
* @param apiLevel API level.
* @param name Permission name.
* @param description Permission description.
* @param deprecated A flag indicating whether the permission is deprecated.
*/
Permission(int apiLevel, String name, String description, boolean deprecated)
{
apLevelNumber = apiLevel;
permissionName = name;
permissionDescription = (description != null) ? description : ""; //$NON-NLS-1$
this.deprecated = deprecated;
}
/**
* Gets the permission´s name.
*
* @return Returns the permission name.
*/
private String getPermissionName()
{
return permissionName;
}
/**
* Sets the permission´s name.
*
* @param permissionName
* The permission name to set
*/
@SuppressWarnings("unused")
private void setPermissionName(String permissionName)
{
this.permissionName = permissionName;
}
/**
* Gets the permission´s description.
*
* @return Returns the permission description
*/
@SuppressWarnings("unused")
private String getPermissionDescription()
{
return permissionDescription;
}
/**
* Sets the permission´s description.
*
* @param permissionDescription
* the permission description to set.
*/
@SuppressWarnings("unused")
private void setPermissionDescription(String permissionDescription)
{
this.permissionDescription = permissionDescription;
}
/**
* The a flag indicating whether the permission is deprecated.
*
* @return the Returns the flag indicating whether a permission
* is deprecated.
*/
@SuppressWarnings("unused")
private boolean isDeprecated()
{
return deprecated;
}
/**
* Set a flag indicating whether the permission is deprecated.
*
* @param permissionDescription
* The flag for deprecation to be set.
*/
@SuppressWarnings("unused")
private void setDeprecated(boolean deprecated)
{
this.deprecated = deprecated;
}
/**
* Get the API Level related to this permission.
*
* @return Returns the API Level related to this permission.
*/
@SuppressWarnings("unused")
private int getApLevelNumber()
{
return apLevelNumber;
}
}
/**
* An inner class to represent a set of features and their first API level.
*/
class Feature
{
private String featureID = null;
private int sinceApiLevel = 0;
/**
* Constructor which sets necessary parameters.
*
* @param featureID Feature identifier.
* @param sinceApiLevel Since-when API Level.
*/
Feature(String featureID, int sinceApiLevel)
{
this.featureID = featureID;
this.sinceApiLevel = sinceApiLevel;
}
/**
* Gets the feature Id.
*
* @return Returns the feature Id.
*/
private String getFeatureID()
{
return featureID;
}
/**
* Sets the feature Id.
*
* @param featureID
* The feature Id to be set.
*/
@SuppressWarnings("unused")
private void setFeatureID(String featureID)
{
this.featureID = featureID;
}
/**
* Get the Since-when API level.
*
* @return Returns the Since-when API Level.
*/
@SuppressWarnings("unused")
private int getSinceApiLevel()
{
return sinceApiLevel;
}
/**
* Sets the Since-When API level.
*
* @param sinceApiLevel
* Sets the Since-When API Level.
*/
@SuppressWarnings("unused")
private void setSinceApiLevel(int sinceApiLevel)
{
this.sinceApiLevel = sinceApiLevel;
}
}
}