Skip to content

Commit f32882d

Browse files
committed
ESOB016: Detects explicit or implicit use of hardware acceleration in Android components.
1 parent b24a748 commit f32882d

File tree

8 files changed

+205
-2
lines changed

8 files changed

+205
-2
lines changed

android-plugin/src/main/java/io/ecocode/xml/XmlCheckList.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.ecocode.xml.checks.sobriety.DarkUIThemeXmlRule;
2727
import io.ecocode.xml.checks.power.CompagnionInBackgroundXmlRule;
2828
import io.ecocode.xml.checks.power.IgnoreBatteryOptimizationsXmlRule;
29+
import io.ecocode.xml.checks.sobriety.HardwareAccelerationXmlRule;
2930

3031
import java.util.Arrays;
3132
import java.util.List;
@@ -45,7 +46,8 @@ public static List<Class<?>> getXmlChecks() {
4546
CompagnionInBackgroundXmlRule.class,
4647
ChargeAwarenessXmlRule.class,
4748
ServiceBootTimeXmlRule.class,
48-
SaveModeAwarenessXmlRule.class
49+
SaveModeAwarenessXmlRule.class,
50+
HardwareAccelerationXmlRule.class
4951
);
5052
}
5153

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.ecocode.xml.checks.sobriety;
2+
3+
import io.ecocode.xml.checks.XPathSimpleCheck;
4+
import org.sonar.check.Priority;
5+
import org.sonar.check.Rule;
6+
import org.w3c.dom.Node;
7+
8+
/**
9+
* Rule ESOB016: Detects explicit or implicit use of hardware acceleration in Android components.
10+
* <p>
11+
* Enabling hardware acceleration (via {@code android:hardwareAccelerated="true"}) can increase memory usage
12+
* and may lead to performance issues on low-end devices. In many cases, hardware acceleration is enabled
13+
* by default, especially if not explicitly disabled at the application or component level.
14+
* </p>
15+
*
16+
* <p>
17+
* This rule flags two cases:
18+
* <ul>
19+
* <li><b>Explicit enablement</b>: When {@code android:hardwareAccelerated="true"} is defined on a component.</li>
20+
* <li><b>Implicit enablement</b>: When the component does not define the attribute, and the parent {@code <application>}
21+
* does not explicitly disable it with {@code android:hardwareAccelerated="false"}.</li>
22+
* </ul>
23+
* </p>
24+
*
25+
* <p>
26+
* Recommendation: Only enable hardware acceleration when necessary for rendering performance,
27+
* and consider disabling it when working with memory-constrained applications.
28+
* </p>
29+
*
30+
* <p>
31+
* Targeted XML elements:
32+
* {@code <activity>}, {@code <service>}, {@code <receiver>}, {@code <provider>}.
33+
* </p>
34+
*
35+
* @see <a href="https://developer.android.com/guide/topics/graphics/hardware-accel">Android Developer Guide: Hardware Acceleration</a>
36+
*/
37+
@Rule(key = "ESOB016", name = "Hardware acceleration", priority = Priority.MAJOR)
38+
public class HardwareAccelerationXmlRule extends XPathSimpleCheck {
39+
40+
private static final String ERROR_MESSAGE_ENABLED =
41+
"Hardware acceleration is enabled. Consider disabling it to reduce RAM usage.";
42+
43+
private static final String ERROR_MESSAGE_IMPLICIT =
44+
"Hardware acceleration is implicitly enabled. Consider disabling it explicitly with android:hardwareAccelerated=\"false\".";
45+
46+
private static final String ANDROID_HARDWARE_ACCELERATED = "android:hardwareAccelerated";
47+
private static final String XPATH_TARGET_COMPONENTS = "//application | //activity | //window | //view";
48+
49+
@Override
50+
protected String getMessage() {
51+
return ERROR_MESSAGE_IMPLICIT;
52+
}
53+
54+
@Override
55+
protected String getXPathExpressionString() {
56+
return XPATH_TARGET_COMPONENTS;
57+
}
58+
59+
@Override
60+
protected void visitNode(Node node, String message) {
61+
Node hardwareAttributeNode = getHardwareAccelerationAttribute(node);
62+
63+
if ("true".equals(getNodeValue(hardwareAttributeNode))) {
64+
reportIssue(hardwareAttributeNode, ERROR_MESSAGE_ENABLED);
65+
}
66+
else if (!isHardwareAccelerationDisabledInParent(node)) {
67+
reportIssue(node, ERROR_MESSAGE_IMPLICIT);
68+
}
69+
}
70+
71+
private boolean isHardwareAccelerationDisabledInParent(Node node) {
72+
Node parent = node;
73+
74+
while (parent != null && !"application".equals(parent.getNodeName())) {
75+
parent = parent.getParentNode();
76+
}
77+
78+
if (parent == null) {
79+
return false;
80+
}
81+
82+
Node parentHardwareAttr = getHardwareAccelerationAttribute(parent);
83+
return "false".equals(getNodeValue(parentHardwareAttr));
84+
}
85+
86+
private static Node getHardwareAccelerationAttribute(Node node) {
87+
if (node == null || node.getAttributes() == null) {
88+
return null;
89+
}
90+
91+
return node.getAttributes().getNamedItem(ANDROID_HARDWARE_ACCELERATED);
92+
}
93+
94+
private String getNodeValue(Node node) {
95+
return (node != null) ? node.getNodeValue() : null;
96+
}
97+
98+
}

android-plugin/src/main/resources/io/ecocode/android/xml/ecocode_xml_profile.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"EC544",
99
"EC545",
1010
"EC546",
11-
"EC548"
11+
"EC548",
12+
"ESOB016"
1213
]
1314
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<img src="http://www.neomades.com/extern/partage/ecoCode/2sur5_1x.png">
2+
<p>
3+
Hardware acceleration is a feature that improves rendering performance by offloading certain 2D drawing operations to the GPU.
4+
While it can enhance UI responsiveness, it also increases memory and power consumption — especially on lower-end or older devices.
5+
<br>
6+
Since API level 14, hardware acceleration is enabled by default in Android applications. However, if not required,
7+
it is recommended to disable it explicitly in the manifest to reduce resource usage and promote energy-efficient design.
8+
<br>
9+
This rule detects components (activities, services, etc.) that either explicitly enable hardware acceleration or inherit it implicitly without being disabled at the application level.
10+
</p>
11+
12+
<h2>Noncompliant Code Example</h2>
13+
<pre>
14+
&lt;application
15+
android:label="@string/app_name"&gt;
16+
17+
&lt;activity
18+
android:name=".FastActivity"
19+
android:hardwareAccelerated="true" /&gt;
20+
21+
&lt;activity
22+
android:name=".AnotherFastActivity" /&gt;
23+
24+
&lt;/application&gt;
25+
</pre>
26+
27+
<h2>Compliant Solution</h2>
28+
<pre>
29+
&lt;application
30+
android:label="@string/app_name"
31+
android:hardwareAccelerated="false"&gt;
32+
33+
&lt;activity
34+
android:name=".FastActivity"
35+
android:hardwareAccelerated="true" /&gt; &lt;!-- Only if really needed --&gt;
36+
37+
&lt;activity
38+
android:name=".AnotherFastActivity" /&gt; &lt;!-- Inherits 'false' --&gt;
39+
40+
&lt;/application&gt;
41+
</pre>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"title": "Sobriety: Hardware acceleration",
3+
"type": "CODE_SMELL",
4+
"status": "ready",
5+
"remediation": {
6+
"func": "Constant\/Issue",
7+
"constantCost": "1min"
8+
},
9+
"tags": [
10+
"sobriety",
11+
"environment",
12+
"ecocode",
13+
"android",
14+
"eco-design"
15+
],
16+
"defaultSeverity": "Major"
17+
}

android-plugin/src/test/java/io/ecocode/xml/XmlRulesDefinitionTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ public void test() {
7171
assertThat(saveModeAwarenessXml).isNotNull();
7272
assertThat(saveModeAwarenessXml.name()).isEqualTo("Power: Save Mode Awareness");
7373

74+
RulesDefinition.Rule hardwareAccelerationXml = repository.rule("ESOB016");
75+
assertThat(hardwareAccelerationXml).isNotNull();
76+
assertThat(hardwareAccelerationXml.name()).isEqualTo("Sobriety: Hardware acceleration");
77+
78+
7479
for (RulesDefinition.Rule rule : repository.rules()) {
7580
for (RulesDefinition.Param param : rule.params()) {
7681
assertThat(param.description()).as("description for " + param.key()).isNotEmpty();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.ecocode.xml.checks.sobriety;
2+
3+
import org.junit.Test;
4+
import org.sonarsource.analyzer.commons.xml.checks.SonarXmlCheckVerifier;
5+
6+
public class HardwareAccelerationXmlRuleTest {
7+
8+
@Test
9+
public void detectExplicitOrImplicitHardwareAcceleration() {
10+
SonarXmlCheckVerifier.verifyIssues("HardwareAccelerationCheck.xml", new HardwareAccelerationXmlRule());
11+
}
12+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
package="com.example.hardwaretest">
3+
4+
<application android:label="@string/app_name" android:hardwareAccelerated="false">
5+
6+
<activity android:name=".SafeActivity" android:hardwareAccelerated="false"/>
7+
<activity android:name=".FastActivity" android:hardwareAccelerated="true"/> <!-- Noncompliant {{Hardware acceleration is enabled. Consider disabling it to reduce RAM usage.}} -->
8+
<activity android:name=".MainActivity"/>
9+
<service android:name=".MyService"/>
10+
<activity android:name=".AnotherFastActivity" android:hardwareAccelerated="true"/> <!-- Noncompliant {{Hardware acceleration is enabled. Consider disabling it to reduce RAM usage.}} -->
11+
<receiver android:name=".MyReceiver"/>
12+
<window android:name=".FancyWindow" android:hardwareAccelerated="true"/> <!-- Noncompliant {{Hardware acceleration is enabled. Consider disabling it to reduce RAM usage.}} -->
13+
<view android:name=".DefaultNotAcceleratedView"/>
14+
15+
</application>
16+
17+
<activity android:name=".OutOfAppActivity"/> <!-- Noncompliant {{Hardware acceleration is implicitly enabled. Consider disabling it explicitly with android:hardwareAccelerated="false".}} -->
18+
<view android:name=".DefaultAcceleratedView"/> <!-- Noncompliant {{Hardware acceleration is implicitly enabled. Consider disabling it explicitly with android:hardwareAccelerated="false".}} -->
19+
20+
<activity android:name=".OutOfAppActivityExplicit" android:hardwareAccelerated="true"/><!-- Noncompliant {{Hardware acceleration is enabled. Consider disabling it to reduce RAM usage.}} -->
21+
<view android:name=".DefaultAcceleratedViewExplicit" android:hardwareAccelerated="true"/><!-- Noncompliant {{Hardware acceleration is enabled. Consider disabling it to reduce RAM usage.}} -->
22+
23+
<application android:label="@string/alt_app" android:hardwareAccelerated="false">
24+
<activity android:name=".AltActivity"/>
25+
</application>
26+
</manifest>
27+

0 commit comments

Comments
 (0)