blob: 0d6e67d25cde231372ef31464491f760526cdef6 [file] [log] [blame]
/*
* Copyright 2020 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.
*/
@file:Suppress("UnstableApiUsage")
package androidx.work.lint
import com.android.SdkConstants.ANDROID_URI
import com.android.SdkConstants.ATTR_NAME
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.android.tools.lint.detector.api.XmlContext
import com.android.tools.lint.detector.api.XmlScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
import org.w3c.dom.Element
import java.util.EnumSet
/**
* Detects usage of `ForegroundInfo` with the `foregroundServiceType` and ensure that the service
* type is defined in the `AndroidManifest.xml`.
*/
class SpecifyForegroundServiceTypeIssueDetector : Detector(), SourceCodeScanner, XmlScanner {
private val knownServiceTypes = mutableSetOf<String>()
companion object {
val ISSUE = Issue.create(
id = "SpecifyForegroundServiceType",
briefDescription = "Specify foreground service type",
explanation = """
When using the setForegroundAsync() API, the application must override <service /> \
entry for `SystemForegroundService` to include the foreground service type in the \
`AndroidManifest.xml` file.
""",
androidSpecific = true,
category = Category.CORRECTNESS,
severity = Severity.FATAL,
implementation = Implementation(
SpecifyForegroundServiceTypeIssueDetector::class.java,
EnumSet.of(Scope.JAVA_FILE, Scope.MANIFEST)
)
)
private val SERVICE_TYPE_MAPPING = mapOf(
1 to "dataSync",
2 to "mediaPlayback",
4 to "phoneCall",
8 to "location",
16 to "connectedDevice",
32 to "mediaProjection"
)
}
override fun getApplicableConstructorTypes() = listOf("androidx.work.ForegroundInfo")
override fun getApplicableElements() = listOf("service")
override fun visitElement(context: XmlContext, element: Element) {
val name = element.getAttributeNS(ANDROID_URI, ATTR_NAME)
if ("androidx.work.impl.foreground.SystemForegroundService" == name) {
val serviceTypes = element.getAttributeNS(ANDROID_URI, "foregroundServiceType")
if (serviceTypes != null) {
knownServiceTypes += serviceTypes.split("|")
}
}
}
override fun visitConstructor(
context: JavaContext,
node: UCallExpression,
constructor: PsiMethod
) {
if (node.valueArgumentCount > 2) {
val type = node.valueArguments[2].evaluate()
if (type != null && type is Int && type > 0) {
for ((mask, name) in SERVICE_TYPE_MAPPING) {
if (type and mask > 0) {
if (name !in knownServiceTypes) {
context.report(
issue = ISSUE,
location = context.getLocation(node),
message = "Missing $name foregroundServiceType in " +
"the AndroidManifest.xml"
)
}
}
}
}
}
}
}