Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 348dbe4f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes I1f7b0181,I1ee616a3 into tm-qpr-dev am: a53c7bbe

parents 3cfa9f55 a53c7bbe
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.android.internal.systemui.lint

import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression

/** Detects usage of Context.getSystemService() and suggests to use an injected instance instead. */
@Suppress("UnstableApiUsage")
class NonInjectedServiceDetector : Detector(), SourceCodeScanner {

    override fun getApplicableMethodNames(): List<String> {
        return listOf("getSystemService")
    }

    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
        val evaluator = context.evaluator
        if (
            !evaluator.isStatic(method) &&
                method.name == "getSystemService" &&
                method.containingClass?.qualifiedName == "android.content.Context"
        ) {
            context.report(
                ISSUE,
                method,
                context.getNameLocation(node),
                "Use @Inject to get the handle to a system-level services instead of using " +
                    "Context.getSystemService()"
            )
        }
    }

    companion object {
        @JvmField
        val ISSUE: Issue =
            Issue.create(
                id = "NonInjectedService",
                briefDescription =
                    "System-level services should be retrieved using " +
                        "@Inject instead of Context.getSystemService().",
                explanation =
                    "Context.getSystemService() should be avoided because it makes testing " +
                        "difficult. Instead, use an injected service. For example, " +
                        "instead of calling Context.getSystemService(UserManager.class), " +
                        "use @Inject and add UserManager to the constructor",
                category = Category.CORRECTNESS,
                priority = 8,
                severity = Severity.WARNING,
                implementation =
                    Implementation(NonInjectedServiceDetector::class.java, Scope.JAVA_FILE_SCOPE)
            )
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression

@Suppress("UnstableApiUsage")
class RegisterReceiverViaContextDetector : Detector(), SourceCodeScanner {

    override fun getApplicableMethodNames(): List<String> {
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ class SystemUIIssueRegistry : IssueRegistry() {
                GetMainLooperViaContextDetector.ISSUE,
                RegisterReceiverViaContextDetector.ISSUE,
                SoftwareBitmapDetector.ISSUE,
                NonInjectedServiceDetector.ISSUE,
        )

    override val api: Int
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.android.internal.systemui.lint

import com.android.tools.lint.checks.infrastructure.LintDetectorTest.java

/*
 * This file contains stubs of framework APIs and System UI classes for testing purposes only. The
 * stubs are not used in the lint detectors themselves.
 */
@Suppress("UnstableApiUsage")
internal val androidStubs =
    arrayOf(
        java(
            """
package android.app;

public class ActivityManager {
    public static int getCurrentUser() {}
}
"""
        ),
        java(
            """
package android.os;
import android.content.pm.UserInfo;
import android.annotation.UserIdInt;

public class UserManager {
    public UserInfo getUserInfo(@UserIdInt int userId) {}
}
"""
        ),
        java("""
package android.annotation;

public @interface UserIdInt {}
"""),
        java("""
package android.content.pm;

public class UserInfo {}
"""),
        java("""
package android.os;

public class Looper {}
"""),
        java("""
package android.os;

public class Handler {}
"""),
        java("""
package android.content;

public class ServiceConnection {}
"""),
        java("""
package android.os;

public enum UserHandle {
    ALL
}
"""),
        java(
            """
package android.content;
import android.os.UserHandle;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;

public class Context {
    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {}
    public void registerReceiverAsUser(
            BroadcastReceiver receiver, UserHandle user, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {}
    public void registerReceiverForAllUsers(
            BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission,
            Handler scheduler) {}
    public void sendBroadcast(Intent intent) {}
    public void sendBroadcast(Intent intent, String receiverPermission) {}
    public void sendBroadcastAsUser(Intent intent, UserHandle userHandle, String permission) {}
    public void bindService(Intent intent) {}
    public void bindServiceAsUser(
            Intent intent, ServiceConnection connection, int flags, UserHandle userHandle) {}
    public void unbindService(ServiceConnection connection) {}
    public Looper getMainLooper() { return null; }
    public Executor getMainExecutor() { return null; }
    public Handler getMainThreadHandler() { return null; }
    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) { return null; }
    public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
}
"""
        ),
        java(
            """
package android.app;
import android.content.Context;

public class Activity extends Context {}
"""
        ),
        java(
            """
package android.graphics;

public class Bitmap {
    public enum Config {
        ARGB_8888,
        RGB_565,
        HARDWARE
    }
    public static Bitmap createBitmap(int width, int height, Config config) {
        return null;
    }
}
"""
        ),
        java("""
package android.content;

public class BroadcastReceiver {}
"""),
        java("""
package android.content;

public class IntentFilter {}
"""),
        java(
            """
package com.android.systemui.settings;
import android.content.pm.UserInfo;

public interface UserTracker {
    int getUserId();
    UserInfo getUserInfo();
}
"""
        ),
    )
+33 −57
Original line number Diff line number Diff line
@@ -17,26 +17,26 @@
package com.android.internal.systemui.lint

import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestFiles
import com.android.tools.lint.checks.infrastructure.TestLintTask
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import org.junit.Test

@Suppress("UnstableApiUsage")
class BindServiceViaContextDetectorTest : LintDetectorTest() {

    override fun getDetector(): Detector = BindServiceViaContextDetector()
    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)

    override fun getIssues(): List<Issue> = listOf(
            BindServiceViaContextDetector.ISSUE)
    override fun getIssues(): List<Issue> = listOf(BindServiceViaContextDetector.ISSUE)

    private val explanation = "Binding or unbinding services are synchronous calls"

    @Test
    fun testBindService() {
        lint().files(
        lint()
            .files(
                TestFiles.java(
                        """
                    package test.pkg;
@@ -49,8 +49,10 @@ class BindServiceViaContextDetectorTest : LintDetectorTest() {
                        }
                    }
                """
                ).indented(),
                *stubs)
                    )
                    .indented(),
                *stubs
            )
            .issues(BindServiceViaContextDetector.ISSUE)
            .run()
            .expectWarningCount(1)
@@ -59,7 +61,8 @@ class BindServiceViaContextDetectorTest : LintDetectorTest() {

    @Test
    fun testBindServiceAsUser() {
        lint().files(
        lint()
            .files(
                TestFiles.java(
                        """
                    package test.pkg;
@@ -73,8 +76,10 @@ class BindServiceViaContextDetectorTest : LintDetectorTest() {
                        }
                    }
                """
                ).indented(),
                *stubs)
                    )
                    .indented(),
                *stubs
            )
            .issues(BindServiceViaContextDetector.ISSUE)
            .run()
            .expectWarningCount(1)
@@ -83,7 +88,8 @@ class BindServiceViaContextDetectorTest : LintDetectorTest() {

    @Test
    fun testUnbindService() {
        lint().files(
        lint()
            .files(
                TestFiles.java(
                        """
                    package test.pkg;
@@ -96,45 +102,15 @@ class BindServiceViaContextDetectorTest : LintDetectorTest() {
                        }
                    }
                """
                ).indented(),
                *stubs)
                    )
                    .indented(),
                *stubs
            )
            .issues(BindServiceViaContextDetector.ISSUE)
            .run()
            .expectWarningCount(1)
            .expectContains(explanation)
    }

    private val contextStub: TestFile = java(
            """
        package android.content;
        import android.os.UserHandle;

        public class Context {
            public void bindService(Intent intent) {};
            public void bindServiceAsUser(Intent intent, ServiceConnection connection, int flags,
                                          UserHandle userHandle) {};
            public void unbindService(ServiceConnection connection) {};
        }
        """
    )

    private val serviceConnectionStub: TestFile = java(
            """
        package android.content;

        public class ServiceConnection {}
        """
    )

    private val userHandleStub: TestFile = java(
            """
        package android.os;

        public enum UserHandle {
            ALL
        }
        """
    )

    private val stubs = arrayOf(contextStub, serviceConnectionStub, userHandleStub)
    private val stubs = androidStubs
}
Loading