/** * Is this drawable folder for an Android 3.0 drawable? This will be the * case if it specifies -v11+, or if the minimum SDK version declared in the * manifest is at least 11. */ private boolean isAndroid30(Context context, int folderVersion) { return folderVersion >= 11 || context.project.getMinSdk() >= 11; }
/** * Is this drawable folder for an Android 3.0 drawable? This will be the * case if it specifies -v11+, or if the minimum SDK version declared in the * manifest is at least 11. */ private static boolean isAndroid30(Context context, int folderVersion) { return folderVersion >= 11 || context.getMainProject().getMinSdk() >= 11; }
/** * Is this drawable folder for an Android 3.0 drawable? This will be the * case if it specifies -v11+, or if the minimum SDK version declared in the * manifest is at least 11. */ private static boolean isAndroid30(Context context, int folderVersion) { return folderVersion >= 11 || context.getMainProject().getMinSdk() >= 11; }
/** * Is this drawable folder for an Android 2.3 drawable? This will be the * case if it specifies -v9 or -v10, or if the minimum SDK version declared in the * manifest is 9 or 10 (and it does not specify some higher version like -v11 */ private boolean isAndroid23(Context context, int folderVersion) { if (isAndroid30(context, folderVersion)) { return false; } if (folderVersion == 9 || folderVersion == 10) { return true; } int minSdk = context.project.getMinSdk(); return minSdk == 9 || minSdk == 10; }
/** Return the theme to be used for the given layout */ private String getTheme(Context context, String layoutName) { if (mActivityToTheme != null && mLayoutToActivity != null) { List<String> activities = mLayoutToActivity.get(layoutName); if (activities != null) { for (String activity : activities) { String theme = mActivityToTheme.get(activity); if (theme != null) { return theme; } } } } if (mManifestTheme != null) { return mManifestTheme; } Project project = context.project; int apiLevel = project.getTargetSdk(); if (apiLevel == -1) { apiLevel = project.getMinSdk(); } if (apiLevel >= 11) { return "@android:style/Theme.Holo"; //$NON-NLS-1$ } else { return "@android:style/Theme"; //$NON-NLS-1$ } }
/** * Is this drawable folder for an Android 2.3 drawable? This will be the * case if it specifies -v9 or -v10, or if the minimum SDK version declared in the * manifest is 9 or 10 (and it does not specify some higher version like -v11 */ private static boolean isAndroid23(Context context, int folderVersion) { if (isAndroid30(context, folderVersion)) { return false; } if (folderVersion == 9 || folderVersion == 10) { return true; } int minSdk = context.getMainProject().getMinSdk(); return minSdk == 9 || minSdk == 10; }
/** * Is this drawable folder for an Android 2.3 drawable? This will be the * case if it specifies -v9 or -v10, or if the minimum SDK version declared in the * manifest is 9 or 10 (and it does not specify some higher version like -v11 */ private static boolean isAndroid23(Context context, int folderVersion) { if (isAndroid30(context, folderVersion)) { return false; } if (folderVersion == 9 || folderVersion == 10) { return true; } int minSdk = context.getMainProject().getMinSdk(); return minSdk == 9 || minSdk == 10; }
@Override public void visitElement(@NonNull XmlContext context, @NonNull Element element) { // NOTE: This should NOT be checking *minSdkVersion*, but *targetSdkVersion* // or even buildTarget instead. However, there's a risk that this will flag // way too much and make the rule annoying until API 17 support becomes // more widespread, so for now limit the check to those projects *really* // working with 17. When API 17 reaches a certain amount of adoption, change // this to flag all apps supporting 17, including those supporting earlier // versions as well. if (context.getMainProject().getMinSdk() < 17) { return; } if (mTextFields == null) { mTextFields = new ArrayList<>(); } mTextFields.add(element); } }
/** Return the theme to be used for the given layout */ private String getTheme(Context context, String layoutName) { if (mActivityToTheme != null && mLayoutToActivity != null) { List<String> activities = mLayoutToActivity.get(layoutName); if (activities != null) { for (String activity : activities) { String theme = mActivityToTheme.get(activity); if (theme != null) { return theme; } } } } if (mManifestTheme != null) { return mManifestTheme; } Project project = context.getMainProject(); int apiLevel = project.getTargetSdk(); if (apiLevel == -1) { apiLevel = project.getMinSdk(); } if (apiLevel >= 11) { return "@android:style/Theme.Holo"; } else { return "@android:style/Theme"; } }
/** Return the theme to be used for the given layout */ private String getTheme(Context context, String layoutName) { if (mActivityToTheme != null && mLayoutToActivity != null) { List<String> activities = mLayoutToActivity.get(layoutName); if (activities != null) { for (String activity : activities) { String theme = mActivityToTheme.get(activity); if (theme != null) { return theme; } } } } if (mManifestTheme != null) { return mManifestTheme; } Project project = context.getMainProject(); int apiLevel = project.getTargetSdk(); if (apiLevel == -1) { apiLevel = project.getMinSdk(); } if (apiLevel >= 11) { return "@android:style/Theme.Holo"; //$NON-NLS-1$ } else { return "@android:style/Theme"; //$NON-NLS-1$ } }
@Override public void visitElement(@NonNull XmlContext context, @NonNull Element element) { // NOTE: This should NOT be checking *minSdkVersion*, but *targetSdkVersion* // or even buildTarget instead. However, there's a risk that this will flag // way too much and make the rule annoying until API 17 support becomes // more widespread, so for now limit the check to those projects *really* // working with 17. When API 17 reaches a certain amount of adoption, change // this to flag all apps supporting 17, including those supporting earlier // versions as well. if (context.getMainProject().getMinSdk() < 17) { return; } if (mTextFields == null) { mTextFields = new ArrayList<Element>(); } mTextFields.add(element); } }
@Override public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method, @NonNull MethodInsnNode call) { String owner = call.owner; if (owner.equals("android/util/FloatMath") //$NON-NLS-1$ && context.getProject().getMinSdk() >= 8) { String message = String.format( "Use `java.lang.Math#%1$s` instead of `android.util.FloatMath#%1$s()` " + "since it is faster as of API 8", call.name); context.report(ISSUE, method, call, context.getLocation(call), message /*data*/); } } }
private void checkWebpSupported(@NonNull Context context, @NonNull File[] files) { int minSdk = Math.max(context.getMainProject().getMinSdk(), context.getDriver().getResourceFolderVersion(files[0])); if (minSdk >= 18) {
private static void reportWifiServiceLeak(@NonNull Issue issue, @NonNull JavaContext context, @NonNull PsiMethodCallExpression call) { if (context.getMainProject().getMinSdk() >= 24) { // Bug is fixed in Nougat return; } String qualifier = ""; if (call.getMethodExpression().getQualifierExpression() != null) { qualifier = call.getMethodExpression().getQualifierExpression().getText(); } String message = String.format("The WIFI_SERVICE must be looked up on the " + "Application context or memory will leak on devices < Android N. " + "Try changing `%1$s` to `%1$s.getApplicationContext()` ", qualifier); context.report(issue, call, context.getLocation(call), message); }
@Override public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) { // Ignore the issue if we never build for any API less than 17. if (context.getMainProject().getMinSdk() >= 17) { return; } JavaEvaluator evaluator = context.getEvaluator(); if (!evaluator.methodMatches(method, WEB_VIEW, true, TYPE_OBJECT, TYPE_STRING)) { return; } String message = "`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: " + "JavaScript can use reflection to manipulate application"; context.report(ISSUE, node, context.getNameLocation(node), message); } }
@Override public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) { if (context.getEvaluator().isMemberInClass(method, "android.util.FloatMath") && context.getProject().getMinSdk() >= 8) { String message = String.format( "Use `java.lang.Math#%1$s` instead of `android.util.FloatMath#%1$s()` " + "since it is faster as of API 8", method.getName()); PsiReferenceExpression expression = call.getMethodExpression(); PsiElement operand = expression.getQualifier(); Location location = operand != null && expression.getReferenceNameElement() != null ? context.getRangeLocation(operand, 0, expression.getReferenceNameElement(), 0) : context.getLocation(call); context.report(ISSUE, call, location, message); } } }
@Override public void beforeCheckProject(@NonNull Context context) { AndroidProject model = context.getProject().getGradleProjectModel(); if (model == null) { mSkipChecks = true; return; } if (context.getProject().getMinSdk() >= 21) { mSkipChecks = true; return; } GradleVersion version = context.getProject().getGradleModelVersion(); if (version == null || version.getMajor() < 2) { mSkipChecks = true; return; } Variant currentVariant = context.getProject().getCurrentVariant(); if (currentVariant == null) { mSkipChecks = true; return; } if (Boolean.TRUE.equals( currentVariant.getMergedFlavor().getVectorDrawables().getUseSupportLibrary())) { mUseSupportLibrary = true; } }
@Override public void afterCheckProject(@NonNull Context context) { if (!mSeenAllowBackup && context.isEnabled(ALLOW_BACKUP) && !context.getProject().isLibrary() && context.getMainProject().getMinSdk() >= 4) { Location location = getMainApplicationTagLocation(context); context.report(ALLOW_BACKUP, location, "Should explicitly set `android:allowBackup` to `true` or " + "`false` (it's `true` by default, and that can have some security " + "implications for the application's data)"); } if (!context.getMainProject().isLibrary() && !mSeenAppIcon && context.isEnabled(APPLICATION_ICON)) { Location location = getMainApplicationTagLocation(context); context.report(APPLICATION_ICON, location, "Should explicitly set `android:icon`, there is no default"); } }
@Override public void afterCheckProject(@NonNull Context context) { if (!mSeenAllowBackup && context.isEnabled(ALLOW_BACKUP) && !context.getProject().isLibrary() && context.getMainProject().getMinSdk() >= 4) { Location location = getMainApplicationTagLocation(context); context.report(ALLOW_BACKUP, location, "Should explicitly set `android:allowBackup` to `true` or " + "`false` (it's `true` by default, and that can have some security " + "implications for the application's data)"); } if (!context.getMainProject().isLibrary() && !mSeenAppIcon && context.isEnabled(APPLICATION_ICON)) { Location location = getMainApplicationTagLocation(context); context.report(APPLICATION_ICON, location, "Should explicitly set `android:icon`, there is no default"); } }
@Override public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, @NonNull MethodInvocation node) { // Ignore the issue if we never build for any API less than 17. if (context.getMainProject().getMinSdk() >= 17) { return; } // Ignore if the method doesn't fit our description. ResolvedNode resolved = context.resolve(node); if (!(resolved instanceof ResolvedMethod)) { return; } ResolvedMethod method = (ResolvedMethod) resolved; if (!method.getContainingClass().isSubclassOf(WEB_VIEW, false)) { return; } if (method.getArgumentCount() != 2 || !method.getArgumentType(0).matchesName(TYPE_OBJECT) || !method.getArgumentType(1).matchesName(TYPE_STRING)) { return; } String message = "`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: " + "JavaScript can use reflection to manipulate application"; context.report(ISSUE, node, context.getLocation(node.astName()), message); } }