| /* |
| * Copyright (C) 2011 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 com.android.annotations.NonNull; |
| import com.android.tools.lint.detector.api.Category; |
| import com.android.tools.lint.detector.api.Context; |
| 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.Location; |
| import com.android.tools.lint.detector.api.Scope; |
| import com.android.tools.lint.detector.api.Severity; |
| import com.android.tools.lint.detector.api.Speed; |
| |
| import java.io.File; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import lombok.ast.AstVisitor; |
| import lombok.ast.ForwardingAstVisitor; |
| import lombok.ast.Node; |
| import lombok.ast.StringLiteral; |
| |
| /** |
| * Looks for hardcoded references to /sdcard/. |
| */ |
| public class SdCardDetector extends Detector implements Detector.JavaScanner { |
| /** Hardcoded /sdcard/ references */ |
| public static final Issue ISSUE = Issue.create( |
| "SdCardPath", //$NON-NLS-1$ |
| "Hardcoded reference to `/sdcard`", |
| |
| "Your code should not reference the `/sdcard` path directly; instead use " + |
| "`Environment.getExternalStorageDirectory().getPath()`.\n" + |
| "\n" + |
| "Similarly, do not reference the `/data/data/` path directly; it can vary " + |
| "in multi-user scenarios. Instead, use " + |
| "`Context.getFilesDir().getPath()`.", |
| |
| Category.CORRECTNESS, |
| 6, |
| Severity.WARNING, |
| new Implementation( |
| SdCardDetector.class, |
| Scope.JAVA_FILE_SCOPE)) |
| .addMoreInfo( |
| "http://developer.android.com/guide/topics/data/data-storage.html#filesExternal"); //$NON-NLS-1$ |
| |
| /** Constructs a new {@link SdCardDetector} check */ |
| public SdCardDetector() { |
| } |
| |
| @Override |
| public boolean appliesTo(@NonNull Context context, @NonNull File file) { |
| return true; |
| } |
| |
| @NonNull |
| @Override |
| public Speed getSpeed() { |
| return Speed.FAST; |
| } |
| |
| // ---- Implements JavaScanner ---- |
| |
| @Override |
| public List<Class<? extends Node>> getApplicableNodeTypes() { |
| return Collections.<Class<? extends Node>>singletonList(StringLiteral.class); |
| } |
| |
| @Override |
| public AstVisitor createJavaVisitor(@NonNull JavaContext context) { |
| return new StringChecker(context); |
| } |
| |
| private static class StringChecker extends ForwardingAstVisitor { |
| private final JavaContext mContext; |
| |
| public StringChecker(JavaContext context) { |
| mContext = context; |
| } |
| |
| @Override |
| public boolean visitStringLiteral(StringLiteral node) { |
| String s = node.astValue(); |
| if (s.isEmpty()) { |
| return false; |
| } |
| char c = s.charAt(0); |
| if (c != '/' && c != 'f') { |
| return false; |
| } |
| |
| if (s.startsWith("/sdcard") //$NON-NLS-1$ |
| || s.startsWith("/mnt/sdcard/") //$NON-NLS-1$ |
| || s.startsWith("/system/media/sdcard") //$NON-NLS-1$ |
| || s.startsWith("file://sdcard/") //$NON-NLS-1$ |
| || s.startsWith("file:///sdcard/")) { //$NON-NLS-1$ |
| String message = "Do not hardcode \"/sdcard/\"; " + |
| "use `Environment.getExternalStorageDirectory().getPath()` instead"; |
| Location location = mContext.getLocation(node); |
| mContext.report(ISSUE, node, location, message); |
| } else if (s.startsWith("/data/data/") //$NON-NLS-1$ |
| || s.startsWith("/data/user/")) { //$NON-NLS-1$ |
| String message = "Do not hardcode \"`/data/`\"; " + |
| "use `Context.getFilesDir().getPath()` instead"; |
| Location location = mContext.getLocation(node); |
| mContext.report(ISSUE, node, location, message); |
| } |
| |
| return false; |
| } |
| } |
| } |