diff --git a/android-plugin/src/main/java/io/ecocode/xml/XmlCheckList.java b/android-plugin/src/main/java/io/ecocode/xml/XmlCheckList.java index 387de3b2..9eeeb274 100644 --- a/android-plugin/src/main/java/io/ecocode/xml/XmlCheckList.java +++ b/android-plugin/src/main/java/io/ecocode/xml/XmlCheckList.java @@ -26,6 +26,7 @@ import io.ecocode.xml.checks.sobriety.DarkUIThemeXmlRule; import io.ecocode.xml.checks.power.CompagnionInBackgroundXmlRule; import io.ecocode.xml.checks.power.IgnoreBatteryOptimizationsXmlRule; +import io.ecocode.xml.checks.sobriety.HardwareAccelerationXmlRule; import java.util.Arrays; import java.util.List; @@ -45,7 +46,8 @@ public static List> getXmlChecks() { CompagnionInBackgroundXmlRule.class, ChargeAwarenessXmlRule.class, ServiceBootTimeXmlRule.class, - SaveModeAwarenessXmlRule.class + SaveModeAwarenessXmlRule.class, + HardwareAccelerationXmlRule.class ); } diff --git a/android-plugin/src/main/java/io/ecocode/xml/checks/sobriety/HardwareAccelerationXmlRule.java b/android-plugin/src/main/java/io/ecocode/xml/checks/sobriety/HardwareAccelerationXmlRule.java new file mode 100644 index 00000000..ae11557e --- /dev/null +++ b/android-plugin/src/main/java/io/ecocode/xml/checks/sobriety/HardwareAccelerationXmlRule.java @@ -0,0 +1,88 @@ +/* + * ecoCode Android plugin - Provides rules to reduce the environmental footprint of your Android applications + * Copyright © 2020 Green Code Initiative (contact@ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package io.ecocode.xml.checks.sobriety; + +import io.ecocode.xml.checks.XPathSimpleCheck; +import org.sonar.check.Priority; +import org.sonar.check.Rule; +import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey; +import org.w3c.dom.Node; + +@Rule(key = "EC549", name = "Hardware acceleration", priority = Priority.MAJOR) +@DeprecatedRuleKey(repositoryKey = "ecoCode-java", ruleKey = "ESOB016") +public class HardwareAccelerationXmlRule extends XPathSimpleCheck { + + private static final String ERROR_MESSAGE_ENABLED = + "Hardware acceleration is enabled. Consider disabling it to reduce RAM usage."; + + private static final String ERROR_MESSAGE_IMPLICIT = + "Hardware acceleration is implicitly enabled. Consider disabling it explicitly with android:hardwareAccelerated=\"false\"."; + + private static final String ANDROID_HARDWARE_ACCELERATED = "android:hardwareAccelerated"; + private static final String XPATH_TARGET_COMPONENTS = "//application | //activity | //window | //view"; + + @Override + protected String getMessage() { + return ERROR_MESSAGE_IMPLICIT; + } + + @Override + protected String getXPathExpressionString() { + return XPATH_TARGET_COMPONENTS; + } + + @Override + protected void visitNode(Node node, String message) { + Node hardwareAttributeNode = getHardwareAccelerationAttribute(node); + + if ("true".equals(getNodeValue(hardwareAttributeNode))) { + reportIssue(hardwareAttributeNode, ERROR_MESSAGE_ENABLED); + } + else if (!isHardwareAccelerationDisabledInParent(node)) { + reportIssue(node, ERROR_MESSAGE_IMPLICIT); + } + } + + private boolean isHardwareAccelerationDisabledInParent(Node node) { + Node parent = node; + + while (parent != null && !"application".equals(parent.getNodeName())) { + parent = parent.getParentNode(); + } + + if (parent == null) { + return false; + } + + Node parentHardwareAttr = getHardwareAccelerationAttribute(parent); + return "false".equals(getNodeValue(parentHardwareAttr)); + } + + private static Node getHardwareAccelerationAttribute(Node node) { + if (node == null || node.getAttributes() == null) { + return null; + } + + return node.getAttributes().getNamedItem(ANDROID_HARDWARE_ACCELERATED); + } + + private String getNodeValue(Node node) { + return (node != null) ? node.getNodeValue() : null; + } + +} diff --git a/android-plugin/src/main/resources/io/ecocode/android/xml/ecocode_xml_profile.json b/android-plugin/src/main/resources/io/ecocode/android/xml/ecocode_xml_profile.json index 22801068..d37fa446 100644 --- a/android-plugin/src/main/resources/io/ecocode/android/xml/ecocode_xml_profile.json +++ b/android-plugin/src/main/resources/io/ecocode/android/xml/ecocode_xml_profile.json @@ -8,6 +8,7 @@ "EC544", "EC545", "EC546", - "EC548" + "EC548", + "EC549" ] } diff --git a/android-plugin/src/main/resources/io/ecocode/rules/xml/EC549.html b/android-plugin/src/main/resources/io/ecocode/rules/xml/EC549.html new file mode 100644 index 00000000..e626170b --- /dev/null +++ b/android-plugin/src/main/resources/io/ecocode/rules/xml/EC549.html @@ -0,0 +1,41 @@ + +

+ Hardware acceleration is a feature that improves rendering performance by offloading certain 2D drawing operations to the GPU. + While it can enhance UI responsiveness, it also increases memory and power consumption — especially on lower-end or older devices. +
+ Since API level 14, hardware acceleration is enabled by default in Android applications. However, if not required, + it is recommended to disable it explicitly in the manifest to reduce resource usage and promote energy-efficient design. +
+ This rule detects components (activities, services, etc.) that either explicitly enable hardware acceleration or inherit it implicitly without being disabled at the application level. +

+ +

Noncompliant Code Example

+
+    <application
+        android:label="@string/app_name">
+
+        <activity
+            android:name=".FastActivity"
+            android:hardwareAccelerated="true" />
+
+        <activity
+            android:name=".AnotherFastActivity" />
+
+    </application>
+
+ +

Compliant Solution

+
+    <application
+        android:label="@string/app_name"
+        android:hardwareAccelerated="false">
+
+        <activity
+            android:name=".FastActivity"
+            android:hardwareAccelerated="true" /> <!-- Only if really needed -->
+
+        <activity
+            android:name=".AnotherFastActivity" /> <!-- Inherits 'false' -->
+
+    </application>
+
diff --git a/android-plugin/src/main/resources/io/ecocode/rules/xml/EC549.json b/android-plugin/src/main/resources/io/ecocode/rules/xml/EC549.json new file mode 100644 index 00000000..decefdc9 --- /dev/null +++ b/android-plugin/src/main/resources/io/ecocode/rules/xml/EC549.json @@ -0,0 +1,17 @@ +{ + "title": "Sobriety: Hardware acceleration", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "1min" + }, + "tags": [ + "sobriety", + "environment", + "ecocode", + "android", + "eco-design" + ], + "defaultSeverity": "Major" +} \ No newline at end of file diff --git a/android-plugin/src/test/java/io/ecocode/xml/XmlRulesDefinitionTest.java b/android-plugin/src/test/java/io/ecocode/xml/XmlRulesDefinitionTest.java index f3738826..a5d31363 100644 --- a/android-plugin/src/test/java/io/ecocode/xml/XmlRulesDefinitionTest.java +++ b/android-plugin/src/test/java/io/ecocode/xml/XmlRulesDefinitionTest.java @@ -71,6 +71,11 @@ public void test() { assertThat(saveModeAwarenessXml).isNotNull(); assertThat(saveModeAwarenessXml.name()).isEqualTo("Power: Save Mode Awareness"); + RulesDefinition.Rule hardwareAccelerationXml = repository.rule("EC549"); + assertThat(hardwareAccelerationXml).isNotNull(); + assertThat(hardwareAccelerationXml.name()).isEqualTo("Sobriety: Hardware acceleration"); + + for (RulesDefinition.Rule rule : repository.rules()) { for (RulesDefinition.Param param : rule.params()) { assertThat(param.description()).as("description for " + param.key()).isNotEmpty(); diff --git a/android-plugin/src/test/java/io/ecocode/xml/checks/sobriety/HardwareAccelerationXmlRuleTest.java b/android-plugin/src/test/java/io/ecocode/xml/checks/sobriety/HardwareAccelerationXmlRuleTest.java new file mode 100644 index 00000000..1356d4e6 --- /dev/null +++ b/android-plugin/src/test/java/io/ecocode/xml/checks/sobriety/HardwareAccelerationXmlRuleTest.java @@ -0,0 +1,12 @@ +package io.ecocode.xml.checks.sobriety; + +import org.junit.Test; +import org.sonarsource.analyzer.commons.xml.checks.SonarXmlCheckVerifier; + +public class HardwareAccelerationXmlRuleTest { + + @Test + public void detectExplicitOrImplicitHardwareAcceleration() { + SonarXmlCheckVerifier.verifyIssues("HardwareAccelerationCheck.xml", new HardwareAccelerationXmlRule()); + } +} \ No newline at end of file diff --git a/android-plugin/src/test/resources/checks/HardwareAccelerationXmlRule/HardwareAccelerationCheck.xml b/android-plugin/src/test/resources/checks/HardwareAccelerationXmlRule/HardwareAccelerationCheck.xml new file mode 100644 index 00000000..ba6365a6 --- /dev/null +++ b/android-plugin/src/test/resources/checks/HardwareAccelerationXmlRule/HardwareAccelerationCheck.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +