All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.checks.UnpackedNativeCodeDetector Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016 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.tools.lint.checks;

import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.TAG_APPLICATION;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.repository.GradleVersion;
import com.android.tools.lint.client.api.JavaEvaluator;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
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.Location;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.xml.AndroidManifest;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Checks for extractNativeLibs flag in AndroidManifest.xml when Native code is present.
 * 

* NOTE: This is not a final API; if you rely on this be prepared * to adjust your code for the next tools release. */ public class UnpackedNativeCodeDetector extends ResourceXmlDetector implements Detector.XmlScanner, JavaPsiScanner, Detector.ClassScanner { public static final Issue ISSUE = Issue.create( "UnpackedNativeCode", "Missing `android:extractNativeLibs=false`", "This app loads native libraries using `System.loadLibrary()`.\n\n" + "Consider adding `android:extractNativeLibs=\"false\"` " + "to the `` tag in AndroidManifest.xml. " + "Starting with Android 6.0, this will make installation faster, " + "the app will take up less space on the device " + "and updates will have smaller download sizes.", Category.PERFORMANCE, 6, Severity.WARNING, new Implementation( UnpackedNativeCodeDetector.class, EnumSet.of(Scope.MANIFEST, Scope.JAVA_FILE, Scope.JAVA_LIBRARIES), EnumSet.of(Scope.MANIFEST, Scope.JAVA_FILE) ) ).setEnabledByDefault(false); // b.android.com/232158 private static final String SYSTEM_CLASS = "java.lang.System"; private static final String RUNTIME_CLASS = "java.lang.Runtime"; private static final String SYSTEM_CLASS_ALT = "java/lang/System"; private static final String RUNTIME_CLASS_ALT = "java/lang/Runtime"; private static final String LOAD_LIBRARY = "loadLibrary"; /** * Android Gradle plugin 2.2.0+ supports uncompressed native libs in the APK */ private static final GradleVersion MIN_GRADLE_VERSION = GradleVersion.parse("2.2.0"); /** * This will be true if the project or dependencies use native libraries */ private boolean mHasNativeLibs; /** * The manifest tag location for the main project, */ private Location.Handle mApplicationTagHandle; /** * If the issue should be suppressed. */ private boolean mSuppress; /** * No-args constructor used by the lint framework to instantiate the detector. */ public UnpackedNativeCodeDetector() { } // ---- Implements Detector.ClassDetector---- @Nullable @Override public List getApplicableCallNames() { if (mSuppress) { return null; } return Collections.singletonList(LOAD_LIBRARY); } @Override public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method, @NonNull MethodInsnNode call) { String owner = call.owner; String name = call.name; if (name.equals(LOAD_LIBRARY) && (owner.equals(SYSTEM_CLASS_ALT) || owner.equals(RUNTIME_CLASS_ALT))) { mHasNativeLibs = true; } } // ---- Implements Detector.JavaPsiScanner ---- @Override public List getApplicableMethodNames() { // Identify calls to Runtime.loadLibrary() and System.loadLibrary() if (mSuppress) { return null; } return Collections.singletonList(LOAD_LIBRARY); } @Override public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) { // Identify calls to Runtime.loadLibrary() and System.loadLibrary() if (LOAD_LIBRARY.equals(method.getName())) { JavaEvaluator evaluator = context.getEvaluator(); if (evaluator.isMemberInSubClassOf(method, RUNTIME_CLASS, false) || evaluator.isMemberInSubClassOf(method, SYSTEM_CLASS, false)) { mHasNativeLibs = true; } } } // ---- Implements XmlScanner ---- @Override public Collection getApplicableElements() { return Collections.singleton(TAG_APPLICATION); } @Override public void beforeCheckProject(@NonNull Context context) { mHasNativeLibs = false; mApplicationTagHandle = null; if (!context.getMainProject().isGradleProject() || context.getMainProject().getGradleModelVersion() == null) { mSuppress = true; return; } //compileSdkVersion must be >= 23 boolean projectSupportsAttribute = context.getMainProject().getBuildSdk() >= 23; //android gradle plugin must be 2.2.0+ GradleVersion gradleVersion = context.getMainProject().getGradleModelVersion(); boolean gradleSupportsAttribute = MIN_GRADLE_VERSION.compareIgnoringQualifiers(gradleVersion) <= 0; //suppress lint check if the compile SDK or the Gradle plugin are too old mSuppress = !projectSupportsAttribute || !gradleSupportsAttribute; } @Override public void afterCheckProject(@NonNull Context context) { // Don't report issues on libraries if (context.getProject() == context.getMainProject() && !context.getMainProject().isLibrary() && mApplicationTagHandle != null) { if (!mSuppress && mHasNativeLibs) { context.report(ISSUE, mApplicationTagHandle.resolve(), "Missing attribute android:extractNativeLibs=\"false\"" + " on the `` tag."); } } } @Override public void visitElement(@NonNull XmlContext context, @NonNull Element element) { // Don't check library manifests if (context.getProject() != context.getMainProject() || context.getMainProject().isLibrary()) { return; } if (context.getDriver().isSuppressed(context, ISSUE, element)) { mSuppress = true; return; } if (TAG_APPLICATION.equals(element.getNodeName())) { Node extractAttr = element.getAttributeNodeNS(ANDROID_URI, AndroidManifest.ATTRIBUTE_EXTRACT_NATIVE_LIBS); //noinspection VariableNotUsedInsideIf if (extractAttr != null) { mSuppress = true; } else { mApplicationTagHandle = context.createLocationHandle(element); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy