From f6d59cc60c3319583295ee70fa4c1998458b209d Mon Sep 17 00:00:00 2001 From: Angelo Buono Date: Thu, 15 Feb 2024 10:53:43 +0100 Subject: [PATCH] SONARJAVA-4833 - Implement S6889: Proper Sensor Resource Management (#4654) --- .../resources/autoscan/diffs/diff_S2699.json | 2 +- .../resources/autoscan/diffs/diff_S6889.json | 6 + .../main/java/android/hardware/Camera.java | 32 +++++ .../java/android/hardware/SensorManager.java | 32 +++++ .../hardware/camera2/CameraDevice.java | 44 ++++++ .../hardware/camera2/CameraManager.java | 29 ++++ .../hardware/display/VirtualDisplay.java | 32 +++++ .../android/location/LocationManager.java | 32 +++++ .../main/java/android/media/MediaPlayer.java | 32 +++++ .../java/android/media/MediaRecorder.java | 32 +++++ .../main/java/android/media/SoundPool.java | 42 ++++++ .../android/media/audiofx/Visualizer.java | 32 +++++ .../media/projection/MediaProjection.java | 41 ++++++ .../java/android/net/wifi/WifiManager.java | 33 +++++ .../main/java/android/os/PowerManager.java | 37 +++++ .../src/main/java/android/view/Surface.java | 27 ++++ .../ReleaseSensorsCheckSample_compliant.java | 88 ++++++++++++ ...leaseSensorsCheckSample_non_compliant.java | 45 ++++++ .../java/checks/ReleaseSensorsCheck.java | 129 ++++++++++++++++++ .../java/checks/ReleaseSensorsCheckTest.java | 53 +++++++ .../org/sonar/plugins/java/CheckList.java | 4 +- .../org/sonar/l10n/java/rules/java/S6889.html | 116 ++++++++++++++++ .../org/sonar/l10n/java/rules/java/S6889.json | 24 ++++ .../java/rules/java/Sonar_way_profile.json | 1 + 24 files changed, 943 insertions(+), 2 deletions(-) create mode 100644 its/autoscan/src/test/resources/autoscan/diffs/diff_S6889.json create mode 100644 java-checks-test-sources/default/src/main/java/android/hardware/Camera.java create mode 100644 java-checks-test-sources/default/src/main/java/android/hardware/SensorManager.java create mode 100644 java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraDevice.java create mode 100644 java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraManager.java create mode 100644 java-checks-test-sources/default/src/main/java/android/hardware/display/VirtualDisplay.java create mode 100644 java-checks-test-sources/default/src/main/java/android/location/LocationManager.java create mode 100644 java-checks-test-sources/default/src/main/java/android/media/MediaPlayer.java create mode 100644 java-checks-test-sources/default/src/main/java/android/media/MediaRecorder.java create mode 100644 java-checks-test-sources/default/src/main/java/android/media/SoundPool.java create mode 100644 java-checks-test-sources/default/src/main/java/android/media/audiofx/Visualizer.java create mode 100644 java-checks-test-sources/default/src/main/java/android/media/projection/MediaProjection.java create mode 100644 java-checks-test-sources/default/src/main/java/android/net/wifi/WifiManager.java create mode 100644 java-checks-test-sources/default/src/main/java/android/os/PowerManager.java create mode 100644 java-checks-test-sources/default/src/main/java/android/view/Surface.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_compliant.java create mode 100644 java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_non_compliant.java create mode 100644 java-checks/src/main/java/org/sonar/java/checks/ReleaseSensorsCheck.java create mode 100644 java-checks/src/test/java/org/sonar/java/checks/ReleaseSensorsCheckTest.java create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.html create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.json diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2699.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2699.json index a1771da2a20..c20bf2984e7 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2699.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2699.json @@ -3,4 +3,4 @@ "hasTruePositives": true, "falseNegatives": 147, "falsePositives": 1 -} \ No newline at end of file +} diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S6889.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S6889.json new file mode 100644 index 00000000000..77f819a402c --- /dev/null +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S6889.json @@ -0,0 +1,6 @@ +{ + "ruleKey": "S6889", + "hasTruePositives": true, + "falseNegatives": 0, + "falsePositives": 0 +} \ No newline at end of file diff --git a/java-checks-test-sources/default/src/main/java/android/hardware/Camera.java b/java-checks-test-sources/default/src/main/java/android/hardware/Camera.java new file mode 100644 index 00000000000..ea96e410a03 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/hardware/Camera.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.hardware; + +public class Camera { + public static Camera open(int id) { + // mock implementation + return new Camera(); + } + + public void release() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/hardware/SensorManager.java b/java-checks-test-sources/default/src/main/java/android/hardware/SensorManager.java new file mode 100644 index 00000000000..adfd6fd87f1 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/hardware/SensorManager.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.hardware; + +public class SensorManager { + public boolean registerListener() { + // mock implementation + return true; + } + + public void unregisterListener() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraDevice.java b/java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraDevice.java new file mode 100644 index 00000000000..717a574cf5e --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraDevice.java @@ -0,0 +1,44 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.hardware.camera2; + +public class CameraDevice { + public void close() { + // mock implementation + } + + public abstract static class StateCallback { + public StateCallback() { + // mock implementation + } + + public void onClosed(CameraDevice camera) { + // mock implementation + } + + public abstract void onDisconnected(CameraDevice camera); + + public abstract void onError(CameraDevice camera, int error); + + public abstract void onOpened(CameraDevice camera); + + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraManager.java b/java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraManager.java new file mode 100644 index 00000000000..8304f1a0b67 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/hardware/camera2/CameraManager.java @@ -0,0 +1,29 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.hardware.camera2; + +import android.os.Handler; + +public class CameraManager { + public void openCamera (String cameraId, CameraDevice.StateCallback callback, Handler handler) { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/hardware/display/VirtualDisplay.java b/java-checks-test-sources/default/src/main/java/android/hardware/display/VirtualDisplay.java new file mode 100644 index 00000000000..a4dc15c4c6c --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/hardware/display/VirtualDisplay.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.hardware.display; + +public class VirtualDisplay { + + public void release() { + // mock implementation + } + + public abstract static class Callback { + public abstract void onStopped(); + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/location/LocationManager.java b/java-checks-test-sources/default/src/main/java/android/location/LocationManager.java new file mode 100644 index 00000000000..6a138d35572 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/location/LocationManager.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.location; + +public class LocationManager { + public boolean requestLocationUpdates() { + // mock implementation + return true; + } + + public void removeUpdates() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/media/MediaPlayer.java b/java-checks-test-sources/default/src/main/java/android/media/MediaPlayer.java new file mode 100644 index 00000000000..8021a9746ed --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/media/MediaPlayer.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.media; + +public class MediaPlayer { + + public MediaPlayer() { + // mock implementation + } + + public void release() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/media/MediaRecorder.java b/java-checks-test-sources/default/src/main/java/android/media/MediaRecorder.java new file mode 100644 index 00000000000..7aab1529367 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/media/MediaRecorder.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.media; + +public class MediaRecorder { + + public MediaRecorder() { + // mock implementation + } + + public void release() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/media/SoundPool.java b/java-checks-test-sources/default/src/main/java/android/media/SoundPool.java new file mode 100644 index 00000000000..f92c7302b26 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/media/SoundPool.java @@ -0,0 +1,42 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.media; + +public class SoundPool { + + public SoundPool() { + // mock implementation + } + + public void release() { + // mock implementation + } + + public static class Builder { + public Builder() { + // mock implementation + } + + public SoundPool build() { + return new SoundPool(); + } + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/media/audiofx/Visualizer.java b/java-checks-test-sources/default/src/main/java/android/media/audiofx/Visualizer.java new file mode 100644 index 00000000000..d41fea5b329 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/media/audiofx/Visualizer.java @@ -0,0 +1,32 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.media.audiofx; + +public class Visualizer { + + public Visualizer(int audioSession) { + // mock implementation + } + + public void release() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/media/projection/MediaProjection.java b/java-checks-test-sources/default/src/main/java/android/media/projection/MediaProjection.java new file mode 100644 index 00000000000..5e5e772672c --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/media/projection/MediaProjection.java @@ -0,0 +1,41 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.media.projection; + +import android.hardware.display.VirtualDisplay; +import android.os.Handler; +import android.view.Surface; + +public class MediaProjection { + + public VirtualDisplay createVirtualDisplay( + String name, + int width, + int height, + int dpi, + int flags, + Surface surface, + VirtualDisplay.Callback callback, + Handler handler) { + // mock implementation + return new VirtualDisplay(); + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/net/wifi/WifiManager.java b/java-checks-test-sources/default/src/main/java/android/net/wifi/WifiManager.java new file mode 100644 index 00000000000..d4ea699da21 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/net/wifi/WifiManager.java @@ -0,0 +1,33 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.net.wifi; + +public class WifiManager { + public class MulticastLock { + public void acquire() { + // mock implementation + } + + public void release() { + // mock implementation + } + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/os/PowerManager.java b/java-checks-test-sources/default/src/main/java/android/os/PowerManager.java new file mode 100644 index 00000000000..b2ef64151eb --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/os/PowerManager.java @@ -0,0 +1,37 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.os; + +public class PowerManager { + + public final class WakeLock { + public void acquire() { + // mock implementation + } + public void acquire(long timeout) { + // mock implementation + } + + public void release() { + // mock implementation + } + } +} diff --git a/java-checks-test-sources/default/src/main/java/android/view/Surface.java b/java-checks-test-sources/default/src/main/java/android/view/Surface.java new file mode 100644 index 00000000000..b41dd2ace8d --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/android/view/Surface.java @@ -0,0 +1,27 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package android.view; + +public class Surface { + public void release() { + // mock implementation + } +} diff --git a/java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_compliant.java b/java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_compliant.java new file mode 100644 index 00000000000..0e7f5194135 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_compliant.java @@ -0,0 +1,88 @@ +package checks; + +import android.hardware.camera2.CameraDevice; + +public class ReleaseSensorsCheckSample_compliant { + + public static class MockedSensorsExample { + public void acquireSensors( + android.location.LocationManager locationManager, + android.hardware.SensorManager sensorManager, + android.os.PowerManager.WakeLock wakeLock, + android.media.projection.MediaProjection mediaProjection, + android.net.wifi.WifiManager.MulticastLock multicastLock, + android.hardware.camera2.CameraManager cameraManager) { + + locationManager.requestLocationUpdates(); + sensorManager.registerListener(); + wakeLock.acquire(); + multicastLock.acquire(); + mediaProjection.createVirtualDisplay("name", 1, 1, 1, 1, null,null, null); + new android.media.SoundPool.Builder().build(); + new android.media.audiofx.Visualizer(0); + android.hardware.Camera.open(1); + new android.media.MediaPlayer(); + new android.media.MediaRecorder(); + + cameraManager.openCamera("id", + new android.hardware.camera2.CameraDevice.StateCallback() { + @Override + public void onDisconnected(CameraDevice camera) { + camera.close(); + } + + @Override + public void onError(CameraDevice camera, int error) { + camera.close(); + } + + @Override + public void onOpened(android.hardware.camera2.CameraDevice camera) { + // mock implementation + } + }, + null); + } + + public void releaseSensors( + android.location.LocationManager locationManager, + android.hardware.SensorManager sensorManager, + android.os.PowerManager.WakeLock wakeLock, + android.net.wifi.WifiManager.MulticastLock multicastLock, + android.hardware.display.VirtualDisplay virtualDisplay, + android.hardware.Camera camera, + android.media.SoundPool soundPool, + android.media.audiofx.Visualizer visualizer, + android.media.MediaPlayer mediaPlayer, + android.media.MediaRecorder mediaRecorder) { + + camera.release(); + locationManager.removeUpdates(); + sensorManager.unregisterListener(); + wakeLock.release(); + multicastLock.release(); + virtualDisplay.release(); + soundPool.release(); + visualizer.release(); + mediaPlayer.release(); + mediaRecorder.release(); + } + } + + public static class FakeSensorExample { + public void test() { + FakeSensor fakeSensor = new FakeSensor(); + fakeSensor.acquire(); // Compliant + } + + public static class FakeSensor { + public void acquire() { + // mock implementation + } + + public void release() { + // mock implementation + } + } + } +} diff --git a/java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_non_compliant.java b/java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_non_compliant.java new file mode 100644 index 00000000000..cc7639dfecf --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/ReleaseSensorsCheckSample_non_compliant.java @@ -0,0 +1,45 @@ + +package checks; + +import android.hardware.camera2.CameraDevice; + +public class ReleaseSensorsCheckSample_non_compliant { + public void aquireSensors( + android.location.LocationManager locationManager, + android.hardware.SensorManager sensorManager, + android.os.PowerManager.WakeLock wakeLock, + android.media.projection.MediaProjection mediaProjection, + android.net.wifi.WifiManager.MulticastLock multicastLock, + android.hardware.camera2.CameraManager cameraManager) { + + locationManager.requestLocationUpdates(); // Noncompliant {{Make sure to release this sensor after use.}} + sensorManager.registerListener(); // Noncompliant + wakeLock.acquire(); // Noncompliant + multicastLock.acquire(); // Noncompliant + mediaProjection.createVirtualDisplay("name", 1, 1, 1, 1, null,null, null); // Noncompliant + android.hardware.Camera camera = android.hardware.Camera.open(1); // Noncompliant + android.media.SoundPool soundPool = new android.media.SoundPool.Builder().build(); // Noncompliant + android.media.audiofx.Visualizer visualizer = new android.media.audiofx.Visualizer(0); // Noncompliant + android.media.MediaPlayer mediaPlayer = new android.media.MediaPlayer(); // Noncompliant + android.media.MediaRecorder mediaRecorder = new android.media.MediaRecorder(); // Noncompliant + + cameraManager.openCamera("id", // Noncompliant + new android.hardware.camera2.CameraDevice.StateCallback() { + @Override + public void onDisconnected(CameraDevice camera) { + // mock implementation + } + + @Override + public void onError(CameraDevice camera, int error) { + // mock implementation + } + + @Override + public void onOpened(android.hardware.camera2.CameraDevice camera) { + // mock implementation + } + }, + null); + } +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/ReleaseSensorsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/ReleaseSensorsCheck.java new file mode 100644 index 00000000000..a2c87f3d4ca --- /dev/null +++ b/java-checks/src/main/java/org/sonar/java/checks/ReleaseSensorsCheck.java @@ -0,0 +1,129 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.java.checks; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.IntStream; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.semantic.MethodMatchers; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; +import org.sonar.plugins.java.api.tree.NewClassTree; +import org.sonar.plugins.java.api.tree.Tree; + +/** + * Rule S6889 flags issues when any sensor defined in {@link AcquireReleaseSensor} is not released. + * The intent of this rule is to make the developer aware of the need to release the sensor when it is not needed anymore. + *

+ * The implemented logic checks that after acquiring a sensor there is at least one release invocation within the same file. + */ +@Rule(key = "S6889") +public class ReleaseSensorsCheck extends IssuableSubscriptionVisitor { + + private enum AcquireReleaseSensor { + LOCATION_MANAGER("android.location.LocationManager", "requestLocationUpdates", "removeUpdates"), + SENSOR_MANAGER("android.hardware.SensorManager", "registerListener", "unregisterListener"), + VIRTUAL_DISPLAY("android.media.projection.MediaProjection", "createVirtualDisplay", "android.hardware.display.VirtualDisplay", RELEASE), + CAMERA("android.hardware.Camera", "open", RELEASE), + CAMERA2("android.hardware.camera2.CameraManager", "openCamera", "android.hardware.camera2.CameraDevice", "close"), + POWER_MANAGER("android.os.PowerManager$WakeLock", ACQUIRE, RELEASE), + WIFI_MANAGER("android.net.wifi.WifiManager$MulticastLock", ACQUIRE, RELEASE), + SOUND_POOL("android.media.SoundPool$Builder", "build", "android.media.SoundPool", RELEASE), + VISUALIZER("android.media.audiofx.Visualizer", MethodMatchers.CONSTRUCTOR, RELEASE), + MEDIA_PLAYER("android.media.MediaPlayer", MethodMatchers.CONSTRUCTOR, RELEASE), + MEDIA_RECORDER("android.media.MediaRecorder", MethodMatchers.CONSTRUCTOR, RELEASE); + + private final MethodMatchers acquireMethodMatcher; + private final MethodMatchers releaseMethodMatcher; + + AcquireReleaseSensor(String sensorClass, String acquireMethod, String releaseMethod) { + this.acquireMethodMatcher = MethodMatchers.create().ofTypes(sensorClass).names(acquireMethod).withAnyParameters().build(); + this.releaseMethodMatcher = MethodMatchers.create().ofTypes(sensorClass).names(releaseMethod).withAnyParameters().build(); + } + + AcquireReleaseSensor(String acquireSensorClass, String acquireMethod, String releaseSensorClass, String releaseMethod) { + this.acquireMethodMatcher = MethodMatchers.create().ofTypes(acquireSensorClass).names(acquireMethod).withAnyParameters().build(); + this.releaseMethodMatcher = MethodMatchers.create().ofTypes(releaseSensorClass).names(releaseMethod).withAnyParameters().build(); + } + } + + private static class AcquireReleaseStatus { + List acquireInvocations = new LinkedList<>(); + boolean released = false; + } + + private static final String ACQUIRE = "acquire"; + private static final String RELEASE = "release"; + private AcquireReleaseStatus[] statuses; + + @Override + public void setContext(JavaFileScannerContext context) { + super.setContext(context); + initStatuses(); + } + + @Override + public void leaveFile(JavaFileScannerContext context) { + Arrays.stream(statuses) + .filter(status -> !status.released) + .forEach(status -> status.acquireInvocations.forEach(mit -> reportIssue(mit, "Make sure to release this sensor after use."))); + statuses = null; + } + + private void initStatuses() { + this.statuses = IntStream.range(0, AcquireReleaseSensor.values().length) + .mapToObj(i -> new AcquireReleaseStatus()) + .toArray(AcquireReleaseStatus[]::new); + } + + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.METHOD_INVOCATION, Tree.Kind.NEW_CLASS); + } + + @Override + public void visitNode(Tree tree) { + // collect acquire invocations + Arrays.stream(AcquireReleaseSensor.values()) + .filter(sensor -> isMethodMatched(tree, sensor.acquireMethodMatcher)) + .findAny() + .ifPresent(sensor -> statuses[sensor.ordinal()].acquireInvocations.add(tree)); + + // flag released invocations + Arrays.stream(AcquireReleaseSensor.values()) + .filter(sensor -> isMethodMatched(tree, sensor.releaseMethodMatcher)) + .findAny() + .ifPresent(sensor -> statuses[sensor.ordinal()].released = true); + } + + private static boolean isMethodMatched(Tree tree, MethodMatchers methodMatchers) { + switch (tree.kind()) { + case METHOD_INVOCATION: + return methodMatchers.matches((MethodInvocationTree) tree); + case NEW_CLASS: + return methodMatchers.matches((NewClassTree) tree); + default: + return false; + } + } +} diff --git a/java-checks/src/test/java/org/sonar/java/checks/ReleaseSensorsCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/ReleaseSensorsCheckTest.java new file mode 100644 index 00000000000..7394ce948e6 --- /dev/null +++ b/java-checks/src/test/java/org/sonar/java/checks/ReleaseSensorsCheckTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.sonar.java.checks.verifier.TestUtils; + +class ReleaseSensorsCheckTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile(TestUtils.mainCodeSourcesPath("checks/ReleaseSensorsCheckSample_compliant.java")) + .withCheck(new ReleaseSensorsCheck()) + .verifyNoIssues(); + } + + @Test + void test_noncompliant() { + CheckVerifier.newVerifier() + .onFile(TestUtils.mainCodeSourcesPath("checks/ReleaseSensorsCheckSample_non_compliant.java")) + .withCheck(new ReleaseSensorsCheck()) + .verifyIssues(); + } + + @Test + void test_no_semantic() { + CheckVerifier.newVerifier() + .onFile(TestUtils.mainCodeSourcesPath("checks/ReleaseSensorsCheckSample_non_compliant.java")) + .withCheck(new ReleaseSensorsCheck()) + .withoutSemantic() + .verifyNoIssues(); + } + +} diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/CheckList.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/CheckList.java index 05f9d5e057b..83ea8f27c31 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/CheckList.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/CheckList.java @@ -241,6 +241,7 @@ import org.sonar.java.checks.MissingNewLineAtEndOfFileCheck; import org.sonar.java.checks.MissingOverridesInRecordWithArrayComponentCheck; import org.sonar.java.checks.MissingPackageInfoCheck; +import org.sonar.java.checks.MissingPathVariableAnnotationCheck; import org.sonar.java.checks.ModifiersOrderCheck; import org.sonar.java.checks.ModulusEqualityCheck; import org.sonar.java.checks.MultilineBlocksCurlyBracesCheck; @@ -280,7 +281,6 @@ import org.sonar.java.checks.OverwrittenKeyCheck; import org.sonar.java.checks.ParameterReassignedToCheck; import org.sonar.java.checks.ParsingErrorCheck; -import org.sonar.java.checks.MissingPathVariableAnnotationCheck; import org.sonar.java.checks.PopulateBeansCheck; import org.sonar.java.checks.PredictableSeedCheck; import org.sonar.java.checks.PreferStreamAnyMatchCheck; @@ -313,6 +313,7 @@ import org.sonar.java.checks.RedundantTypeCastCheck; import org.sonar.java.checks.ReflectionOnNonRuntimeAnnotationCheck; import org.sonar.java.checks.RegexPatternsNeedlesslyCheck; +import org.sonar.java.checks.ReleaseSensorsCheck; import org.sonar.java.checks.RepeatAnnotationCheck; import org.sonar.java.checks.ReplaceGuavaWithJavaCheck; import org.sonar.java.checks.ReplaceLambdaByMethodRefCheck; @@ -1013,6 +1014,7 @@ public final class CheckList { RegexLookaheadCheck.class, RegexPatternsNeedlesslyCheck.class, RegexStackOverflowCheck.class, + ReleaseSensorsCheck.class, ReluctantQuantifierCheck.class, ReluctantQuantifierWithEmptyContinuationCheck.class, ReplaceGuavaWithJavaCheck.class, diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.html new file mode 100644 index 00000000000..c01e3b3818b --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.html @@ -0,0 +1,116 @@ +

Why is this an issue?

+

Optimizing resource usage and preventing unnecessary battery drain are critical considerations in Android development. Failing to release sensor +resources when they are no longer needed can lead to prolonged device activity, negatively impacting battery life. Common Android sensors, such as +cameras, GPS, and microphones, provide a method to release resources after they are not in use anymore.

+

This rule identifies situations where a sensor is not released after being utilized, helping developers maintain efficient and battery-friendly +applications.

+ +

How to fix it

+

Ensure that resources are released when they are no longer needed. This can be done by calling the appropriate release method, such as +release(), removeUpdates(), unregisterListener(), or stop().

+

Code examples

+ +

Noncompliant code example

+
+public void method() {
+  PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+  PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Wake Lock");
+  wakeLock.acquire(); // Noncompliant
+  // do some work...
+}
+
+

Compliant solution

+
+public void method() {
+  PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+  PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Wake Lock");
+  wakeLock.acquire(); // Compliant
+  // do some work...
+  wakeLock.release();
+}
+
+ +

Noncompliant code example

+
+public void method() {
+  MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
+  mediaPlayer.start(); // Noncompliant
+  // do some work...
+}
+
+

Compliant solution

+
+public void onCreate() {
+  MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
+  mediaPlayer.start(); // Compliant
+  // do some work...
+  wakeLock.release();
+}
+
+ +

Noncompliant code example

+
+public void method() {
+  SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
+  Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+  sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL); // Noncompliant
+  // do some work...
+}
+
+

Compliant solution

+
+public void method() {
+  SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
+  Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+  sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL); // Compliant
+  // do some work...
+  sensorManager.unregisterListener(this);
+}
+
+

Resources

+

Documentation

+ + diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.json new file mode 100644 index 00000000000..6f18125220f --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6889.json @@ -0,0 +1,24 @@ +{ + "title": "Proper Sensor Resource Management", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "10min" + }, + "tags": [ + "sustainability", + "android" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-6889", + "sqKey": "S6889", + "scope": "Main", + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "EFFICIENT" + } +} diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json index 5a4e493ef44..83995e1a32e 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json @@ -499,6 +499,7 @@ "S6857", "S6862", "S6863", + "S6889" "S6904" ] }